import { Inject, InternalServerErrorException } from '@nestjs/common';
import { CommandHandler, type ICommandHandler } from '@nestjs/cqrs';
import { DomainException, LoggerService, UnauthorizedException } from '@modules/shared';
import {
  MFA_CHALLENGE_REPOSITORY,
  type IMfaChallengeRepository,
} from '../../../domain/repositories/mfa-challenge.repository';
import { USER_REPOSITORY, type IUserRepository } from '../../../domain/repositories/user.repository';
import { MfaService } from '../../../infrastructure/services/mfa.service';
import { TokenService, type TokenPair } from '../../../infrastructure/services/token.service';
import { VerifyMfaChallengeCommand } from './verify-mfa-challenge.command';

@CommandHandler(VerifyMfaChallengeCommand)
export class VerifyMfaChallengeHandler implements ICommandHandler<VerifyMfaChallengeCommand> {
  constructor(
    @Inject(USER_REPOSITORY) private readonly userRepo: IUserRepository,
    @Inject(MFA_CHALLENGE_REPOSITORY) private readonly challengeRepo: IMfaChallengeRepository,
    private readonly mfaService: MfaService,
    private readonly tokenService: TokenService,
    private readonly logger: LoggerService,
  ) {}

  async execute(command: VerifyMfaChallengeCommand): Promise<TokenPair> {
    try {
      // Find and validate the challenge
      const challenge = await this.challengeRepo.findById(command.challengeId);
      if (!challenge) {
        throw new UnauthorizedException('Phiên xác thực MFA không tồn tại hoặc đã hết hạn');
      }

      if (challenge.isVerified) {
        throw new UnauthorizedException('Phiên xác thực MFA đã được sử dụng');
      }

      if (challenge.expiresAt < new Date()) {
        throw new UnauthorizedException('Phiên xác thực MFA đã hết hạn');
      }

      if (challenge.attemptCount >= challenge.maxAttempts) {
        throw new UnauthorizedException('Đã vượt quá số lần thử. Vui lòng đăng nhập lại');
      }

      // Look up the user
      const user = await this.userRepo.findById(challenge.userId);
      if (!user || !user.totpSecret || !user.totpEnabled) {
        throw new UnauthorizedException('MFA chưa được thiết lập cho tài khoản này');
      }

      // Verify the TOTP code
      const isValid = await this.mfaService.verifyTotp(command.totpCode, user.totpSecret);
      if (!isValid) {
        await this.challengeRepo.incrementAttempts(command.challengeId);
        const remaining = challenge.maxAttempts - challenge.attemptCount - 1;
        throw new UnauthorizedException(
          `Mã TOTP không hợp lệ. Còn ${remaining} lần thử`,
        );
      }

      // Mark the challenge as verified
      await this.challengeRepo.markVerified(command.challengeId);

      // Generate token pair (login complete)
      return this.tokenService.generateTokenPair({
        sub: user.id,
        phone: user.phone.value,
        role: user.role,
      });
    } catch (error) {
      if (error instanceof DomainException) throw error;
      this.logger.error(
        `Failed to verify MFA challenge: ${error instanceof Error ? error.message : error}`,
        error instanceof Error ? error.stack : undefined,
        this.constructor.name,
      );
      throw new InternalServerErrorException('Không thể xác thực MFA');
    }
  }
}
