/**
 * SSL Certificate Analyzer Service for Redirect Intelligence v2
 * 
 * Provides comprehensive SSL certificate analysis and security assessment
 */

import https from 'https';
import tls from 'tls';
import { URL } from 'url';
import { logger } from '../lib/logger';

export interface SSLCertificateInfo {
  valid: boolean;
  subject: {
    commonName?: string;
    organization?: string;
    organizationalUnit?: string;
    locality?: string;
    state?: string;
    country?: string;
  };
  issuer: {
    commonName?: string;
    organization?: string;
    organizationalUnit?: string;
    locality?: string;
    state?: string;
    country?: string;
  };
  validFrom: string;
  validTo: string;
  daysToExpiry: number;
  serialNumber?: string;
  fingerprint?: string;
  signatureAlgorithm?: string;
  keySize?: number;
  protocol?: string;
  cipher?: {
    name?: string;
    version?: string;
  };
}

export interface SSLAnalysisResult {
  host: string;
  port: number;
  certificate?: SSLCertificateInfo;
  warnings: string[];
  errors: string[];
  securityScore: number; // 0-100
  recommendations: string[];
}

export class SSLAnalyzerService {
  
  /**
   * Analyze SSL certificate for a given URL
   */
  async analyzeSSL(url: string): Promise<SSLAnalysisResult> {
    const parsedUrl = new URL(url);
    const host = parsedUrl.hostname;
    const port = parsedUrl.port ? parseInt(parsedUrl.port) : 443;
    
    logger.info(`Starting SSL analysis for: ${host}:${port}`);
    
    const result: SSLAnalysisResult = {
      host,
      port,
      warnings: [],
      errors: [],
      securityScore: 0,
      recommendations: [],
    };
    
    if (parsedUrl.protocol !== 'https:') {
      result.errors.push('URL is not HTTPS');
      result.recommendations.push('Use HTTPS to secure communications');
      return result;
    }
    
    try {
      const certificate = await this.getCertificateInfo(host, port);
      result.certificate = certificate;
      
      // Perform security analysis
      this.analyzeCertificateSecurity(result);
      
      logger.info(`SSL analysis completed for: ${host}:${port}`, {
        valid: certificate.valid,
        daysToExpiry: certificate.daysToExpiry,
        securityScore: result.securityScore
      });
      
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Unknown SSL error';
      result.errors.push(`SSL analysis failed: ${errorMessage}`);
      logger.error(`SSL analysis failed for ${host}:${port}:`, error);
    }
    
    return result;
  }
  
