import { Injectable, Logger, BadRequestException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { createLocalJWKSet, jwtVerify, type JWTPayload } from 'jose';
import * as crypto from 'crypto';

/**
 * M-MEMBER-01-05: App Store JWS Verifier
 *
 * 验证 Apple App Store 交易和通知的 JWS 签名 + x5c 证书链。
 * 独立于 AppleAuthService（Sign in with Apple）。
 *
 * App Store 证书链：
 *   Apple Root CA - G3 → Apple Computer, Inc. Root
 *   (https://www.apple.com/certificateauthority/)
 */

// B1：Apple App Store 已知根证书（PEM）。
// App Store Server API 的叶子证书必须链到此根。
// 来源：https://www.apple.com/certificateauthority/
// 指纹：Apple Root CA - G3 (EC P-384, valid 2014-2039)
const APPLE_ROOT_CA_G3_PEM = `-----BEGIN CERTIFICATE-----
MIICQzCCAcmgAwIBAgIILcX8iNLFS5UwCgYIKoZIzj0EAwMwZzEbMBkGA1UEAwwS
QXBwbGUgUm9vdCBDQSAtIEczMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9u
IEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwHhcN
MTQwNDMwMTgxOTA2WhcNMzkwNDMwMTgxOTA2WjBnMRswGQYDVQQDDBJBcHBsZSBS
b290IENBIC0gRzMxJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9y
aXR5MRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzB2MBAGByqGSM49
AgEGBSuBBAAiA2IABJjpLz1AcqTtkyJygRMc3RCV8cWjTnHcFBbZDuWmBSp3ZHhf
T6T3EoDt29obPx4iNzhMLjq7hpqUGRiMnYGtYp4MDDPRdP1dDvyeEKwBmYlRxGNo
Bfv70YG1s0T7phCBaKNFMEMwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8E
BAMCAQYwHQYDVR0OBBYEFGPuG5y5jHlpBcQDBMYVwO/vAQF7MAoGCCqGSM49BAMD
A2gAMGUCMQCSSMcoRGFp1Mq2IEuc4ovA3lbB3fLGCYBbUMrHokB9AYw5aBNWB2UV
zAPpJyF/awIwXYcVE1CzkIM+5tu8DknM2Nhpx/r6wSR2cAJN9/fZx3oQzEdSQThm
dYLHJaV8ae/c
-----END CERTIFICATE-----`;

export interface AppStoreJwsHeader {
  alg: string;
  x5c?: string[];
}

@Injectable()
export class AppStoreJwsVerifier {
  private readonly logger = new Logger(AppStoreJwsVerifier.name);

  // 预解析的根证书
  private readonly rootCerts: crypto.X509Certificate[];

  constructor(private readonly configService: ConfigService) {
    // 加载已知 Apple 根证书
    this.rootCerts = [];
    for (const pem of [APPLE_ROOT_CA_G3_PEM]) {
      try {
        this.rootCerts.push(new crypto.X509Certificate(pem));
      } catch (err: any) {
        this.logger.error(`Failed to load Apple root CA: ${err.message}`);
      }
    }
    if (this.rootCerts.length === 0) {
      this.logger.error('No Apple root CAs loaded! JWS verification will fail.');
    }
  }

  async verify(signedPayload: string): Promise<JWTPayload> {
    if (!signedPayload || typeof signedPayload !== 'string') {
      throw new BadRequestException('signedPayload is required');
    }

    try {
      const parts = signedPayload.split('.');
      if (parts.length !== 3) {
        throw new BadRequestException('Invalid JWS format');
      }

      const headerJson = Buffer.from(parts[0], 'base64url').toString('utf-8');
      const header: AppStoreJwsHeader = JSON.parse(headerJson);

      if (!header.x5c || header.x5c.length === 0) {
        throw new BadRequestException('JWS header missing x5c certificate chain');
      }

      // B1 修复：验证完整 x5c 证书链 → Apple 根证书
      const chain = header.x5c.map((der) => this.x5cToPem(der));
      this.verifyCertificateChain(chain);

      // 证书链验证通过后，用叶子证书公钥验证 JWS 签名
      const leafJwk = this.certToJwk(chain[0]);
      const keySet = createLocalJWKSet({ keys: [leafJwk] });

      const { payload } = await jwtVerify(signedPayload, keySet, {
        algorithms: ['ES256'],
      });

      this.logger.log('App Store JWS verified (signature + certificate chain)');
      return payload;

    } catch (err: any) {
      if (err instanceof BadRequestException) throw err;

      this.logger.error(
        `App Store JWS verification failed: ${err?.message ?? err}`,
      );
      throw new BadRequestException('JWS verification failed');
    }
  }

  getExpectedBundleId(): string {
    return this.configService.get<string>('apple.bundleId', '') || '';
  }

  getExpectedEnvironment(): string {
    return this.configService.get<string>('apple.environment', 'Production');
  }

  // ── Certificate chain validation ──

  /**
   * B1：验证 x5c 证书链是否追溯到 Apple 已知根证书。
   *
   * 链结构：chain[0] = leaf, chain[1..n-1] = intermediates, chain[n-1] = root
   * 验证每一步：
   *   1. 证书未过期
   *   2. 下级证书的 issuer 匹配上级证书的 subject
   *   3. 上级证书的公钥签署了下级证书
   *   4. 链末端（root）必须匹配已知 Apple 根证书
   */
  private verifyCertificateChain(chainPems: string[]): void {
    if (chainPems.length < 1) {
      throw new BadRequestException('Empty certificate chain');
    }

    const certs = chainPems.map((pem) => {
      try {
        return new crypto.X509Certificate(pem);
      } catch {
        throw new BadRequestException('Invalid certificate in x5c chain');
      }
    });

    const now = new Date();

    // 验证链中每个证书的链接关系
    for (let i = 0; i < certs.length - 1; i++) {
      const child = certs[i];
      const parent = certs[i + 1];

      // 1. 证书未过期
      this.checkCertValidity(child, now, `x5c[${i}]`);

      // 2. Issuer-Subject 匹配
      if (child.issuer !== parent.subject) {
        throw new BadRequestException(
          `Certificate chain broken at x5c[${i}]: issuer "${child.issuer}" != parent subject "${parent.subject}"`,
        );
      }

      // 3. 上级证书签署了下级证书（验证签名）
      if (!child.verify(parent.publicKey)) {
        throw new BadRequestException(
          `Certificate chain verification failed at x5c[${i}]: parent did not sign child`,
        );
      }
    }

    // 验证链末端（root）
    const root = certs[certs.length - 1];
    this.checkCertValidity(root, now, `x5c[${certs.length - 1}]`);

    // Root 必须匹配 Apple 已知根证书
    const rootCN = this.extractCN(root.subject);
    const isKnownRoot = this.rootCerts.some(
      (r) => r.raw.equals(root.raw),
    );
    if (!isKnownRoot) {
      const details = `subject="${root.subject}" CN="${rootCN}"`;
      this.logger.error(`Untrusted root certificate in x5c chain: ${details}`);
      throw new BadRequestException(
        `x5c root certificate is not a trusted Apple CA: ${rootCN}`,
      );
    }
  }

  private checkCertValidity(
    cert: crypto.X509Certificate,
    now: Date,
    label: string,
  ): void {
    const validFrom = new Date(cert.validFrom);
    const validTo = new Date(cert.validTo);
    if (now < validFrom) {
      throw new BadRequestException(
        `${label} certificate not yet valid (${validFrom.toISOString()})`,
      );
    }
    if (now > validTo) {
      throw new BadRequestException(
        `${label} certificate expired (${validTo.toISOString()})`,
      );
    }
  }

  private extractCN(subject: string): string {
    const match = subject.match(/CN=([^,]+)/);
    return match ? match[1] : subject;
  }

  // ── Helpers ──

  private x5cToPem(base64Der: string): string {
    const pemBody = base64Der.match(/.{1,64}/g)?.join('\n') ?? base64Der;
    return `-----BEGIN CERTIFICATE-----\n${pemBody}\n-----END CERTIFICATE-----`;
  }

  private certToJwk(pem: string): any {
    const publicKey = crypto.createPublicKey({ key: pem, format: 'pem' });
    const jwk = publicKey.export({ format: 'jwk' });
    return { ...jwk, alg: 'ES256' };
  }
}
