import { Injectable, Logger } from '@nestjs/common';
import * as https from 'https';
import * as tls from 'tls';
import { URL } from 'url';
import { HeaderChecker } from '../../../scanner/Advanced-check/checkheader'
import { SSLChecker } from '../../../scanner/Advanced-check/checkssl'
import { SecurityResults } from '../../../scanner/Advanced-check/types';
import { EnhancedCryptoTestResult } from './interfaces/crypto-test-result.interface';


@Injectable()
export class CryptoTestService {
  private readonly logger = new Logger(CryptoTestService.name);
  private headerChecker: HeaderChecker;
  private sslChecker: SSLChecker;
  private readonly TEST_TIMEOUT = 12000; 

  constructor() {
    // Initialize the advanced checkers
    this.headerChecker = new HeaderChecker();
    this.sslChecker = new SSLChecker();
  }

  async runTest(url: string): Promise<EnhancedCryptoTestResult> {
    try {
      url = this.normalizeUrl(url);
      
      const parsedUrl = new URL(url);
      const hostname = parsedUrl.hostname;
      
      // Initialize results with enhanced test items
      const results: EnhancedCryptoTestResult = {
        passed: [],
        warnings: [],
        failed: [],
      };

      // If URL is using HTTP instead of HTTPS, that's an immediate failure
      if (parsedUrl.protocol === 'http:') {
        results.failed.push({
          test: 'HTTPS Protocol',
          message: 'Website is not using HTTPS. All data is transmitted in plaintext.',
          result: 'FAILED',
          recommendation: 'Implement HTTPS by obtaining an SSL/TLS certificate and configuring your web server to use it.'
        });
        return results;
      }

      // Initialize SecurityResults for the advanced checkers
      const securityResults = this.initializeSecurityResults(url);

      // Run all tests in parallel with proper error handling
      try {
        await Promise.all([
          this.runAdvancedChecks(url, hostname, results, securityResults).catch(error => {
            this.logger.warn(`Advanced checks failed: ${error.message}`);
            results.failed.push({
              test: 'Security Analysis',
              message: `Error during security analysis: ${error.message}`,
              result: 'FAILED',
              recommendation: 'Check if the site is accessible and review server configuration.'
            });
          }),
          this.testTLSVersion(hostname, results).catch(error => {
            this.logger.warn(`TLS Version test failed: ${error.message}`);
            results.failed.push({
              test: 'TLS Protocol Version',
              message: `Error testing TLS version: ${error.message}`,
              result: 'FAILED',
              recommendation: 'Verify that the server is properly configured to use modern TLS protocols (TLS 1.2 or 1.3).'
            });
          }),
          this.testCipherSuites(hostname, results).catch(error => {
            this.logger.warn(`Cipher Suites test failed: ${error.message}`);
            results.failed.push({
              test: 'Cipher Suite',
              message: `Error testing cipher suite: ${error.message}`,
              result: 'FAILED',
              recommendation: 'Ensure the server is configured to use strong cipher suites and disable weak ciphers.'
            });
          })
        ]);
      } catch (error) {
        this.logger.warn(`Test execution error: ${error.message}`);
        // Continue with the data we have
      }
      
      return results;
    } catch (error) {
      this.logger.error(`Failed to run cryptographic test: ${error.message}`, error.stack);
      return {
        passed: [],
        warnings: [],
        failed: [{
          test: 'Test Execution',
          message: `Failed to run test: ${error.message}`,
          result: 'FAILED',
          recommendation: 'Verify that the URL is valid and the website is accessible.'
        }],
      };
    }
  }

  private normalizeUrl(url: string): string {
    // Handle empty or null URLs
    if (!url || url.trim() === '') {
      throw new Error('URL cannot be empty');
    }

    // Normalize URL format
    url = url.trim();
    if (!url.startsWith('http://') && !url.startsWith('https://')) {
      url = 'https://' + url;
    }
    
    // Validate URL format
    try {
      new URL(url);
      return url;
    } catch (error) {
      throw new Error(`Invalid URL format: ${error.message}`);
    }
  }

