import { Injectable, NotFoundException, ForbiddenException, Optional, Logger } from '@nestjs/common';
import { PrismaService } from '@chatapp/database';
import { SearchService } from '../search/search.service';

@Injectable()
export class MessagesService {
  private readonly logger = new Logger(MessagesService.name);

  constructor(
    private readonly prisma: PrismaService,
    @Optional() private readonly searchService?: SearchService,
  ) {}

  async send(channelId: string, userId: string, wsId: string, dto: any) {
    // Verify the user is a member of the channel
    const membership = await this.prisma.channelMember.findFirst({
      where: { channelId, userId, leftAt: null },
    });
    if (!membership) {
      throw new ForbiddenException('You are not a member of this channel');
    }

    // Verify the channel belongs to the workspace
    const channel = await this.prisma.channel.findFirst({
      where: { id: channelId, workspaceId: wsId },
    });
    if (!channel) {
      throw new NotFoundException('Channel not found');
    }

    const message = await this.prisma.message.create({
      data: {
        workspaceId: wsId,
        channelId,
        userId,
        text: dto.text,
        blocks: dto.blocks || undefined,
        threadRootId: dto.thread_root_id || null,
        mentionedUsers: dto.mentioned_users || [],
        mentionedChannels: dto.mentioned_channels || [],
        isScheduled: !!dto.scheduled_for,
        scheduledFor: dto.scheduled_for ? new Date(dto.scheduled_for) : null,
      },
      include: {
        user: { select: { id: true, displayName: true } },
        channel: { select: { id: true, name: true } },
      },
    });

    // Index in Meilisearch (fire-and-forget)
    if (this.searchService) {
      this.searchService.indexMessage({
        id: message.id,
        content: message.text,
        channelId: message.channelId,
        channelName: message.channel?.name || '',
        senderId: message.userId,
        senderName: message.user?.displayName || '',
        workspaceId: message.workspaceId,
        createdAt: message.createdAt,
      }).catch((err: any) => this.logger.warn(`Failed to index message: ${err}`));
    }

    // Update channel last_message_at
    await this.prisma.channel.update({
      where: { id: channelId },
      data: { lastMessageAt: new Date() },
    });

    // If thread reply, update parent reply_count
    if (dto.thread_root_id) {
      await this.prisma.message.update({
        where: { id: dto.thread_root_id },
        data: {
          replyCount: { increment: 1 },
          latestReplyTs: new Date(),
        },
      });
    }

    return message;
  }

  async edit(messageId: string, userId: string, wsId: string, dto: any) {
    const msg = await this.prisma.message.findFirst({
      where: { id: messageId, workspaceId: wsId, isDeleted: false },
    });
    if (!msg) throw new NotFoundException('Message not found');
    if (msg.userId !== userId) throw new ForbiddenException('Cannot edit another user message');

    return this.prisma.message.update({
      where: { id: messageId },
      data: {
        text: dto.text,
        blocks: dto.blocks || undefined,
        isEdited: true,
        editedAt: new Date(),
        editedBy: userId,
      },
    });
  }

  async delete(messageId: string, userId: string, wsId: string) {
    const msg = await this.prisma.message.findFirst({
      where: { id: messageId, workspaceId: wsId, isDeleted: false },
    });
    if (!msg) throw new NotFoundException('Message not found');
    if (msg.userId !== userId) throw new ForbiddenException('Cannot delete another user message');

    await this.prisma.message.update({
      where: { id: messageId },
      data: { isDeleted: true, deletedAt: new Date(), deletedBy: userId },
    });
    return { deleted: true };
  }

  async getThread(rootId: string, wsId: string) {
    const messages = await this.prisma.message.findMany({
      where: { threadRootId: rootId, workspaceId: wsId, isDeleted: false },
      orderBy: { ts: 'asc' },
      include: {
        user: { select: { id: true, displayName: true, avatarUrl: true } },
        reactions: true,
      },
    });
    return { messages };
  }

  async addReaction(messageId: string, userId: string, emoji: string, wsId: string) {
    // Verify message belongs to workspace
    const msg = await this.prisma.message.findFirst({
      where: { id: messageId, workspaceId: wsId, isDeleted: false },
    });
    if (!msg) throw new NotFoundException('Message not found');

    try {
      return await this.prisma.messageReaction.create({
        data: { messageId, userId, emoji },
      });
    } catch (e: any) {
      // Already exists, ignore
      return { message_id: messageId, user_id: userId, emoji };
    }
  }

  async removeReaction(messageId: string, userId: string, emoji: string, wsId: string) {
    // Verify message belongs to workspace
    const msg = await this.prisma.message.findFirst({
      where: { id: messageId, workspaceId: wsId, isDeleted: false },
    });
    if (!msg) throw new NotFoundException('Message not found');

    await this.prisma.messageReaction.deleteMany({
      where: { messageId, userId, emoji },
    });
    return { removed: true };
  }

  async save(messageId: string, userId: string) {
    try {
      await this.prisma.savedMessage.create({ data: { userId, messageId } });
    } catch (e: any) {
      // Already saved
    }
    return { saved: true };
  }

  async unsave(messageId: string, userId: string) {
    await this.prisma.savedMessage.deleteMany({ where: { userId, messageId } });
    return { saved: false };
  }
}