import { Inject, InternalServerErrorException } from '@nestjs/common';
import { CommandHandler, type ICommandHandler } from '@nestjs/cqrs';
import { DomainException, ValidationException, EventBusService, LoggerService } from '@modules/shared';
import type { ConversationEntity } from '../../../domain/entities/conversation.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 { CreateConversationCommand } from './create-conversation.command';

@CommandHandler(CreateConversationCommand)
export class CreateConversationHandler implements ICommandHandler<CreateConversationCommand> {
  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: CreateConversationCommand): Promise<ConversationEntity> {
    try {
      const { initiatorUserId, participantUserId, listingId, subject, initialMessage } = command;

      if (initiatorUserId === participantUserId) {
        throw new ValidationException('Không thể tạo hội thoại với chính mình');
      }

      // Check for existing conversation between these users for the same listing
      const userIds = [initiatorUserId, participantUserId];
      const existing = await this.conversationRepo.findExistingBetweenUsers(userIds, listingId);
      if (existing) {
        // If there's an initial message, send it in the existing conversation
        if (initialMessage) {
          const message = await this.messageRepo.create({
            conversationId: existing.id,
            senderId: initiatorUserId,
            type: 'TEXT',
            content: initialMessage,
          });
          await this.conversationRepo.updateLastMessage(existing.id, initialMessage, message.createdAt);
          await this.conversationRepo.incrementUnreadCount(existing.id, initiatorUserId);
          this.eventBus.publish(
            new MessageSentEvent(message.id, existing.id, initiatorUserId, 'TEXT', initialMessage),
          );
        }
        return existing;
      }

      // Create new conversation
      const conversation = await this.conversationRepo.create({
        listingId,
        subject,
        participantUserIds: userIds,
      });

      // Send initial message if provided
      if (initialMessage) {
        const message = await this.messageRepo.create({
          conversationId: conversation.id,
          senderId: initiatorUserId,
          type: 'TEXT',
          content: initialMessage,
        });
        await this.conversationRepo.updateLastMessage(conversation.id, initialMessage, message.createdAt);
        await this.conversationRepo.incrementUnreadCount(conversation.id, initiatorUserId);
        this.eventBus.publish(
          new MessageSentEvent(message.id, conversation.id, initiatorUserId, 'TEXT', initialMessage),
        );
      }

      this.logger.log(
        `Conversation created: ${conversation.id} between [${userIds.join(', ')}]`,
        'CreateConversationHandler',
      );

      return conversation;
    } catch (error) {
      if (error instanceof DomainException) throw error;
      this.logger.error(
        `Failed to create conversation: ${error instanceof Error ? error.message : error}`,
        error instanceof Error ? error.stack : undefined,
        this.constructor.name,
      );
      throw new InternalServerErrorException('Không thể tạo hội thoại');
    }
  }
}