  private initializeSecurityResults(url: string): SecurityResults {
    const parsedUrl = new URL(url);
    return {
      url: url,
      domain: parsedUrl.hostname,
      scanTime: new Date().toISOString(),
      summary: {
        status: 'secure',
        riskScore: 0,
        criticalIssues: 0,
        highIssues: 0,
        mediumIssues: 0,
        lowIssues: 0,
        infos: 0,
        details: [],
      },
      issues: [],
      findings: {
        malware: [],
        vulnerabilities: [],
        misconfigurations: [],
        blacklists: [],
        ssl: {},
        headers: {},
        contentAnalysis: {},
        externalResources: [],
        cmsInfo: {},
        headerStatus: [],
      },
      redirectChain: [],
      metadata: {
        serverInfo: null,
        technologies: [],
      },
      remediation: [],
      scanDuration: 0,
      results: {
        criticalIssues: 0,
        highIssues: 0,
        mediumIssues: 0,
        lowIssues: 0,
        infos: 0,
      },
      headers: {
        status: 'secure',
        severity: 0,
        details: [],
        headerItems: [],
      },
    };
  }

  private async runAdvancedChecks(url: string, hostname: string, results: EnhancedCryptoTestResult, securityResults: SecurityResults): Promise<void> {
    try {
      // Set fetch timeout with AbortController
      const controller = new AbortController();
      const timeoutId = setTimeout(() => controller.abort(), this.TEST_TIMEOUT);
      
      // Fetch the website to get headers with timeout
      const response = await fetch(url, { 
        signal: controller.signal,
        headers: {
          'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
        }
      }).finally(() => clearTimeout(timeoutId));
      
      // Run the advanced header check
      this.headerChecker.check(response.headers, securityResults);
      
      // Run the advanced SSL check
      await this.sslChecker.check(hostname, securityResults);
      
      // Map the results from the advanced checkers to our EnhancedCryptoTestResult format
      this.mapHeaderResultsToCryptoTest(securityResults, results);
      this.mapSSLResultsToCryptoTest(securityResults, results);
      
    } catch (error) {
      if (error.name === 'AbortError') {
        throw new Error('Connection timed out when testing website headers');
      }
      throw error;
    }
  }

  private mapHeaderResultsToCryptoTest(securityResults: SecurityResults, cryptoResults: EnhancedCryptoTestResult): void {
    // Avoid duplicating test entries
    const addedTests = new Set<string>();

    // Process header status findings
    if (securityResults.findings.headerStatus && Array.isArray(securityResults.findings.headerStatus)) {
      for (const headerStatus of securityResults.findings.headerStatus) {
        // Skip if undefined or missing properties
        if (!headerStatus || !headerStatus.header) continue;
        
        const testName = `${headerStatus.header} Header`;
        if (addedTests.has(testName)) continue;
        
        if (headerStatus.status === 'present') {
          cryptoResults.passed.push({
            test: testName,
            message: `${headerStatus.header} header is properly configured`,
            result: 'PASSED',
            recommendation: 'Continue maintaining this security header.'
          });
          addedTests.add(testName);
        } else if (headerStatus.status === 'missing') {
          if (['Strict-Transport-Security', 'Content-Security-Policy'].includes(headerStatus.header)) {
            cryptoResults.failed.push({
              test: testName,
              message: headerStatus.recommendation || `Missing ${headerStatus.header} header`,
              result: 'FAILED',
              recommendation: this.getHeaderRecommendation(headerStatus.header)
            });
            addedTests.add(testName);
          } else {
            cryptoResults.warnings.push({
              test: testName,
              message: headerStatus.recommendation || `Missing ${headerStatus.header} header`,
              result: 'WARNING',
              recommendation: this.getHeaderRecommendation(headerStatus.header)
            });
            addedTests.add(testName);
          }
        } else if (headerStatus.status === 'invalid') {
          cryptoResults.warnings.push({
            test: testName,
            message: headerStatus.recommendation || `Invalid ${headerStatus.header} header configuration`,
            result: 'WARNING',
            recommendation: this.getHeaderRecommendation(headerStatus.header)
          });
          addedTests.add(testName);
        }
      }
    }

    // Process cookie security findings
    const cookieIssues = securityResults.issues.filter(issue => issue?.title?.includes('Cookie'));
    for (const issue of cookieIssues) {
      if (!issue) continue;
      
      const testName = 'Cookie Security';
      if (addedTests.has(testName)) continue;
      
      if (issue.severity === 'high' || issue.severity === 'critical') {
        cryptoResults.failed.push({
          test: testName,
          message: issue.title || 'Insecure cookie configuration',
          result: 'FAILED',
          recommendation: issue.recommendation || 'Ensure cookies have the Secure, HttpOnly, and SameSite attributes set appropriately.'
        });
        addedTests.add(testName);
      } else {
        cryptoResults.warnings.push({
          test: testName,
          message: issue.title || 'Suboptimal cookie configuration',
          result: 'WARNING',
          recommendation: issue.recommendation || 'Consider setting Secure, HttpOnly, and SameSite attributes for all cookies.'
        });
        addedTests.add(testName);
      }
    }
  }

