import {
  Injectable,
  Logger,
  BadRequestException,
  Inject,
} from '@nestjs/common';
import { SmsVerificationType } from '../../domain/entities/sms-verification.entity';
import { SmsDeliveryStatus } from '../../domain/entities/sms-log.entity';
import {
  ISmsVerificationRepository,
  SMS_VERIFICATION_REPOSITORY,
} from '../../domain/repositories/sms-verification.repository.interface';
import {
  ISmsLogRepository,
  SMS_LOG_REPOSITORY,
} from '../../domain/repositories/sms-log.repository.interface';
import { IUserRepository, USER_REPOSITORY } from '../../domain/repositories/user.repository.interface';
import { ISmsProvider, SMS_PROVIDER } from '../../infrastructure/sms/sms-provider.interface';
import { SmsCodeService } from '../../infrastructure/redis/sms-code.service';
import { SmsCode } from '../../domain/value-objects/sms-code.vo';
import { Phone } from '../../domain/value-objects/phone.vo';

@Injectable()
export class SmsService {
  private readonly logger = new Logger('SmsService');
  private readonly codeExpireSeconds: number;
  private readonly dailyLimit: number;
  private readonly maxAttempts: number;

  constructor(
    @Inject(SMS_VERIFICATION_REPOSITORY)
    private readonly smsVerifRepo: ISmsVerificationRepository,
    @Inject(SMS_LOG_REPOSITORY)
    private readonly smsLogRepo: ISmsLogRepository,
    @Inject(USER_REPOSITORY)
    private readonly userRepo: IUserRepository,
    @Inject(SMS_PROVIDER)
    private readonly smsProvider: ISmsProvider,
    private readonly smsCodeCache: SmsCodeService,
  ) {
    this.codeExpireSeconds = parseInt(process.env.SMS_CODE_EXPIRE_SECONDS || '300', 10);
    this.dailyLimit = parseInt(process.env.SMS_DAILY_LIMIT || '10', 10);
    this.maxAttempts = parseInt(process.env.SMS_MAX_VERIFY_ATTEMPTS || '5', 10);
  }

  /**
   * 发送短信验证码
   */
  async sendCode(
    rawPhone: string,
    type: SmsVerificationType,
  ): Promise<{ expiresIn: number }> {
    const phone = Phone.create(rawPhone);

    // 1. 检查日发送限额
    const dailyCount = await this.smsVerifRepo.getDailySendCount(phone.value);
    if (dailyCount >= this.dailyLimit) {
      throw new BadRequestException('今日发送次数已达上限，请明天再试');
    }

    // 2. 按类型验证业务规则
    await this.validateSendRequest(phone, type);

    // 3. 生成验证码
    const code = SmsCode.generate();
    const expiresAt = new Date(Date.now() + this.codeExpireSeconds * 1000);

    // 4. 持久化到 DB
    await this.smsVerifRepo.create({
      phone: phone.value,
      code: code.value,
      type,
      expiresAt,
    });

    // 5. 缓存到 Redis (快速查找)
    await this.smsCodeCache.setCode(phone.value, code.value, type, this.codeExpireSeconds);

    // 6. 通过 Provider 发送
    const result = await this.smsProvider.send(phone.value, code.value, type);

    // 7. 记录发送日志
    const user = await this.userRepo.findByPhone(phone.value);
    await this.smsLogRepo.create({
      phone: phone.value,
      type,
      status: result.success ? SmsDeliveryStatus.SENT : SmsDeliveryStatus.FAILED,
      provider: result.providerId ? 'aliyun' : 'console',
      providerId: result.providerId,
      errorMsg: result.errorMsg,
      userId: user?.id,
    });

    this.logger.log(`SMS code sent: phone=${phone.masked} type=${type}`);
    return { expiresIn: this.codeExpireSeconds };
  }

  /**
   * 验证短信验证码
   */
  async verifyCode(
    rawPhone: string,
    code: string,
    type: SmsVerificationType,
  ): Promise<boolean> {
    const phone = Phone.create(rawPhone);

    // 先尝试 Redis 快速路径
    const redisMatch = await this.smsCodeCache.verifyAndDelete(phone.value, code, type);
    if (redisMatch) {
      // Redis 匹配成功，同步标记 DB 记录
      const dbRecord = await this.smsVerifRepo.findLatestValid(phone.value, type);
      if (dbRecord) {
        dbRecord.markVerified();
        await this.smsVerifRepo.save(dbRecord);
      }
      return true;
    }

    // Redis miss → 回退到 DB 验证
    const verification = await this.smsVerifRepo.findLatestValid(phone.value, type);
    if (!verification) {
      throw new BadRequestException('验证码已过期或不存在');
    }

    if (!verification.canAttempt(this.maxAttempts)) {
      throw new BadRequestException('验证码尝试次数过多，请重新获取');
    }

    if (!SmsCode.from(verification.code).matches(code)) {
      verification.incrementAttempts();
      await this.smsVerifRepo.save(verification);
      throw new BadRequestException('验证码错误');
    }

    verification.markVerified();
    await this.smsVerifRepo.save(verification);
    return true;
  }

  /**
   * 按类型验证发送条件
   */
  private async validateSendRequest(
    phone: Phone,
    type: SmsVerificationType,
  ): Promise<void> {
    const existingUser = await this.userRepo.findByPhone(phone.value);

    switch (type) {
      case SmsVerificationType.REGISTER:
        if (existingUser) {
          throw new BadRequestException('该手机号已注册');
        }
        break;

      case SmsVerificationType.LOGIN:
      case SmsVerificationType.RESET_PASSWORD:
        if (!existingUser) {
          throw new BadRequestException('该手机号未注册');
        }
        break;

      case SmsVerificationType.CHANGE_PHONE:
        if (existingUser) {
          throw new BadRequestException('该手机号已被其他账户使用');
        }
        break;
    }
  }
}
