/**
 * eIDAS (electronic IDentification, Authentication and trust Services) helpers
 * Enhanced implementation with proper certificate chain validation
 */

import fetch from 'node-fetch';
import { X509Certificate } from 'crypto';
import forge from 'node-forge';

export interface EIDASConfig {
  providerUrl: string;
  apiKey: string;
  trustedRootCAs?: string[]; // PEM-encoded trusted root certificates
}

export interface CertificateValidationResult {
  valid: boolean;
  certificateChain?: string[];
  issuer?: string;
  subject?: string;
  validityPeriod?: { notBefore: Date; notAfter: Date };
  errors?: string[];
}

export interface EIDASSignature {
  signature: string;
  certificate: string;
  timestamp: Date;
}

export class EIDASProvider {
  private trustedRootCAs: X509Certificate[] = [];

  constructor(private config: EIDASConfig) {
    // Load trusted root CAs if provided
    if (config.trustedRootCAs) {
      this.trustedRootCAs = config.trustedRootCAs.map(
        (pem) => new X509Certificate(pem)
      );
    }
  }

  /**
   * Validate certificate chain
   */
  private validateCertificateChain(
    certificate: string,
    chain?: string[]
  ): CertificateValidationResult {
    const errors: string[] = [];
    let cert: X509Certificate;
    let chainCerts: X509Certificate[] = [];

    try {
      // Parse main certificate
      cert = new X509Certificate(certificate);

      // Parse certificate chain if provided
      if (chain && chain.length > 0) {
        chainCerts = chain.map((pem) => {
          try {
            return new X509Certificate(pem);
          } catch (error) {
            errors.push(`Failed to parse certificate in chain: ${error instanceof Error ? error.message : String(error)}`);
            throw error;
          }
        });
      }

      // Check certificate validity period
      const now = new Date();
      const notBefore = new Date(cert.validFrom);
      const notAfter = new Date(cert.validTo);

      if (now < notBefore) {
        errors.push('Certificate not yet valid');
      }
      if (now > notAfter) {
        errors.push('Certificate expired');
      }

      // Validate certificate chain
      if (chainCerts.length > 0) {
        // Verify each certificate in the chain is signed by the next
        for (let i = 0; i < chainCerts.length - 1; i++) {
          const currentCert = chainCerts[i]!;
          const nextCert = chainCerts[i + 1]!;

          try {
            // Verify signature using node-forge for more detailed validation
            const currentCertForge = forge.pki.certificateFromPem(currentCert.toString());
            const nextCertForge = forge.pki.certificateFromPem(nextCert.toString());

            // Check if current cert is signed by next cert
            const verified = nextCertForge.verify(currentCertForge);
            if (!verified) {
              errors.push(`Certificate ${i} is not signed by certificate ${i + 1}`);
            }
          } catch (error) {
            errors.push(`Failed to verify certificate chain at index ${i}: ${error instanceof Error ? error.message : String(error)}`);
          }
        }

        // Verify root certificate is trusted (if trusted CAs provided)
        if (this.trustedRootCAs.length > 0) {
          const rootCert = chainCerts[chainCerts.length - 1];
          if (rootCert) {
            const isTrusted = this.trustedRootCAs.some((trusted) => {
              try {
                return trusted.fingerprint === rootCert.fingerprint;
              } catch {
                return false;
              }
            });

            if (!isTrusted) {
              errors.push('Root certificate is not in trusted CA list');
            }
          }
        }
      } else {
        // If no chain provided, we rely on the eIDAS provider's validation
        // but log a warning
        console.warn('No certificate chain provided - relying on provider validation');
      }

      return {
        valid: errors.length === 0,
        certificateChain: chain,
        issuer: cert.issuer,
        subject: cert.subject,
        validityPeriod: {
          notBefore,
          notAfter,
        },
        errors: errors.length > 0 ? errors : undefined,
      };
    } catch (error) {
      errors.push(`Certificate validation failed: ${error instanceof Error ? error.message : String(error)}`);
      return {
        valid: false,
        errors,
      };
    }
  }

  async requestSignature(document: string): Promise<EIDASSignature> {
    const response = await fetch(`${this.config.providerUrl}/sign`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${this.config.apiKey}`,
      },
      body: JSON.stringify({ document }),
    });

    if (!response.ok) {
      const errorText = await response.text();
      throw new Error(`eIDAS signature request failed: ${response.status} ${errorText}`);
    }

    const data = (await response.json()) as {
      signature: string;
      certificate: string;
      timestamp: string;
    };

    return {
      signature: data.signature,
      certificate: data.certificate,
      timestamp: new Date(data.timestamp),
    };
  }

  async verifySignature(signature: EIDASSignature): Promise<boolean> {
    try {
      // First, verify with the eIDAS provider (they handle certificate chain validation)
      const response = await fetch(`${this.config.providerUrl}/verify`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Authorization: `Bearer ${this.config.apiKey}`,
        },
        body: JSON.stringify({
          signature: signature.signature,
          certificate: signature.certificate,
          timestamp: signature.timestamp.toISOString(),
        }),
      });

      if (!response.ok) {
        return false;
      }

      const result = (await response.json()) as { 
        valid: boolean; 
        certificateChain?: string[];
        issuer?: string;
        subject?: string;
        validityPeriod?: { notBefore: string; notAfter: string };
      };

      if (!result.valid) {
        return false;
      }

      // Additional validation: Check certificate validity period
      if (result.validityPeriod) {
        const now = new Date();
        const notBefore = new Date(result.validityPeriod.notBefore);
        const notAfter = new Date(result.validityPeriod.notAfter);
        
        if (now < notBefore || now > notAfter) {
          return false; // Certificate expired or not yet valid
        }
      }

      // Additional client-side validation: Verify certificate chain if provided
      if (result.certificateChain && result.certificateChain.length > 0) {
        const validationResult = this.validateCertificateChain(
          signature.certificate,
          result.certificateChain
        );

        if (!validationResult.valid) {
          console.error('Certificate chain validation failed:', validationResult.errors);
          return false;
        }
      }

          // Additional validation: Verify signature cryptographically
          // The eIDAS provider has already verified the signature, and we've validated
          // the certificate chain. For additional security, we could verify the signature
          // locally using the certificate's public key, but this requires knowing the
          // exact signature format used by the eIDAS provider.
          // 
          // For production, consider:
          // 1. Implementing local signature verification using the certificate's public key
          // 2. Verifying the signature algorithm matches the certificate's key type
          // 3. Checking signature timestamp against certificate validity period
          //
          // For now, we trust the provider's verification result since we've validated
          // the certificate chain and the provider is a trusted eIDAS node.
          return true;
    } catch (error) {
      // Log error in production
      console.error('eIDAS signature verification failed:', error);
      return false;
    }
  }

  /**
   * Validate certificate without signature verification
   */
  validateCertificate(
    certificate: string,
    chain?: string[]
  ): CertificateValidationResult {
    return this.validateCertificateChain(certificate, chain);
  }
}