  private getHeaderRecommendation(headerName: string): string {
    switch (headerName) {
      case 'Strict-Transport-Security':
        return 'Add the HSTS header with "max-age=31536000; includeSubDomains" to enforce HTTPS connections.';
      case 'Content-Security-Policy':
        return 'Implement a CSP header to prevent XSS attacks and other content injection vulnerabilities.';
      case 'X-Content-Type-Options':
        return 'Add "X-Content-Type-Options: nosniff" to prevent MIME-type sniffing attacks.';
      case 'X-Frame-Options':
        return 'Add "X-Frame-Options: DENY" or "SAMEORIGIN" to prevent clickjacking attacks.';
      case 'Referrer-Policy':
        return 'Add "Referrer-Policy: no-referrer-when-downgrade" or "strict-origin-when-cross-origin" to control referrer information.';
      case 'Permissions-Policy':
        return 'Add a Permissions-Policy header to control which features and APIs can be used in the browser.';
      default:
        return `Add the ${headerName} header with appropriate values to enhance security.`;
    }
  }

  private mapSSLResultsToCryptoTest(securityResults: SecurityResults, cryptoResults: EnhancedCryptoTestResult): void {
    // Avoid duplicating test entries
    const addedTests = new Set<string>();
    const sslIssues = securityResults.issues.filter(issue => issue?.type === 'ssl');
    
    // Certificate validity
    if (securityResults.findings.ssl?.validTo) {
      const testName = 'Certificate Validity';
      if (!addedTests.has(testName)) {
        cryptoResults.passed.push({
          test: testName,
          message: 'Certificate is valid',
          result: 'PASSED',
          recommendation: 'Continue monitoring certificate expiration date and renew before it expires.'
        });
        addedTests.add(testName);
      
        // Check expiration soon
        if (securityResults.findings.ssl.daysToExpiry !== undefined && securityResults.findings.ssl.daysToExpiry < 30) {
          cryptoResults.warnings.push({
            test: 'Certificate Expiration',
            message: `Certificate will expire in ${securityResults.findings.ssl.daysToExpiry} days`,
            result: 'WARNING',
            recommendation: 'Renew the SSL/TLS certificate before it expires to avoid service disruption.'
          });
        }
      }
    }
    
    // Map all SSL issues to the appropriate category
    for (const issue of sslIssues) {
      if (!issue || !issue.title) continue;
      
      const testName = 'SSL/TLS Security';
      if (issue.severity === 'critical' || issue.severity === 'high') {
        cryptoResults.failed.push({
          test: testName,
          message: issue.title,
          result: 'FAILED',
          recommendation: issue.recommendation || 'Fix this critical SSL/TLS security issue immediately.'
        });
      } else if (issue.severity === 'medium') {
        cryptoResults.warnings.push({
          test: testName,
          message: issue.title,
          result: 'WARNING',
          recommendation: issue.recommendation || 'Consider addressing this SSL/TLS security issue in the near future.'
        });
      }
    }
  }

