import { randomInt } from 'crypto';
import { Inject, InternalServerErrorException } from '@nestjs/common';
import { CommandHandler, EventBus, type ICommandHandler } from '@nestjs/cqrs';
import { DomainException, LoggerService, RedisService } from '@modules/shared';
import { PasswordResetRequestedEvent } from '../../../domain/events/password-reset-requested.event';
import { type IUserRepository, USER_REPOSITORY } from '../../../domain/repositories/user.repository';
import { ForgotPasswordCommand } from './forgot-password.command';

export const PASSWORD_RESET_OTP_PREFIX = 'auth:password_reset_otp';

export const PASSWORD_RESET_OTP_TTL = 900;

export const PASSWORD_RESET_OTP_EXPIRY_MINUTES = PASSWORD_RESET_OTP_TTL / 60;

export interface ForgotPasswordResultDto {
  sent: true;
}

@CommandHandler(ForgotPasswordCommand)
export class ForgotPasswordHandler implements ICommandHandler<ForgotPasswordCommand> {
  constructor(
    @Inject(USER_REPOSITORY) private readonly userRepo: IUserRepository,
    private readonly redis: RedisService,
    private readonly eventBus: EventBus,
    private readonly logger: LoggerService,
  ) {}

  async execute(command: ForgotPasswordCommand): Promise<ForgotPasswordResultDto> {
    try {
      const identifier = command.emailOrPhone.trim();
      const user = identifier.includes('@')
        ? await this.userRepo.findByEmail(identifier)
        : await this.userRepo.findByPhone(identifier);

      if (user?.email) {
        const code = String(randomInt(100_000, 1_000_000));
        const redisKey = `${PASSWORD_RESET_OTP_PREFIX}:${user.id}`;

        await this.redis.set(
          redisKey,
          JSON.stringify({ code }),
          PASSWORD_RESET_OTP_TTL,
        );

        this.eventBus.publish(
          new PasswordResetRequestedEvent(user.id, user.email.value, code),
        );

        this.logger.log(
          `Password reset OTP issued for user ${user.id}`,
          this.constructor.name,
        );
      } else {
        this.logger.log(
          `Forgot password requested for unknown identifier (masked)`,
          this.constructor.name,
        );
      }

      return { sent: true };
    } catch (error) {
      if (error instanceof DomainException) throw error;
      this.logger.error(
        `Failed to process forgot password: ${error instanceof Error ? error.message : error}`,
        error instanceof Error ? error.stack : undefined,
        this.constructor.name,
      );
      throw new InternalServerErrorException('Không thể gửi mã đặt lại mật khẩu');
    }
  }
}
