// ============================================================
// AppleProvider — Apple Sign In Identity Token 验证
//
// ── Flutter sign_in_with_apple 流程 ─────────────────────────
//   1. 客户端调用 SignInWithApple.getAppleIDCredential(...)
//   2. 用户在 Apple 身份验证界面完成授权
//   3. 获取 AppleIDCredential.identityToken（JWT 字符串）
//      + AppleIDCredential.userIdentifier（user ID，18-20位字母数字）
//   4. 将 identityToken 发送到 POST /api/v1/auth/apple
//
// ── Identity Token 验证流程 ─────────────────────────────────
//   Apple Identity Token 是 ES256（ECDSA P-256 SHA-256）签名的 JWT
//
//   Step 1: 解析 JWT header，获取 kid（密钥 ID）
//   Step 2: 获取 Apple 公钥集合（JWKS）
//           GET https://appleid.apple.com/auth/keys
//   Step 3: 找到 kid 匹配的公钥（JWK 格式转 PEM）
//   Step 4: 验证 JWT 签名（crypto.createVerify('SHA256')）
//   Step 5: 验证 claims：
//           - iss = 'https://appleid.apple.com'
//           - aud = APPLE_CLIENT_ID (Bundle ID or Service ID)
//           - exp > now（未过期）
//
// ── 申请 Apple Sign In 步骤 ────────────────────────────────
// 1. Apple Developer 账号（个人 $99/年）
//    https://developer.apple.com
//
// 2. 开启 Sign In with Apple 能力
//    Certificates, Identifiers & Profiles
//    → Identifiers → 选择 App ID (cn.gogenex.consumer)
//    → Capabilities → 勾选「Sign In with Apple」
//
// 3. iOS 项目配置（Xcode）
//    Target → Signing & Capabilities → + Capability → Sign In with Apple
//
// 4. 环境变量
//    APPLE_CLIENT_ID — Bundle ID (cn.gogenex.consumer) 或 Service ID
//
// ── 注意事项 ───────────────────────────────────────────────
//   - Apple 首次登录会提供 email，之后登录不再提供（需客户端缓存或提示用户输入）
//   - userIdentifier（user ID）是稳定的，可跨设备识别同一用户
//   - JWKS 可缓存（Cache-Control），无需每次请求
// ============================================================

import {
  Injectable,
  Logger,
  UnauthorizedException,
} from '@nestjs/common';
import * as https from 'https';
import * as crypto from 'crypto';

/** Apple JWKS 公钥 */
interface AppleJWK {
  kty: string;
  kid: string;
  use: string;
  alg: string;
  n: string;   // RSA modulus（Base64URL）
  e: string;   // RSA exponent（Base64URL）
}

interface AppleJWKS {
  keys: AppleJWK[];
}

/** Apple Identity Token 验证后的用户信息 */
export interface AppleUserInfo {
  sub: string;       // user identifier（稳定唯一 ID）
  email?: string;    // 仅首次登录时返回
  emailVerified?: boolean;
  isPrivateEmail?: boolean;  // Apple 隐藏真实邮箱时为 true
}

@Injectable()
export class AppleProvider {
  private readonly logger = new Logger('AppleProvider');
  private readonly clientId = process.env.APPLE_CLIENT_ID || 'cn.gogenex.consumer';

  // 简单内存缓存 JWKS（Apple 公钥极少变化）
  private cachedJwks: AppleJWK[] | null = null;
  private jwksCachedAt = 0;
  private readonly JWKS_CACHE_TTL = 24 * 60 * 60 * 1000; // 24小时