  private async testTLSVersion(hostname: string, results: EnhancedCryptoTestResult): Promise<void> {
    return new Promise<void>((resolve) => {
      const req = https.request({
        hostname: hostname,
        port: 443,
        method: 'HEAD',
        path: '/',
        timeout: this.TEST_TIMEOUT,
        headers: {
          'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
        },
        // Disable certificate validation for testing purposes
        rejectUnauthorized: false,
        // Try TLS 1.2 or higher by default
        minVersion: 'TLSv1.2'
      }, (res) => {
        // Get TLS version from socket
        const socket = res.socket as tls.TLSSocket;
        const protocol = socket ? socket.getProtocol() : null;
        
        if (!protocol) {
          results.warnings.push({
            test: 'TLS Protocol Version',
            message: 'Could not determine TLS protocol version, but HTTPS connection succeeded',
            result: 'WARNING',
            recommendation: 'Verify server configuration to ensure it uses TLS 1.2 or 1.3.'
          });
        } else if (protocol === 'TLSv1.3') {
          results.passed.push({
            test: 'TLS Protocol Version',
            message: 'Using TLS 1.3, which is currently the most secure version',
            result: 'PASSED',
            recommendation: 'Continue using TLS 1.3 for best security.'
          });
        } else if (protocol === 'TLSv1.2') {
          results.passed.push({
            test: 'TLS Protocol Version',
            message: 'Using TLS 1.2, which is acceptably secure but TLS 1.3 is recommended',
            result: 'PASSED',
            recommendation: 'Consider upgrading to TLS 1.3 for improved security and performance.'
          });
        } else if (protocol === 'TLSv1.1' || protocol === 'TLSv1') {
          results.failed.push({
            test: 'TLS Protocol Version',
            message: `Using outdated TLS version: ${protocol}`,
            result: 'FAILED',
            recommendation: 'Upgrade to TLS 1.2 or 1.3 immediately. Older versions are deprecated and insecure.'
          });
        } else if (protocol.includes('SSL')) {
          results.failed.push({
            test: 'TLS Protocol Version',
            message: `Using deprecated SSL protocol: ${protocol}`,
            result: 'FAILED',
            recommendation: 'Immediately upgrade to TLS 1.2 or 1.3. SSL is critically insecure and deprecated.'
          });
        } else {
          results.warnings.push({
            test: 'TLS Protocol Version',
            message: `Using protocol: ${protocol}. Could not definitively determine its security level`,
            result: 'WARNING',
            recommendation: 'Verify and ensure the server is configured to use TLS 1.2 or 1.3 only.'
          });
        }
        
        res.resume(); // Consume the response data to free up memory
        res.on('end', () => {
          resolve();
        });
      });
      
      req.on('error', (err) => {
        // If TLS 1.2+ fails, try with older versions as fallback
        if (err.message.includes('alert handshake failure') || (err as NodeJS.ErrnoException).code === 'ECONNRESET') {
          // Try with an explicit fallback to TLS 1.0
          const fallbackReq = https.request({
            hostname: hostname,
            port: 443,
            method: 'HEAD',
            path: '/',
            timeout: this.TEST_TIMEOUT,
            headers: {
              'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
            },
            rejectUnauthorized: false,
            minVersion: 'TLSv1',
            maxVersion: 'TLSv1.1'
          }, (res) => {
            const socket = res.socket as tls.TLSSocket;
            const protocol = socket ? socket.getProtocol() : null;
            
            if (!protocol) {
              results.warnings.push({
                test: 'TLS Protocol Version',
                message: 'Could not determine TLS protocol version, but HTTPS connection succeeded',
                result: 'WARNING',
                recommendation: 'Verify server configuration to ensure it uses TLS 1.2 or 1.3.'
              });
            } else if (protocol === 'TLSv1.1' || protocol === 'TLSv1') {
              results.failed.push({
                test: 'TLS Protocol Version',
                message: `Using outdated TLS version: ${protocol}`,
                result: 'FAILED',
                recommendation: 'Upgrade to TLS 1.2 or 1.3 immediately. Older versions are deprecated and insecure.'
              });
            } else if (protocol.includes('SSL')) {
              results.failed.push({
                test: 'TLS Protocol Version',
                message: `Using deprecated SSL protocol: ${protocol}`,
                result: 'FAILED',
                recommendation: 'Immediately upgrade to TLS 1.2 or 1.3. SSL is critically insecure and deprecated.'
              });
            }
            
            res.resume();
            res.on('end', () => {
              resolve();
            });
          });
          
          fallbackReq.on('error', () => {
            // Only add this message if no TLS version result has been added yet
            if (!results.passed.some(item => item.test === 'TLS Protocol Version') && 
                !results.failed.some(item => item.test === 'TLS Protocol Version') &&
                !results.warnings.some(item => item.test === 'TLS Protocol Version')) {
              results.warnings.push({
                test: 'TLS Protocol Version',
                message: 'Could not determine TLS version, connection attempts failed',
                result: 'WARNING',
                recommendation: 'Verify that the server is properly configured for TLS and accessible.'
              });
            }
            resolve();
          });
          
          fallbackReq.on('timeout', () => {
            fallbackReq.destroy();
            if (!results.passed.some(item => item.test === 'TLS Protocol Version') && 
                !results.failed.some(item => item.test === 'TLS Protocol Version') &&
                !results.warnings.some(item => item.test === 'TLS Protocol Version')) {
              results.warnings.push({
                test: 'TLS Protocol Version',
                message: 'Connection timed out when testing TLS version',
                result: 'WARNING',
                recommendation: 'Check server response time and TLS configuration.'
              });
            }
            resolve();
          });
          
          fallbackReq.end();
        } else {
          // Only add this if no other TLS result exists
          if (!results.passed.some(item => item.test === 'TLS Protocol Version') && 
              !results.failed.some(item => item.test === 'TLS Protocol Version') &&
              !results.warnings.some(item => item.test === 'TLS Protocol Version')) {
            // Default to a warning instead of failure
            results.warnings.push({
              test: 'TLS Protocol Version',
              message: `Could not test TLS version: ${err.message}`,
              result: 'WARNING',
              recommendation: 'Verify that the server is properly configured and accessible.'
            });
          }
          resolve();
        }
      });
      
      req.on('timeout', () => {
        req.destroy();
        if (!results.passed.some(item => item.test === 'TLS Protocol Version') && 
            !results.failed.some(item => item.test === 'TLS Protocol Version') &&
            !results.warnings.some(item => item.test === 'TLS Protocol Version')) {
          results.warnings.push({
            test: 'TLS Protocol Version',
            message: 'Connection timed out when testing TLS version',
            result: 'WARNING',
            recommendation: 'Check server response time and TLS configuration.'
          });
        }
        resolve();
      });
      
      req.end();
    });
  }