  /**
   * Get detailed certificate information
   */
  private async getCertificateInfo(host: string, port: number): Promise<SSLCertificateInfo> {
    return new Promise((resolve, reject) => {
      const options = {
        host,
        port,
        servername: host,
        rejectUnauthorized: false, // We want to analyze even invalid certs
        timeout: 10000,
      };
      
      const socket = tls.connect(options, () => {
        try {
          const cert = socket.getPeerCertificate(true);
          const cipher = socket.getCipher();
          const protocol = socket.getProtocol();
          
          if (!cert || Object.keys(cert).length === 0) {
            socket.end();
            reject(new Error('No certificate found'));
            return;
          }
          
          const now = new Date();
          const validFrom = new Date(cert.valid_from);
          const validTo = new Date(cert.valid_to);
          const daysToExpiry = Math.floor((validTo.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
          
          const certificateInfo: SSLCertificateInfo = {
            valid: socket.authorized,
            subject: this.parseCertificateField(cert.subject),
            issuer: this.parseCertificateField(cert.issuer),
            validFrom: validFrom.toISOString(),
            validTo: validTo.toISOString(),
            daysToExpiry,
            serialNumber: cert.serialNumber,
            fingerprint: cert.fingerprint,
            signatureAlgorithm: cert.signatureAlgorithm,
            keySize: cert.bits,
            protocol,
            cipher: cipher ? {
              name: cipher.name,
              version: cipher.version,
            } : undefined,
          };
          
          socket.end();
          resolve(certificateInfo);
          
        } catch (error) {
          socket.end();
          reject(error);
        }
      });
      
      socket.on('error', (error) => {
        reject(error);
      });
      
      socket.on('timeout', () => {
        socket.destroy();
        reject(new Error('SSL connection timeout'));
      });
      
      socket.setTimeout(10000);
    });
  }
  
  /**
   * Parse certificate subject/issuer field
   */
  private parseCertificateField(field: any): any {
    if (!field) return {};
    
    return {
      commonName: field.CN,
      organization: field.O,
      organizationalUnit: field.OU,
      locality: field.L,
      state: field.ST,
      country: field.C,
    };
  }
  
  /**
   * Analyze certificate security and generate recommendations
   */
  private analyzeCertificateSecurity(result: SSLAnalysisResult): void {
    const cert = result.certificate;
    if (!cert) return;
    
    let score = 100;
    
    // Check certificate validity
    if (!cert.valid) {
      result.warnings.push('Certificate is not valid or trusted');
      result.recommendations.push('Install a valid SSL certificate from a trusted CA');
      score -= 30;
    }
    
    // Check expiry
    if (cert.daysToExpiry < 0) {
      result.errors.push('Certificate has expired');
      result.recommendations.push('Renew the SSL certificate immediately');
      score -= 40;
    } else if (cert.daysToExpiry < 30) {
      result.warnings.push(`Certificate expires in ${cert.daysToExpiry} days`);
      result.recommendations.push('Renew the SSL certificate soon');
      score -= 10;
    } else if (cert.daysToExpiry < 90) {
      result.warnings.push(`Certificate expires in ${cert.daysToExpiry} days`);
      score -= 5;
    }
    
    // Check key size
    if (cert.keySize && cert.keySize < 2048) {
      result.warnings.push(`Weak key size: ${cert.keySize} bits`);
      result.recommendations.push('Use at least 2048-bit RSA keys or 256-bit ECC keys');
      score -= 20;
    }
    
    // Check signature algorithm
    if (cert.signatureAlgorithm) {
      if (cert.signatureAlgorithm.toLowerCase().includes('sha1')) {
        result.warnings.push('Using deprecated SHA-1 signature algorithm');
        result.recommendations.push('Upgrade to SHA-256 or better signature algorithm');
        score -= 15;
      } else if (cert.signatureAlgorithm.toLowerCase().includes('md5')) {
        result.errors.push('Using insecure MD5 signature algorithm');
        result.recommendations.push('Immediately upgrade to SHA-256 or better');
        score -= 30;
      }
    }
    
    // Check protocol version
    if (cert.protocol) {
      if (cert.protocol.includes('TLSv1.0') || cert.protocol.includes('TLSv1.1')) {
        result.warnings.push(`Using deprecated protocol: ${cert.protocol}`);
        result.recommendations.push('Upgrade to TLS 1.2 or TLS 1.3');
        score -= 15;
      } else if (cert.protocol.includes('SSLv')) {
        result.errors.push(`Using insecure protocol: ${cert.protocol}`);
        result.recommendations.push('Immediately upgrade to TLS 1.2 or TLS 1.3');
        score -= 35;
      }
    }
    
    // Check cipher suite
    if (cert.cipher?.name) {
      const cipherName = cert.cipher.name.toLowerCase();
      if (cipherName.includes('rc4') || cipherName.includes('des')) {
        result.errors.push(`Weak cipher suite: ${cert.cipher.name}`);
        result.recommendations.push('Use strong cipher suites like AES-GCM');
        score -= 25;
      } else if (cipherName.includes('cbc')) {
        result.warnings.push(`CBC cipher mode detected: ${cert.cipher.name}`);
        result.recommendations.push('Prefer GCM or ChaCha20-Poly1305 cipher modes');
        score -= 5;
      }
    }
    
    // Check subject alternative names (basic check)
    if (!cert.subject.commonName) {
      result.warnings.push('Certificate missing Common Name');
      score -= 5;
    }
    
    // Ensure score doesn't go below 0
    result.securityScore = Math.max(0, score);
    
    // Add positive recommendations
    if (result.securityScore >= 90) {
      result.recommendations.push('SSL configuration looks excellent!');
    } else if (result.securityScore >= 70) {
      result.recommendations.push('SSL configuration is good with minor improvements possible');
    } else if (result.securityScore >= 50) {
      result.recommendations.push('SSL configuration needs improvement for better security');
    } else {
      result.recommendations.push('SSL configuration requires immediate attention');
    }
  }
  
  /**
   * Quick SSL check for redirect chain analysis
   */
  async quickSSLCheck(url: string): Promise<{
    valid: boolean;
    daysToExpiry?: number;
    warnings: string[];
  }> {
    try {
      const analysis = await this.analyzeSSL(url);
      
      return {
        valid: analysis.certificate?.valid ?? false,
        daysToExpiry: analysis.certificate?.daysToExpiry,
        warnings: analysis.warnings,
      };
    } catch (error) {
      logger.warn(`Quick SSL check failed for ${url}:`, error);
      return {
        valid: false,
        warnings: ['SSL check failed'],
      };
    }
  }
}
