import { Inject, InternalServerErrorException } from '@nestjs/common';
import { CommandHandler, type ICommandHandler } from '@nestjs/cqrs';
import {
  DomainException,
  LoggerService,
  NotFoundException,
  RedisService,
  ValidationException,
} from '@modules/shared';
import { type IRefreshTokenRepository, REFRESH_TOKEN_REPOSITORY } from '../../../domain/repositories/refresh-token.repository';
import { type IUserRepository, USER_REPOSITORY } from '../../../domain/repositories/user.repository';
import { HashedPassword } from '../../../domain/value-objects/hashed-password.vo';
import { PASSWORD_RESET_OTP_PREFIX } from '../forgot-password/forgot-password.handler';
import { ResetPasswordCommand } from './reset-password.command';

export interface ResetPasswordResultDto {
  id: string;
  updatedAt: Date;
}

@CommandHandler(ResetPasswordCommand)
export class ResetPasswordHandler implements ICommandHandler<ResetPasswordCommand> {
  constructor(
    @Inject(USER_REPOSITORY) private readonly userRepo: IUserRepository,
    @Inject(REFRESH_TOKEN_REPOSITORY) private readonly refreshTokenRepo: IRefreshTokenRepository,
    private readonly redis: RedisService,
    private readonly logger: LoggerService,
  ) {}

  async execute(command: ResetPasswordCommand): Promise<ResetPasswordResultDto> {
    try {
      const hashedResult = await HashedPassword.fromPlain(command.newPassword);
      if (hashedResult.isErr) {
        throw new ValidationException(hashedResult.unwrapErr());
      }
      const passwordVo = hashedResult.unwrap();

      const identifier = command.emailOrPhone.trim();
      const user = identifier.includes('@')
        ? await this.userRepo.findByEmail(identifier)
        : await this.userRepo.findByPhone(identifier);

      if (!user) {
        throw new NotFoundException('Người dùng', identifier);
      }

      const redisKey = `${PASSWORD_RESET_OTP_PREFIX}:${user.id}`;
      const raw = await this.redis.get(redisKey);

      if (!raw) {
        throw new ValidationException(
          'Mã xác thực đã hết hạn hoặc không tồn tại. Vui lòng yêu cầu lại.',
        );
      }

      const { code } = JSON.parse(raw) as { code: string };
      if (code !== command.code) {
        throw new ValidationException('Mã xác thực không đúng');
      }

      user.changePassword(passwordVo);
      await this.userRepo.update(user);

      await this.redis.del(redisKey);
      await this.refreshTokenRepo.revokeAllForUser(user.id);

      this.logger.log(
        `Password reset completed for user ${user.id}`,
        this.constructor.name,
      );

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