  private async testCipherSuites(hostname: string, results: EnhancedCryptoTestResult): Promise<void> {
    return new Promise<void>((resolve) => {
      const req = https.request({
        hostname: hostname,
        port: 443,
        method: 'HEAD',
        path: '/',
        timeout: this.TEST_TIMEOUT,
        headers: {
          'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
        },
        rejectUnauthorized: false,
        // Try with robust ciphers
        ciphers: 'HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA'
      }, (res) => {
        const socket = res.socket as tls.TLSSocket;
        const cipher = socket ? socket.getCipher() : null;
        
        if (!cipher || !cipher.name) {
          results.warnings.push({
            test: 'Cipher Suite',
            message: 'Could not determine cipher suite, but HTTPS connection succeeded',
            result: 'WARNING',
            recommendation: 'Verify server configuration to ensure it uses strong cipher suites.'
          });
        } else {
          // Check for weak ciphers
          const weakCiphers = [
            'RC4', 'DES', '3DES', 'NULL', 'EXPORT', 'ANON', 'aDSS', 'MD5', 'SHA1'
          ];
          
          // Check if cipher name contains any weak cipher indicators
          const weakCipherFound = weakCiphers.some(weakCipher => 
            cipher.name.toUpperCase().includes(weakCipher));
          
          // Also check for CBC (less secure than GCM/CCM)
          const usingCBC = cipher.name.includes('CBC');
          
          if (weakCipherFound) {
            results.failed.push({
              test: 'Cipher Suite',
              message: `Using weak cipher: ${cipher.name}`,
              result: 'FAILED',
              recommendation: 'Configure server to use only strong ciphers such as TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256, or TLS_AES_128_GCM_SHA256 for TLS 1.3, or ECDHE-RSA-AES256-GCM-SHA384 for TLS 1.2.'
            });
          } else if (cipher.name.includes('CHACHA20') || cipher.name.includes('GCM') || cipher.name.includes('CCM')) {
            results.passed.push({
              test: 'Cipher Suite',
              message: `Using strong AEAD cipher: ${cipher.name}`,
              result: 'PASSED',
              recommendation: 'Continue using strong AEAD ciphers for best security.'
            });
          } else if (usingCBC) {
            results.warnings.push({
              test: 'Cipher Suite',
              message: `Using CBC-mode cipher: ${cipher.name}`,
              result: 'WARNING',
              recommendation: 'Configure server to prefer GCM or ChaCha20-Poly1305 ciphers which provide authenticated encryption.'
            });
          } else {
            results.warnings.push({
              test: 'Cipher Suite',
              message: `Using cipher: ${cipher.name}. Could not definitively determine its strength`,
              result: 'WARNING',
              recommendation: 'Review and configure server to use only strong cipher suites (GCM, CCM, or ChaCha20-Poly1305).'
            });
          }
        }
        
        res.resume();
        res.on('end', () => {
          resolve();
        });
      });
      
      req.on('error', (err) => {
        // Only add this if no other cipher result exists
        if (!results.passed.some(item => item.test === 'Cipher Suite') && 
            !results.failed.some(item => item.test === 'Cipher Suite') &&
            !results.warnings.some(item => item.test === 'Cipher Suite')) {
          // Default to a warning instead of failure
          results.warnings.push({
            test: 'Cipher Suite',
            message: `Could not test cipher suite: ${err.message}`,
            result: 'WARNING',
            recommendation: 'Verify that the server is properly configured and accessible.'
          });
        }
        resolve();
      });
      
      req.on('timeout', () => {
        req.destroy();
        if (!results.passed.some(item => item.test === 'Cipher Suite') && 
            !results.failed.some(item => item.test === 'Cipher Suite') &&
            !results.warnings.some(item => item.test === 'Cipher Suite')) {
          results.warnings.push({
            test: 'Cipher Suite',
            message: 'Connection timed out when testing cipher suite',
            result: 'WARNING',
            recommendation: 'Check server response time and TLS configuration.'
          });
        }
        resolve();
      });
      
      req.end();
    });
  }
}