import { Injectable, Inject, Logger } from '@nestjs/common';
import { RecoverByPhoneCommand } from './recover-by-phone.command';
import {
  UserAccountRepository,
  USER_ACCOUNT_REPOSITORY,
} from '@/domain/repositories/user-account.repository.interface';
import { AccountSequence, PhoneNumber } from '@/domain/value-objects';
import { TokenService } from '@/application/services/token.service';
import { RedisService } from '@/infrastructure/redis/redis.service';
import { EventPublisherService } from '@/infrastructure/kafka/event-publisher.service';
import { ApplicationError } from '@/shared/exceptions/domain.exception';
import { RecoverAccountResult } from '../index';
import { generateRandomAvatarSvg } from '@/shared/utils/random-identity.util';

@Injectable()
export class RecoverByPhoneHandler {
  private readonly logger = new Logger(RecoverByPhoneHandler.name);

  constructor(
    @Inject(USER_ACCOUNT_REPOSITORY)
    private readonly userRepository: UserAccountRepository,
    private readonly tokenService: TokenService,
    private readonly redisService: RedisService,
    private readonly eventPublisher: EventPublisherService,
  ) {}

  async execute(command: RecoverByPhoneCommand): Promise<RecoverAccountResult> {
    const accountSequence = AccountSequence.create(command.accountSequence);
    const account =
      await this.userRepository.findByAccountSequence(accountSequence);
    if (!account) throw new ApplicationError('账户序列号不存在');
    if (!account.isActive) throw new ApplicationError('账户已冻结或注销');
    if (!account.phoneNumber)
      throw new ApplicationError('该账户未绑定手机号，请使用助记词恢复');

    const phoneNumber = PhoneNumber.create(command.phoneNumber);
    if (!account.phoneNumber.equals(phoneNumber))
      throw new ApplicationError('手机号与账户不匹配');

    const cachedCode = await this.redisService.get(
      `sms:recover:${phoneNumber.value}`,
    );
    if (cachedCode !== command.smsCode)
      throw new ApplicationError('验证码错误或已过期');

    // 如果头像为空，重新生成一个
    let avatarUrl = account.avatarUrl;
    if (!avatarUrl) {
      this.logger.log(
        `Account ${command.accountSequence} has no avatar, generating new one`,
      );
      avatarUrl = generateRandomAvatarSvg();
      account.updateProfile({ avatarUrl });
    }

    account.addDevice(command.newDeviceId, command.deviceName);
    account.recordLogin();
    await this.userRepository.save(account);
    await this.redisService.delete(`sms:recover:${phoneNumber.value}`);

    const tokens = await this.tokenService.generateTokenPair({
      userId: account.userId.toString(),
      accountSequence: account.accountSequence.value,
      deviceId: command.newDeviceId,
    });

    await this.eventPublisher.publishAll(account.domainEvents);
    account.clearDomainEvents();

    return {
      userId: account.userId.toString(),
      accountSequence: account.accountSequence.value,
      nickname: account.nickname,
      avatarUrl,
      referralCode: account.referralCode.value,
      accessToken: tokens.accessToken,
      refreshToken: tokens.refreshToken,
    };
  }
}
