import { SecurityResults, SecurityIssue } from './types';

export class SSLChecker {
  async check(domain: string, results: SecurityResults): Promise<void> {
    if (typeof window !== 'undefined') {
      this.browserChecks(results);
      return;
    }
    
    await this.serverSideChecks(domain, results);
  }

  private browserChecks(results: SecurityResults): void {
    results.findings.ssl = {
      inBrowser: true,
      isHttps: location.protocol === 'https:',
      fullCheck: false,
      timestamp: new Date().toISOString()
    };

    if (location.protocol !== 'https:') {
      this.addIssue(
        results,
        'misconfiguration',
        'critical',
        'Website not using HTTPS',
        'Configure HTTPS with a valid certificate and enforce HSTS'
      );
    }
  }

  private async serverSideChecks(domain: string, results: SecurityResults): Promise<void> {
    try {
      const https = require('https');
      const tls = require('tls');

      // Initialize findings structure
      results.findings.ssl = {
        inBrowser: false,
        fullCheck: true,
        certificate: null,
        issuer: null,
        validFrom: null,
        validTo: null,
        daysToExpiry: null,
        selfSigned: null,
        supportedProtocols: [],
        timestamp: new Date().toISOString(),
      };

      // Check protocols in parallel
      const protocolCheck = this.checkProtocolSupport(domain, tls);
      
      // Certificate check
      await this.performCertificateCheck(domain, https, results, protocolCheck);
      
    } catch (error) {
      results.findings.ssl = {
        inBrowser: false,
        fullCheck: false,
        error: error.message,
        timestamp: new Date().toISOString(),
      };

      this.addIssue(
        results,
        'error',
        'medium',
        `Could not check SSL/TLS: ${error.message}`,
        'Ensure proper SSL/TLS configuration'
      );
    }
  }

