import { Inject, InternalServerErrorException } from '@nestjs/common';
import { CommandHandler, type ICommandHandler } from '@nestjs/cqrs';
import { DomainException, ForbiddenException, NotFoundException, ValidationException, EventBusService, LoggerService } from '@modules/shared';
import type { MessageEntity } from '../../../domain/entities/message.entity';
import { MessageSentEvent } from '../../../domain/events/message-sent.event';
import {
  CONVERSATION_REPOSITORY,
  type IConversationRepository,
} from '../../../domain/repositories/conversation.repository';
import {
  MESSAGE_REPOSITORY,
  type IMessageRepository,
} from '../../../domain/repositories/message.repository';
import { SendMessageCommand } from './send-message.command';

@CommandHandler(SendMessageCommand)
export class SendMessageHandler implements ICommandHandler<SendMessageCommand> {
  constructor(
    @Inject(CONVERSATION_REPOSITORY)
    private readonly conversationRepo: IConversationRepository,
    @Inject(MESSAGE_REPOSITORY)
    private readonly messageRepo: IMessageRepository,
    private readonly eventBus: EventBusService,
    private readonly logger: LoggerService,
  ) {}

  async execute(command: SendMessageCommand): Promise<MessageEntity> {
    try {
      const { conversationId, senderId, content, type, metadata } = command;

      // Verify conversation exists and sender is a participant
      const conversation = await this.conversationRepo.findById(conversationId);
      if (!conversation) {
        throw new NotFoundException('Conversation', conversationId);
      }

      const isParticipant = conversation.participants.some((p) => p.userId === senderId);
      if (!isParticipant) {
        throw new ForbiddenException('Bạn không phải là thành viên của hội thoại này');
      }

      if (conversation.status !== 'ACTIVE') {
        throw new ValidationException('Hội thoại đã đóng');
      }

      // Create message
      const message = await this.messageRepo.create({
        conversationId,
        senderId,
        type,
        content,
        metadata,
      });

      // Update conversation last message
      await this.conversationRepo.updateLastMessage(conversationId, content, message.createdAt);

      // Increment unread count for other participants
      await this.conversationRepo.incrementUnreadCount(conversationId, senderId);

      // Publish domain event
      this.eventBus.publish(
        new MessageSentEvent(message.id, conversationId, senderId, type, content),
      );

      return message;
    } catch (error) {
      if (error instanceof DomainException) throw error;
      this.logger.error(
        `Failed to send message: ${error instanceof Error ? error.message : error}`,
        error instanceof Error ? error.stack : undefined,
        this.constructor.name,
      );
      throw new InternalServerErrorException('Không thể gửi tin nhắn');
    }
  }
}
