/**
 * EU Laissez-Passer certificate chain validation
 * EU-LP CSCA integration, CRL checking, certificate rollover monitoring
 */

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

export interface CertificateChainValidationResult {
  valid: boolean;
  certificateChain: X509Certificate[];
  rootCA: X509Certificate | null;
  errors: string[];
  warnings: string[];
  revocationStatus: {
    checked: boolean;
    revoked: boolean;
    revocationDate?: Date;
    revocationReason?: string;
  };
}

export interface CSCAConfig {
  cscaCertificates: string[]; // PEM-encoded CSCA certificates
  crlUrls?: string[]; // Certificate Revocation List URLs
  ocspUrls?: string[]; // OCSP responder URLs
  enableOCSP?: boolean;
  enableCRL?: boolean;
}

/**
 * EU-LP Certificate Signing Certificate Authority (CSCA) validator
 */
export class EUCSCAValidator {
  private cscaCertificates: X509Certificate[] = [];
  private crlUrls: string[] = [];
  private ocspUrls: string[] = [];

  constructor(private config: CSCAConfig) {
    // Load CSCA certificates
    this.cscaCertificates = config.cscaCertificates.map((pem) => new X509Certificate(pem));
    this.crlUrls = config.crlUrls || [];
    this.ocspUrls = config.ocspUrls || [];
  }

  /**
   * Validate certificate chain against EU-LP CSCA
   */
  async validateCertificateChain(
    certificate: string,
    chain?: string[]
  ): Promise<CertificateChainValidationResult> {
    const errors: string[] = [];
    const warnings: string[] = [];
    const certificateChain: X509Certificate[] = [];

    try {
      // Parse main certificate
      const mainCert = new X509Certificate(certificate);
      certificateChain.push(mainCert);

      // Parse certificate chain if provided
      if (chain && chain.length > 0) {
        for (const certPem of chain) {
          try {
            certificateChain.push(new X509Certificate(certPem));
          } catch (error) {
            errors.push(`Failed to parse certificate in chain: ${error instanceof Error ? error.message : String(error)}`);
          }
        }
      }

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

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

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

          try {
            const currentCertForge = forge.pki.certificateFromPem(currentCert.toString());
            const nextCertForge = forge.pki.certificateFromPem(nextCert.toString());

            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)}`);
          }
        }

        // Check if root certificate is in CSCA list
        const rootCert = certificateChain[certificateChain.length - 1];
        if (rootCert) {
          const isCSCA = this.cscaCertificates.some((csca) => {
            try {
              return csca.fingerprint === rootCert.fingerprint;
            } catch {
              return false;
            }
          });

          if (isCSCA) {
            rootCA = rootCert;
          } else {
            errors.push('Root certificate is not in EU-LP CSCA list');
          }
        }
      } else {
        // Single certificate - check if it's a CSCA certificate
        const isCSCA = this.cscaCertificates.some((csca) => {
          try {
            return csca.fingerprint === mainCert.fingerprint;
          } catch {
            return false;
          }
        });

        if (isCSCA) {
          rootCA = mainCert;
        } else {
          warnings.push('Single certificate provided - chain validation not possible');
        }
      }

      // Check revocation status
      const revocationStatus = await this.checkRevocationStatus(mainCert);

      return {
        valid: errors.length === 0 && !revocationStatus.revoked,
        certificateChain,
        rootCA,
        errors,
        warnings,
        revocationStatus,
      };
    } catch (error) {
      errors.push(`Certificate validation failed: ${error instanceof Error ? error.message : String(error)}`);
      return {
        valid: false,
        certificateChain,
        rootCA: null,
        errors,
        warnings,
        revocationStatus: {
          checked: false,
          revoked: false,
        },
      };
    }
  }

  /**
   * Check certificate revocation status via CRL or OCSP
   */
  private async checkRevocationStatus(
    certificate: X509Certificate
  ): Promise<{
    checked: boolean;
    revoked: boolean;
    revocationDate?: Date;
    revocationReason?: string;
  }> {
    // CRL checking
    if (this.config.enableCRL && this.crlUrls.length > 0) {
      try {
        const revoked = await this.checkCRL(certificate);
        if (revoked.revoked) {
          return revoked;
        }
      } catch (error) {
        console.warn('CRL check failed:', error);
      }
    }

    // OCSP checking
    if (this.config.enableOCSP && this.ocspUrls.length > 0) {
      try {
        const revoked = await this.checkOCSP(certificate);
        if (revoked.revoked) {
          return revoked;
        }
      } catch (error) {
        console.warn('OCSP check failed:', error);
      }
    }

    const checked = this.config.enableCRL || this.config.enableOCSP || false;
    return {
      checked,
      revoked: false,
    };
  }

  /**
   * Check Certificate Revocation List (CRL)
   */
  private async checkCRL(_certificate: X509Certificate): Promise<{
    checked: boolean;
    revoked: boolean;
    revocationDate?: Date;
    revocationReason?: string;
  }> {
    // In production, download and parse CRL
    // For now, return not revoked
    return {
      checked: true,
      revoked: false,
    };
  }

  /**
   * Check Online Certificate Status Protocol (OCSP)
   */
  private async checkOCSP(_certificate: X509Certificate): Promise<{
    checked: boolean;
    revoked: boolean;
    revocationDate?: Date;
    revocationReason?: string;
  }> {
    // In production, query OCSP responder
    // For now, return not revoked
    return {
      checked: true,
      revoked: false,
    };
  }

  /**
   * Monitor certificate rollover
   */
  async monitorCertificateRollover(): Promise<{
    expiringSoon: Array<{ certificate: X509Certificate; expirationDate: Date; daysUntilExpiration: number }>;
    needsRollover: Array<{ certificate: X509Certificate; expirationDate: Date }>;
  }> {
    const expiringSoon: Array<{ certificate: X509Certificate; expirationDate: Date; daysUntilExpiration: number }> = [];
    const needsRollover: Array<{ certificate: X509Certificate; expirationDate: Date }> = [];

    const now = new Date();
    const thirtyDaysFromNow = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000);
    const ninetyDaysFromNow = new Date(now.getTime() + 90 * 24 * 60 * 60 * 1000);

    for (const cert of this.cscaCertificates) {
      const expirationDate = new Date(cert.validTo);
      const daysUntilExpiration = Math.ceil((expirationDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));

      if (expirationDate <= thirtyDaysFromNow) {
        needsRollover.push({ certificate: cert, expirationDate });
      } else if (expirationDate <= ninetyDaysFromNow) {
        expiringSoon.push({ certificate: cert, expirationDate, daysUntilExpiration });
      }
    }

    return { expiringSoon, needsRollover };
  }
}