  private async checkProtocolSupport(domain: string, tls: any): Promise<string[]> {
    const protocols = ['TLSv1', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3'];
    const supported = [];

    for (const protocol of protocols) {
      try {
        const socket = tls.connect({
          host: domain,
          port: 443,
          rejectUnauthorized: false,
          timeout: 5000,
          minVersion: protocol,
          maxVersion: protocol,
        });

        await new Promise((resolve) => {
          socket.on('secureConnect', () => {
            supported.push(protocol);
            socket.end();
            resolve(true);
          });

          socket.on('error', () => {
            socket.destroy();
            resolve(false);
          });

          socket.on('timeout', () => {
            socket.destroy();
            resolve(false);
          });
        });
      } catch (e) {
        // Protocol not supported
      }
    }

    return supported;
  }

  private async performCertificateCheck(domain: string, https: any, results: SecurityResults, protocolCheck: Promise<string[]>): Promise<void> {
    return new Promise<void>((resolve) => {
      try {
        const req = https.get(
          {
            host: domain,
            port: 443,
            rejectUnauthorized: false,
            timeout: 10000,
          },
          async (res) => {
            try {
              // Get certificate details
              const socket = res.socket as any;
              const cert = socket.getPeerCertificate(true);
              const valid = socket.authorized;

              // Fill basic certificate info
              this.populateCertificateInfo(socket, cert, results);

              // Certificate security checks
              this.checkCertificateSecurity(cert, valid, results);

              // Protocol security checks
              await this.checkProtocolSecurity(results, protocolCheck);

              // Check for HSTS Header
              this.checkServerHSTS(res, results);

              res.resume();
              resolve();
            } catch (innerError) {
              this.addIssue(
                results,
                'ssl',
                'medium',
                `SSL processing error: ${innerError.message}`,
                'Check SSL configuration'
              );
              res.resume();
              resolve();
            }
          }
        );

        req.on('error', (err) => {
          this.addIssue(
            results,
            'ssl',
            'high',
            `SSL Connection Error: ${err.message}`,
            'Check SSL configuration and certificate validity'
          );
          resolve();
        });

        req.on('timeout', () => {
          req.destroy();
          this.addIssue(
            results,
            'ssl',
            'medium',
            'SSL Connection Timeout',
            'Check server response time and network connectivity'
          );
          resolve();
        });

        req.end();
      } catch (finalError) {
        this.addIssue(
          results,
          'error',
          'medium',
          `Could not complete SSL/TLS checks: ${finalError.message}`,
          'Ensure proper SSL/TLS configuration'
        );
        resolve();
      }
    });
  }

  private populateCertificateInfo(socket: any, cert: any, results: SecurityResults): void {
    results.findings.ssl.protocol = socket.getProtocol();
    results.findings.ssl.certificate = {
      subject: cert.subject,
      fingerprint: cert.fingerprint,
      bits: cert.bits,
      pubkey: {
        type: cert.publicKey?.type || null,
        bits: cert.publicKey?.bits || null,
      },
      altNames: cert.subjectaltname?.split(', ') || [],
    };
    results.findings.ssl.issuer = cert.issuer;
    results.findings.ssl.validFrom = cert.valid_from;
    results.findings.ssl.validTo = cert.valid_to;
    results.findings.ssl.selfSigned = this.isSelfSigned(cert);
  }

  private checkCertificateSecurity(cert: any, valid: boolean, results: SecurityResults): void {
    // Check certificate validity
    if (!valid) {
      this.addIssue(
        results,
        'ssl',
        'high',
        'Invalid SSL certificate',
        'Install a properly signed SSL certificate from a trusted CA'
      );
    }

    // Check expiry date
    this.checkCertificateExpiry(cert, results);

    // Check if self-signed
    if (results.findings.ssl.selfSigned) {
      this.addIssue(
        results,
        'ssl',
        'high',
        'Self-signed certificate detected',
        'Replace with a certificate from a trusted CA'
      );
    }

    // Check key strength
    if (cert.publicKey?.bits) {
      this.checkKeyStrength(cert, results);
    }
  }

  private checkKeyStrength(cert: any, results: SecurityResults): void {
    if (cert.publicKey.type === 'RSA' && cert.publicKey.bits < 2048) {
      this.addIssue(
        results,
        'ssl',
        'high',
        `Weak RSA key (${cert.publicKey.bits} bits)`,
        'Use RSA keys with at least 2048 bits'
      );
    }

    if (cert.publicKey.type === 'EC' && cert.publicKey.bits < 256) {
      this.addIssue(
        results,
        'ssl',
        'high',
        `Weak elliptic curve key (${cert.publicKey.bits} bits)`,
        'Use elliptic curve keys with at least 256 bits'
      );
    }
  }

  private checkCertificateExpiry(cert: any, results: SecurityResults): void {
    if (cert.valid_to) {
      const expiryDate = new Date(cert.valid_to);
      const daysToExpiry = Math.floor((expiryDate.getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24));

      results.findings.ssl.daysToExpiry = daysToExpiry;

      if (daysToExpiry < 0) {
        this.addIssue(
          results,
          'ssl',
          'critical',
          'SSL certificate has expired',
          'Renew SSL certificate immediately'
        );
      } else if (daysToExpiry < 30) {
        this.addIssue(
          results,
          'ssl',
          'high',
          `SSL certificate expiring soon (${daysToExpiry} days)`,
          'Renew SSL certificate before it expires'
        );
      }
    }
  }

  private async checkProtocolSecurity(results: SecurityResults, protocolCheck: Promise<string[]>): Promise<void> {
    // Check supported protocols from our parallel check
    results.findings.ssl.supportedProtocols = await protocolCheck;

    // Check for weak protocols
    const weakProtocols = ['TLSv1', 'TLSv1.1'];
    const hasWeakProtocols = results.findings.ssl.supportedProtocols.some(p => weakProtocols.includes(p));

    if (hasWeakProtocols) {
      this.addIssue(
        results,
        'ssl',
        'high',
        `Weak SSL/TLS protocols detected: ${results.findings.ssl.supportedProtocols.filter(p => weakProtocols.includes(p)).join(', ')}`,
        'Configure server to use TLSv1.2 or TLSv1.3 only'
      );
    }
  }

  private checkServerHSTS(res: any, results: SecurityResults): void {
    try {
      const hstsHeader = res.headers['strict-transport-security'];
      results.findings.ssl.hstsHeader = hstsHeader || null;

      if (!hstsHeader) {
        this.addIssue(
          results,
          'ssl',
          'medium',
          'HSTS header not implemented',
          'Implement HTTP Strict Transport Security with appropriate max-age'
        );
      }
    } catch (e) {
      // Silently fail HSTS check
    }
  }

  private addIssue(
    results: SecurityResults,
    type: string,
    severity: 'critical' | 'high' | 'medium' | 'low' | 'info',
    title: string,
    recommendation: string
  ): void {
    results.issues.push({
      type,
      severity,
      title,
      recommendation
    });
  }

  private isSelfSigned(cert: any): boolean {
    if (!cert || !cert.subject || !cert.issuer) {
      return false;
    }
    
    // Check if subject and issuer match (simple self-signed check)
    return cert.subject.CN === cert.issuer.CN;
  }
}