import { Inject, InternalServerErrorException } from '@nestjs/common';
import { CommandHandler, EventBus, type ICommandHandler } from '@nestjs/cqrs';
import { createId } from '@paralleldrive/cuid2';
import { DomainException, NotFoundException, ValidationException, PrismaService, LoggerService } from '@modules/shared';
import { LeadEntity } from '../../../domain/entities/lead.entity';
import { LEAD_REPOSITORY, type ILeadRepository } from '../../../domain/repositories/lead.repository';
import { LeadScore } from '../../../domain/value-objects/lead-score.vo';
import { CreateLeadCommand } from './create-lead.command';

export interface CreateLeadResult {
  id: string;
  status: string;
  createdAt: string;
}

@CommandHandler(CreateLeadCommand)
export class CreateLeadHandler implements ICommandHandler<CreateLeadCommand> {
  constructor(
    @Inject(LEAD_REPOSITORY) private readonly leadRepo: ILeadRepository,
    private readonly eventBus: EventBus,
    private readonly prisma: PrismaService,
    private readonly logger: LoggerService,
  ) {}

  async execute(command: CreateLeadCommand): Promise<CreateLeadResult> {
    try {
      // Look up agent by userId
      const agent = await this.prisma.agent.findUnique({
        where: { userId: command.agentUserId },
      });
      if (!agent) {
        throw new NotFoundException('Agent', command.agentUserId);
      }

      // Validate score value object
      let score: LeadScore | null = null;
      if (command.score !== null && command.score !== undefined) {
        const scoreResult = LeadScore.create(command.score);
        if (scoreResult.isErr) {
          throw new ValidationException(scoreResult.unwrapErr());
        }
        score = scoreResult.unwrap();
      }

      const id = createId();
      const lead = LeadEntity.createNew(
        id,
        agent.id,
        command.name,
        command.phone,
        command.email,
        command.source,
        score,
        command.notes ?? null,
      );

      await this.leadRepo.save(lead);

      // Publish domain events
      const events = lead.clearDomainEvents();
      for (const event of events) {
        this.eventBus.publish(event);
      }

      this.logger.log(`Lead ${id} created by agent ${agent.id}`, 'CreateLeadHandler');

      return {
        id,
        status: lead.status,
        createdAt: lead.createdAt.toISOString(),
      };
    } catch (error) {
      if (error instanceof DomainException) throw error;
      this.logger.error(
        `Failed to create lead: ${error instanceof Error ? error.message : String(error)}`,
        error instanceof Error ? error.stack : undefined,
        'CreateLeadHandler',
      );
      throw new InternalServerErrorException('Lỗi khi tạo lead');
    }
  }
}
