import { BadRequestException, Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';

/**
 * AppleService - Service for Apple Sign-In (Sign in with Apple) token validation
 *
 * This service handles the verification of Apple ID tokens by:
 * 1. Decoding the JWT header to extract the key ID (kid)
 * 2. Fetching Apple's public keys from their JWKS endpoint
 * 3. Finding the matching public key for verification
 * 4. Verifying the token signature and extracting user claims
 *
 * Implements Apple's Sign in with Apple (SIWA) flow:
 * @see https://developer.apple.com/documentation/sign_in_with_apple
 *
 * @example
 * // Usage in authentication controller
 * const appleData = await this.appleService.tokenVerify(idToken);
 * // Returns: { name: 'apple_user_id', email: 'user@example.com' }
 */
@Injectable()
export class AppleService {
  constructor(private readonly http: HttpService) {}

  /**
   * Retrieves Apple's public signing key by key ID (kid)
   *
   * Uses jwks-rsa client to fetch Apple's JSON Web Key Set (JWKS) and
   * locate the specific public key needed to verify the token signature.
   *
   * @param kid - Key ID from the JWT header identifying the public key
   * @returns Promise containing the signing key with getPublicKey() method
   *
   * @note Consider caching the JWKS response to reduce network calls
   * @note 30-second timeout prevents hanging requests
   */
  async key(kid: string) {
    // eslint-disable-next-line @typescript-eslint/no-var-requires,global-require
    const jwksClient = require('jwks-rsa');
    const client = jwksClient({
      jwksUri: 'https://appleid.apple.com/auth/keys',
      timeout: 30000, // 30-second timeout for JWKS fetch
    });

    return client.getSigningKey(kid);
  }

  /**
   * Verifies an Apple ID token and extracts user information
   *
   * Complete token verification process:
   * 1. Decode the JWT without verification to inspect header
   * 2. Extract key ID (kid) from header
   * 3. Fetch corresponding public key from Apple's JWKS
   * 4. Verify token signature and expiration
   * 5. Extract user identifier (sub) and email claims
   *
   * @param id_token - Apple ID token (JWT) received from client
   * @returns Object containing user identifier (sub as name) and email
   * @throws BadRequestException for invalid tokens or missing data
   *
   * @note The 'name' field returns Apple's user identifier (sub), not display name
   * @note Email may be null for private email relay or user opt-out
   */
  async tokenVerify(id_token: string) {
    console.log({ id_token }); // Log token for debugging (remove in production)

    // eslint-disable-next-line @typescript-eslint/no-var-requires,global-require
    const jwt = require('jsonwebtoken');

    // Step 1: Decode token to inspect header (without verification)
    const decodedData = jwt.decode(id_token, {
      complete: true, // Return header and payload
    });

    if (!decodedData) {
      throw new BadRequestException('Invalid token: Unable to decode JWT');
    }

    console.log('decodedData', JSON.stringify(decodedData)); // Debug logging

    // Step 2: Extract key ID from JWT header
    const { header } = decodedData;
    const { kid } = header;

    if (!kid) {
      throw new BadRequestException('Token does not contain key ID (kid)');
    }

    // Step 3: Get Apple's public key for verification
    const publicKey = (await this.key(kid)).getPublicKey();
    console.log({ publicKey }); // Debug logging (remove in production)

    // Step 4: Verify token signature and extract claims
    const { sub, email } = jwt.verify(id_token, publicKey);
    console.log({ sub, email }); // Debug logging

    // Step 5: Return verified user data
    return { name: sub, email };
  }
}
