import { 
  SecurityCheckerOptions, 
  SecurityResults,
  SecurityIssue,
} from './types';
import { HeaderChecker } from './checkheader';
import { SSLChecker } from './checkssl';
import { DNSChecker } from './checkDNS'; 

export class AdvancedWebsiteSecurityChecker {
  private options: SecurityCheckerOptions;
  private redirectChain: string[] = [];
  
  public headerChecker: HeaderChecker;
  private sslChecker: SSLChecker;
  private dnsChecker: DNSChecker; 

  constructor(options: Partial<SecurityCheckerOptions> = {}) {
    this.options = {
      timeout: 15000,
      userAgent: 'SecurityChecker/1.0',
      followRedirects: true,
      maxRedirects: 5,
      checkExternalResources: true,
      checkBlacklists: true,
      performContentAnalysis: true,
      ...options,
    };

    this.initializeCheckers();
  }
  private initializeCheckers(): void {
    this.headerChecker = new HeaderChecker();
    this.sslChecker = new SSLChecker();
    this.dnsChecker = new DNSChecker(); 
  }

  async analyzeWebsite(url: string): Promise<SecurityResults> {
    const results = this.initResults(url);
    
    // Check if the URL is using HTTP instead of HTTPS
    if (!url.toLowerCase().startsWith('https://')) {
      this.addIssue(
        results,
        'critical_vulnerability',
        'critical',
        'Website using insecure HTTP protocol',
        'Implement HTTPS with proper SSL/TLS certificates to encrypt traffic',
        'HTTP connections transmit data in plaintext, making them vulnerable to eavesdropping, data theft, and man-in-the-middle attacks'
      );
    }
    
    try {
      const { response, html } = await this.fetchWebsite(url, results);
      if (!response) return this.finalizeResults(results);

      results.metadata.serverInfo = response.headers.get('server');
      results.redirectChain = this.redirectChain;

      // Run checks in parallel for better performance
      await Promise.all([
        this.sslChecker.check(results.domain, results),
        this.dnsChecker.check(results.domain, results) 
      ]);
      
      // Header checks can run after obtaining response
      this.headerChecker.check(response.headers, results);

      this.calculateRiskScore(results);
      return this.finalizeResults(results);
    } catch (error) {
      this.addIssue(results, 'error', 'critical', 
        `Analysis failed: ${error.message}`, 'Check website availability');
      return this.finalizeResults(results);
    }
  }

  private initResults(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: {},
        dns: {  
          status: 'pending',
          records: {},
          issues: []
        },
        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 fetchWebsite(url: string, results: SecurityResults) {
    try {
      // Reset redirect chain
      this.redirectChain = [];

      // Configure fetch options
      const options = {
        headers: {
          'User-Agent': this.options.userAgent,
        },
        redirect: this.options.followRedirects ? ('follow' as const) : ('manual' as const),
        signal: AbortSignal.timeout(this.options.timeout),
      };

      // Track redirects manually if needed
      let currentUrl = url;
      let response = null;
      let redirectCount = 0;

      while (redirectCount <= this.options.maxRedirects) {
        this.redirectChain.push(currentUrl);

        response = await fetch(currentUrl, options);

        // Check if it's a redirect
        if ([301, 302, 303, 307, 308].includes(response.status)) {
          const location = response.headers.get('location');

          if (!location) break;

          // Handle relative URLs
          currentUrl = new URL(location, currentUrl).href;
          redirectCount++;

          // Continue to next URL
          continue;
        }

        // Not a redirect, exit loop
        break;
      }

      if (redirectCount > this.options.maxRedirects) {
        this.addIssue(results, 'warning', 'medium', 'Too many redirects detected', 'Investigate redirect chain and simplify if possible');
      }

      // Check if the final URL is using HTTP after potential redirects
      if (!currentUrl.toLowerCase().startsWith('https://')) {
        this.addIssue(
          results,
          'critical_vulnerability',
          'critical',
          'Website redirects to insecure HTTP protocol',
          'Ensure all redirects lead to HTTPS URLs',
          'Your website may initially use HTTPS but redirects to an insecure HTTP connection'
        );
      }

      // Check response status
      if (!response.ok) {
        this.addIssue(
          results,
          'error',
          'high',
          `Server returned status code ${response.status}`,
          'Investigate server error or unsuccessful response',
        );
        return { response, html: null };
      }

      // Get content type
      const contentType = response.headers.get('content-type') || '';

      // Only process HTML content
      if (!contentType.includes('text/html')) {
        this.addIssue(results, 'warning', 'low', `Not HTML content: ${contentType}`, 'Cannot perform complete analysis on non-HTML content');
        return { response, html: null };
      }

      // Get HTML content
      const html = await response.text();
      return { response, html };
    } catch (error) {
      if (error.name === 'AbortError') {
        this.addIssue(results, 'error', 'high', 'Request timed out', 'Check server response time and connectivity');
      } else {
        this.addIssue(results, 'error', 'high', `Fetch error: ${error.message}`, 'Check network connectivity and DNS resolution');
      }
      return { response: null, html: null };
    }
  }

  private addIssue(
    results: SecurityResults,
    type: string,
    severity: SecurityIssue['severity'],
    title: string,
    recommendation: string,
    details?: string
  ): void {
    results.issues.push({ type, severity, title, recommendation, details });
    this.updateSummary(results.summary, severity);
  }

  private updateSummary(summary: SecurityResults['summary'], severity: string): void {
    switch (severity) {
      case 'critical': summary.criticalIssues++; break;
      case 'high': summary.highIssues++; break;
      case 'medium': summary.mediumIssues++; break;
      case 'low': summary.lowIssues++; break;
      case 'info': summary.infos++; break;
    }
  }

  private calculateRiskScore(results: SecurityResults): void {
    const weights = {
      critical: 150,
      high: 60,
      medium: 20,
      low: 5,
      info: 0,
    };

    // Automatically set high risk score for HTTP websites
    if (!results.url.toLowerCase().startsWith('https://')) {
      results.summary.riskScore = 100;
      this.setStatusBasedOnRisk(results);
      return;
    }

    let score = Object.entries(results.summary)
      .filter(([k]) => k.endsWith('Issues'))
      .reduce((acc, [k, v]) => {
        const severity = k.replace('Issues', '') as keyof typeof weights;
        return acc + (Number(v) * (weights[severity] || 0));
      }, 0);

    // Apply exponential scaling for critical issues
    if (results.summary.criticalIssues > 0) {
      score *= Math.log1p(results.summary.criticalIssues) + 1;
    }

    results.summary.riskScore = Math.min(Math.round(score), 100);
    this.setStatusBasedOnRisk(results);
  }

  private finalizeResults(results: SecurityResults): SecurityResults {
    results.scanDuration = Date.now() - new Date(results.scanTime).getTime();
    return results;
  }

  private setStatusBasedOnRisk(results: SecurityResults): void {
    // Automatically set status to critical if using HTTP
    if (!results.url.toLowerCase().startsWith('https://')) {
      results.summary.status = 'critical';
      return;
    }

    if (results.summary.criticalIssues > 0) {
      results.summary.status = 'critical';
    } else if (results.summary.highIssues > 0) {
      results.summary.status = 'high-risk';
    } else if (results.summary.mediumIssues > 0) {
      results.summary.status = 'medium-risk';
    } else if (results.summary.lowIssues > 0) {
      results.summary.status = 'low-risk';
    } else {
      results.summary.status = 'secure';
    }
  }
}