import { InternalServerErrorException } from '@nestjs/common';
import { CommandHandler, EventBus, type ICommandHandler } from '@nestjs/cqrs';
import { DomainException, LoggerService, RedisService, ValidationException } from '@modules/shared';
import { EmailChangeRequestedEvent } from '../../../domain/events/email-change-requested.event';
import { PhoneChangeRequestedEvent } from '../../../domain/events/phone-change-requested.event';
import { EMAIL_CHANGE_OTP_PREFIX, PHONE_CHANGE_OTP_PREFIX } from '../update-profile/update-profile.handler';
import { ResendOtpCommand } from './resend-otp.command';

export interface ResendOtpResultDto {
  sent: true;
}

interface EmailChangePayload {
  newEmail: string;
  code: string;
}

interface PhoneChangePayload {
  newPhone: string;
  code: string;
}

@CommandHandler(ResendOtpCommand)
export class ResendOtpHandler implements ICommandHandler<ResendOtpCommand> {
  constructor(
    private readonly redis: RedisService,
    private readonly eventBus: EventBus,
    private readonly logger: LoggerService,
  ) {}

  async execute(command: ResendOtpCommand): Promise<ResendOtpResultDto> {
    try {
      const redisKey = this.buildRedisKey(command);
      const raw = await this.redis.get(redisKey);

      if (!raw) {
        throw new ValidationException('Không có yêu cầu OTP nào đang chờ xử lý');
      }

      if (command.context === 'EMAIL_CHANGE') {
        const { newEmail, code } = JSON.parse(raw) as EmailChangePayload;
        this.eventBus.publish(
          new EmailChangeRequestedEvent(command.userId, newEmail, code),
        );
      } else {
        const { newPhone, code } = JSON.parse(raw) as PhoneChangePayload;
        this.eventBus.publish(
          new PhoneChangeRequestedEvent(command.userId, newPhone, code),
        );
      }

      this.logger.log(
        `Resent ${command.context} OTP for user ${command.userId}`,
        this.constructor.name,
      );

      return { sent: true };
    } catch (error) {
      if (error instanceof DomainException) throw error;
      this.logger.error(
        `Failed to resend OTP: ${error instanceof Error ? error.message : error}`,
        error instanceof Error ? error.stack : undefined,
        this.constructor.name,
      );
      throw new InternalServerErrorException('Không thể gửi lại mã OTP');
    }
  }

  private buildRedisKey(command: ResendOtpCommand): string {
    const prefix =
      command.context === 'EMAIL_CHANGE' ? EMAIL_CHANGE_OTP_PREFIX : PHONE_CHANGE_OTP_PREFIX;
    return `${prefix}:${command.userId}`;
  }
}
