import { Injectable, Logger } from '@nestjs/common';
import { generateSecret, generateURI, verifySync } from 'otplib';
import { RedisService } from '@core/cache/redis/redis.service';

/**
 * MFA（TOTP）骨架。
 *
 * 规则对齐（09-iam-security.md §2.3）：敏感操作（密码修改、权限变更、数据导出）触发。
 * 本服务提供生成密钥 / 验证 OTP / 发起挑战的基础能力；
 * 挂接到具体敏感操作的工作由后续阶段按业务需要接入。
 */
@Injectable()
export class MfaService {
  private readonly logger = new Logger(MfaService.name);
  private readonly challengeTtlSec = 10 * 60;

  constructor(private readonly redis: RedisService) {}

  /**
   * 为用户生成 TOTP secret。用户需通过 app 扫码绑定。
   */
  generateSecret(accountName: string): { secret: string; otpauthUrl: string } {
    const secret = generateSecret();
    const otpauthUrl = generateURI({
      issuer: 'FF AI Workspace',
      label: accountName,
      secret,
    });
    return { secret, otpauthUrl };
  }

  /**
   * 验证用户输入的 OTP 是否匹配 secret。
   */
  verify(secret: string, token: string): boolean {
    try {
      const result: any = verifySync({ token, secret });
      // otplib v13 返回 { valid: boolean } 或直接 boolean，兼容处理
      if (typeof result === 'boolean') return result;
      return result?.valid === true;
    } catch {
      return false;
    }
  }

  /**
   * 对敏感操作发起 MFA 挑战：生成挑战 id，等待用户二次验证。
   */
  async createChallenge(userId: string, operation: string): Promise<string> {
    const challengeId = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
    await this.redis.setJson(
      `mfa_challenge:${challengeId}`,
      this.challengeTtlSec,
      { userId, operation, issuedAt: Date.now() },
    );
    return challengeId;
  }

  async consumeChallenge(
    challengeId: string,
    userId: string,
    operation: string,
  ): Promise<boolean> {
    const payload = await this.redis.getJson<{
      userId: string;
      operation: string;
    }>(`mfa_challenge:${challengeId}`);
    if (!payload) return false;
    if (payload.userId !== userId || payload.operation !== operation) return false;
    await this.redis.del(`mfa_challenge:${challengeId}`);
    return true;
  }
}
