import * as crypto from 'crypto';
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { HttpStatus } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { createRemoteJWKSet, jwtVerify } from 'jose';
import { CapiException } from '../../common/errors/capi.exception';
import { CAPIErrorCode } from '../../common/errors/capi-error-codes';

@Injectable()
export class AppleAuthService implements OnModuleInit {
  private readonly logger = new Logger(AppleAuthService.name);
  private readonly appleIssuer: string;
  private readonly appleBundleId: string;
  private readonly jwks: ReturnType<typeof createRemoteJWKSet>;
  /** 上次已验证通过的 nonce set，用于防重放（生产环境） */
  private readonly usedNonces = new Set<string>();

  constructor(private readonly configService: ConfigService) {
    this.appleIssuer = this.configService.get<string>(
      'apple.issuer',
      'https://appleid.apple.com',
    );
    this.appleBundleId = this.configService.get<string>('apple.bundleId', '');
    this.jwks = createRemoteJWKSet(
      new URL('https://appleid.apple.com/auth/keys'),
    );
  }

  onModuleInit() {
    const nodeEnv = process.env.NODE_ENV;
    if (nodeEnv === 'production') {
      if (!this.appleBundleId) {
        this.logger.error(
          '生产环境未配置 APPLE_BUNDLE_ID！Apple 登录将返回 401。\n' +
          '请在 .env 中添加: APPLE_BUNDLE_ID=com.your.bundle.id',
        );
        // 不抛异常 — 拒绝启动会阻断整个服务。运行时已保证 401 拒绝。
      } else {
        this.logger.log('Apple 登录已配置，使用真实验签');
      }
    } else {
      if (this.appleBundleId) {
        this.logger.log('Apple 登录使用真实验签模式（已配置 bundleId）');
      } else {
        this.logger.warn(
          'Apple 登录使用 mock 模式（未配置 APPLE_BUNDLE_ID），仅限开发环境使用',
        );
      }
    }
  }

  async verifyIdentityToken(
    identityToken: string,
    rawNonce?: string,
  ): Promise<{
    appleUserId: string;
    email?: string;
    emailVerified?: boolean;
  }> {
    const nodeEnv = process.env.NODE_ENV;

    if (!this.appleBundleId) {
      if (nodeEnv === 'production') {
        throw new CapiException(
          CAPIErrorCode.AUTH_INVALID_APPLE_TOKEN,
          'Apple 登录未配置，请联系管理员',
          HttpStatus.UNAUTHORIZED,
        );
      }
      return this.verifyMock(identityToken);
    }
    return this.verifyReal(identityToken, rawNonce);
  }

  private verifyMock(identityToken: string): {
    appleUserId: string;
    email?: string;
    emailVerified?: boolean;
  } {
    if (!identityToken || identityToken.trim().length < 4) {
      throw new CapiException(CAPIErrorCode.AUTH_INVALID_APPLE_TOKEN, 'identityToken 无效', HttpStatus.UNAUTHORIZED);
    }

    const mockUserId = crypto
      .createHash('sha256')
      .update(`apple-mock:${identityToken}`)
      .digest('hex')
      .slice(0, 64);

    const mockEmail = `${mockUserId.slice(0, 12)}@mock.apple.user`;

    return {
      appleUserId: mockUserId,
      email: mockEmail,
      emailVerified: true,
    };
  }

  private async verifyReal(
    identityToken: string,
    rawNonce?: string,
  ): Promise<{
    appleUserId: string;
    email?: string;
    emailVerified?: boolean;
  }> {
    try {
      const { payload } = await jwtVerify(identityToken, this.jwks, {
        issuer: this.appleIssuer,
        audience: this.appleBundleId,
        // nonce 校验：如果提供了 nonce，jose 会自动校验 JWT 中的 nonce claim
        ...(rawNonce ? { nonce: this.sha256Hex(rawNonce) } : {}),
      });

      return {
        appleUserId: payload.sub!,
        email:
          typeof payload.email === 'string' ? payload.email : undefined,
        emailVerified:
          payload.email_verified === true ||
          payload.email_verified === 'true',
      };
    } catch (err: any) {
      const msg: string = err?.message ?? '';
      if (msg.includes('audience')) {
        throw new CapiException(CAPIErrorCode.AUTH_INVALID_APPLE_TOKEN, `identityToken audience 不匹配，期望 ${this.appleBundleId}`, HttpStatus.UNAUTHORIZED);
      }
      if (msg.includes('issuer')) {
        throw new CapiException(CAPIErrorCode.AUTH_INVALID_APPLE_TOKEN, 'identityToken issuer 无效', HttpStatus.UNAUTHORIZED);
      }
      if (msg.includes('nonce')) {
        throw new CapiException(CAPIErrorCode.AUTH_INVALID_APPLE_TOKEN, 'identityToken nonce 验证失败', HttpStatus.UNAUTHORIZED);
      }
      throw new CapiException(CAPIErrorCode.AUTH_INVALID_APPLE_TOKEN, 'identityToken 验证失败', HttpStatus.UNAUTHORIZED);
    }
  }

  /**
   * 计算 nonce 的 SHA256 哈希（与 iOS 端 SHA256(nonce) 一致）
   */
  private sha256Hex(input: string): string {
    return crypto.createHash('sha256').update(input).digest('hex');
  }
}
