import {
  Injectable,
  Logger,
  BadRequestException,
  Inject,
} from '@nestjs/common';
import { EmailVerificationType } from '../../domain/entities/email-verification.entity';
import { EmailDeliveryStatus } from '../../domain/entities/email-log.entity';
import {
  IEmailVerificationRepository,
  EMAIL_VERIFICATION_REPOSITORY,
} from '../../domain/repositories/email-verification.repository.interface';
import {
  IEmailLogRepository,
  EMAIL_LOG_REPOSITORY,
} from '../../domain/repositories/email-log.repository.interface';
import { IUserRepository, USER_REPOSITORY } from '../../domain/repositories/user.repository.interface';
import { IEmailProvider, EMAIL_PROVIDER } from '../../infrastructure/email/email-provider.interface';
import { EmailCodeService } from '../../infrastructure/redis/email-code.service';
import { SmsCode } from '../../domain/value-objects/sms-code.vo';
import { Email } from '../../domain/value-objects/email.vo';

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

  constructor(
    @Inject(EMAIL_VERIFICATION_REPOSITORY)
    private readonly emailVerifRepo: IEmailVerificationRepository,
    @Inject(EMAIL_LOG_REPOSITORY)
    private readonly emailLogRepo: IEmailLogRepository,
    @Inject(USER_REPOSITORY)
    private readonly userRepo: IUserRepository,
    @Inject(EMAIL_PROVIDER)
    private readonly emailProvider: IEmailProvider,
    private readonly emailCodeCache: EmailCodeService,
  ) {
    this.codeExpireSeconds = parseInt(process.env.EMAIL_CODE_EXPIRE_SECONDS || '300', 10);
    this.dailyLimit = parseInt(process.env.EMAIL_DAILY_LIMIT || '10', 10);
    this.maxAttempts = parseInt(process.env.EMAIL_MAX_VERIFY_ATTEMPTS || '5', 10);
  }

  /**
   * 发送邮件验证码
   */
  async sendCode(
    rawEmail: string,
    type: EmailVerificationType,
  ): Promise<{ expiresIn: number }> {
    const email = Email.create(rawEmail);

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

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

    // 3. 生成验证码（复用 SmsCode 值对象：安全随机 6 位数字）
    const code = SmsCode.generate();
    const expiresAt = new Date(Date.now() + this.codeExpireSeconds * 1000);

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

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

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

    // 7. 记录发送日志
    const user = await this.userRepo.findByEmail(email.value);
    await this.emailLogRepo.create({
      email: email.value,
      type,
      status: result.success ? EmailDeliveryStatus.SENT : EmailDeliveryStatus.FAILED,
      provider: result.providerId ? 'gmail' : 'console',
      providerId: result.providerId,
      errorMsg: result.errorMsg,
      userId: user?.id,
    });

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

  /**
   * 验证邮件验证码
   */
  async verifyCode(
    rawEmail: string,
    code: string,
    type: EmailVerificationType,
  ): Promise<boolean> {
    const email = Email.create(rawEmail);

    // 先尝试 Redis 快速路径
    const redisMatch = await this.emailCodeCache.verifyAndDelete(email.value, code, type);
    if (redisMatch) {
      const dbRecord = await this.emailVerifRepo.findLatestValid(email.value, type);
      if (dbRecord) {
        dbRecord.markVerified();
        await this.emailVerifRepo.save(dbRecord);
      }
      return true;
    }

    // Redis miss → 回退到 DB 验证
    const verification = await this.emailVerifRepo.findLatestValid(email.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.emailVerifRepo.save(verification);
      throw new BadRequestException('验证码错误');
    }

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

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

    switch (type) {
      case EmailVerificationType.REGISTER:
        if (existingUser) {
          throw new BadRequestException('该邮箱已注册');
        }
        break;

      case EmailVerificationType.LOGIN:
      case EmailVerificationType.RESET_PASSWORD:
        if (!existingUser) {
          throw new BadRequestException('该邮箱未注册');
        }
        break;

      case EmailVerificationType.CHANGE_EMAIL:
        if (existingUser) {
          throw new BadRequestException('该邮箱已被其他账户使用');
        }
        break;
    }
  }
}