  /**
   * 验证 Apple Identity Token 并返回用户信息
   *
   * @param identityToken Flutter sign_in_with_apple 返回的 JWT
   */
  async verifyIdentityToken(identityToken: string): Promise<AppleUserInfo> {
    // 1. 解析 JWT（不验证签名，只读 header + payload）
    const parts = identityToken.split('.');
    if (parts.length !== 3) {
      throw new UnauthorizedException('Apple Identity Token 格式无效');
    }

    const header = JSON.parse(this.base64urlDecode(parts[0]));
    const payload = JSON.parse(this.base64urlDecode(parts[1]));

    // ── Step 2: claims 验证（不依赖签名，快速失败）──────────────────
    const now = Math.floor(Date.now() / 1000);

    // exp: Unix 秒，Apple Identity Token 有效期通常为5分钟
    if (payload.exp < now) {
      throw new UnauthorizedException('Apple Identity Token 已过期');
    }
    // iss: 必须是 Apple 的签发方，防止伪造 Token
    if (payload.iss !== 'https://appleid.apple.com') {
      throw new UnauthorizedException('Apple Identity Token issuer 无效');
    }
    // aud: 必须是本应用的 Bundle ID（或 Service ID），防止其他 App 的 Token 被接受
    // 例: cn.gogenex.consumer
    if (payload.aud !== this.clientId) {
      this.logger.error(`Apple token aud mismatch: ${payload.aud} != ${this.clientId}`);
      throw new UnauthorizedException('Apple Token 不属于此应用');
    }

    // ── Step 3: 获取 Apple 公钥并验证 JWT 签名 ──────────────────────
    // Apple 使用 RS256（RSASSA-PKCS1-v1_5 with SHA-256）对 JWT 签名
    // JWT header 中的 kid 指向具体使用的密钥对，Apple 定期轮换密钥
    const jwks = await this.getAppleJWKS();
    const matchingKey = jwks.find((k) => k.kid === header.kid);
    if (!matchingKey) {
      // kid 不匹配通常是因为 Apple 已轮换密钥但缓存未更新
      // 下次请求会清除缓存重新获取（当前请求直接拒绝）
      throw new UnauthorizedException(`未找到 Apple 公钥 (kid=${header.kid})，请稍后重试`);
    }

    // JWT 签名验证：signed_data = base64url(header) + "." + base64url(payload)
    // signature = base64url(RSA-SHA256_sign(signed_data, apple_private_key))
    const publicKeyPem = this.jwkToPublicKeyPem(matchingKey);
    const signedData = `${parts[0]}.${parts[1]}`;        // 原始 header.payload 字符串
    const signature = Buffer.from(parts[2], 'base64url'); // base64url → Buffer

    const verifier = crypto.createVerify('SHA256');
    verifier.update(signedData);
    const valid = verifier.verify(publicKeyPem, signature);

    if (!valid) {
      // 签名无效意味着 Token 被篡改或不是 Apple 签发的
      throw new UnauthorizedException('Apple Identity Token 签名验证失败');
    }

    this.logger.log(`Apple token verified: sub=${payload.sub?.slice(0, 6)}...`);

    return {
      sub: payload.sub,          // 用户唯一 ID（稳定，跨设备/卸载重装不变）
      email: payload.email,      // 仅首次授权时提供，之后为 null（客户端需缓存）
      // email_verified 在 Apple JWT 中是字符串 'true'/'false' 或布尔值，统一处理
      emailVerified: payload.email_verified === 'true' || payload.email_verified === true,
      // is_private_email=true 表示用户选择了「隐藏邮箱」，Apple 生成了代理邮箱地址
      isPrivateEmail: payload.is_private_email === 'true' || payload.is_private_email === true,
    };
  }

  /**
   * 获取 Apple 公钥集合（JWKS）— 带24小时内存缓存
   *
   * Apple 公钥地址: https://appleid.apple.com/auth/keys
   * 响应示例:
   *   { "keys": [{ "kty":"RSA", "kid":"xxx", "use":"sig", "alg":"RS256", "n":"...", "e":"..." }] }
   *
   * Apple 密钥轮换策略:
   *   - 密钥不频繁轮换，24小时缓存通常安全
   *   - 若出现 kid 不匹配，说明已轮换，下次请求自动获取新密钥
   *   - 生产优化：可根据 Cache-Control 响应头动态调整 TTL
   */
  private async getAppleJWKS(): Promise<AppleJWK[]> {
    const now = Date.now();
    if (this.cachedJwks && now - this.jwksCachedAt < this.JWKS_CACHE_TTL) {
      return this.cachedJwks;
    }

    const data = await this.httpsGet('https://appleid.apple.com/auth/keys');
    const jwks = JSON.parse(data) as AppleJWKS;
    this.cachedJwks = jwks.keys;
    this.jwksCachedAt = now;
    return jwks.keys;
  }

  /**
   * 将 Apple JWK 格式公钥转换为 Node.js crypto 所需的 PEM 格式
   *
   * Apple Identity Token 使用 RS256 算法（RSA + SHA256）
   * JWK 公钥包含:
   *   n — RSA modulus（Base64URL 编码的大整数）
   *   e — RSA exponent（通常为 AQAB，即65537）
   *
   * Node.js 18+ 的 crypto.createPublicKey 直接支持 JWK 格式输入，
   * 导出为 SPKI（SubjectPublicKeyInfo）格式的 PEM，可被 createVerify 使用。
   */
  private jwkToPublicKeyPem(jwk: AppleJWK): string {
    const key = crypto.createPublicKey({ key: jwk as any, format: 'jwk' });
    return key.export({ type: 'spki', format: 'pem' }) as string;
  }

  /**
   * Base64URL 解码为 UTF-8 字符串
   *
   * JWT 各段使用 Base64URL 编码（标准 Base64 的 URL 安全变体）：
   *   - '+' 替换为 '-'
   *   - '/' 替换为 '_'
   *   - 去掉末尾 '=' 填充
   * 解码时需要反向操作并补回 '=' 使 Base64 长度为4的倍数。
   */
  private base64urlDecode(input: string): string {
    const base64 = input.replace(/-/g, '+').replace(/_/g, '/');
    const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4);
    return Buffer.from(padded, 'base64').toString('utf8');
  }

  private httpsGet(url: string): Promise<string> {
    return new Promise((resolve, reject) => {
      const req = https.get(url, (res) => {
        let body = '';
        res.on('data', (chunk) => (body += chunk));
        res.on('end', () => resolve(body));
      });
      req.on('error', reject);
      req.setTimeout(10000, () => {
        req.destroy();
        reject(new Error('Apple JWKS API timeout'));
      });
    });
  }
}
