import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { ScanResult } from './entities/scan-result.entity';
import * as validator from 'validator';
import { ScanResultStatusEnum } from 'common/enum/scan-result.enum';
import { AdvancedWebsiteSecurityChecker } from './Advanced-check/advanced-website-security-checker';
import { HeaderChecker } from './Advanced-check/checkheader';

@Injectable()
export class WebsiteScannerService {
  private readonly logger = new Logger(WebsiteScannerService.name);
  private securityChecker: AdvancedWebsiteSecurityChecker;
  private headerChecker: HeaderChecker;

  constructor(
    @InjectRepository(ScanResult)
    private scanResultRepository: Repository<ScanResult>,
  ) {
    this.securityChecker = new AdvancedWebsiteSecurityChecker();
  }

  async scanWebsite(url: string): Promise<ScanResult> {
    // Validate URL format
    if (!this.isValidUrl(url)) {
      throw new Error('Invalid URL format');
    }

    // Create new scan result record
    const scanResult = new ScanResult();
    scanResult.url = url;
    scanResult.scanDate = new Date();
    scanResult.status = ScanResultStatusEnum.InProgress;

    try {
      // Save initial record with InProgress status
      await this.scanResultRepository.save(scanResult);

      // Use the advanced security checker to analyze the website
      const advancedResults = await this.securityChecker.analyzeWebsite(url);
      
      // Perform blacklist check
      if (this.securityChecker.headerChecker) {
        await this.securityChecker.headerChecker.checkBlacklist(url, advancedResults);
      }
      
      this.logger.log(`Scan completed for ${url}`);

      // Map the advanced results to our database structure
      const results = this.mapAdvancedResultsToScanResult(advancedResults);

      // Update scan result
      scanResult.results = results;
      scanResult.status = ScanResultStatusEnum.Completed;
      
      // Save updated results
      return await this.scanResultRepository.save(scanResult);
    } catch (error) {
      this.logger.error(`Comprehensive scan failed: ${error.message}`, error.stack);
      scanResult.status = ScanResultStatusEnum.Failed;
      scanResult.errorMessage = error.message;
      
      // Save the failed status
      return await this.scanResultRepository.save(scanResult);
    }
  }

  private isValidUrl(url: string): boolean {
    try {
      new URL(url);
      return validator.isURL(url, {
        protocols: ['http', 'https'],
        require_protocol: true,
        require_valid_protocol: true,
      });
    } catch (error) {
      this.logger.error(`Invalid URL: ${url}`, error.message);
      return false;
    }
  }

  private mapAdvancedResultsToScanResult(advancedResults: any): any {
    try {
      const blacklistResults = advancedResults?.findings?.blacklists || [];
      const sslResults = advancedResults?.findings?.ssl || {};
      const headerResults = advancedResults?.findings?.headerStatus || [];
      const summary = advancedResults?.summary || {};
      
      // Process SSL-specific issues
      const sslIssues = advancedResults?.issues?.filter(issue => issue.type === 'ssl') || [];
      const sslDetails = sslIssues.length > 0 
        ? sslIssues.map(issue => `${issue.title}: ${issue.recommendation}`)
        : [];
        
      // Add protocol and other SSL info if available
      if (sslResults.protocol) {
        sslDetails.push(`Protocol: ${sslResults.protocol}`);
      }
      if (sslResults.validTo) {
        sslDetails.push(`Certificate valid until: ${sslResults.validTo}`);
      }
      if (sslResults.daysToExpiry !== undefined) {
        sslDetails.push(`Days to expiry: ${sslResults.daysToExpiry}`);
      }
      
      const sslSeverity = this.calculateSslSeverity(advancedResults);
  
      return {
        blacklist: {
          status: blacklistResults.length > 0 ? 'compromised' : 'clean',
          details:
            blacklistResults.length > 0
              ? blacklistResults.map(item => `Listed on: ${item.provider || 'Unknown provider'}`)
              : ['Not found on known blacklists'],
        },
        headers: {
          status: this.getHeadersStatus(headerResults),
          details: this.extractHeaderDetails(headerResults, advancedResults.findings.headers),
          headerItems: headerResults
        },
        ssl: {
          status: this.getSslStatus(sslSeverity),
          details: sslDetails.length > 0 ? sslDetails : ['SSL analysis completed'],
          certificate: sslResults.certificate || null,
          validFrom: sslResults.validFrom || null,
          validTo: sslResults.validTo || null,
          protocol: sslResults.protocol || null,
          daysToExpiry: sslResults.daysToExpiry || null
        },
        summary: {
          criticalIssues: summary.criticalIssues || 0,
          highIssues: summary.highIssues || 0,
          mediumIssues: summary.mediumIssues || 0,
          lowIssues: summary.lowIssues || 0,
          infos: summary.infos || 0,
        }
      };
    } catch (error) {
      this.logger.error(`Error mapping results: ${error.message}`, error.stack);
      return {
        overallStatus: 'error',
        error: `Mapping error: ${error.message}`
      };
    }
  }

  private calculateSslSeverity(results: any): number {
    // Extract all SSL-related issues
    const sslIssues = results?.issues?.filter(issue => issue.type === 'ssl') || [];
    
    const severityMapping = {
      critical: 10,
      high: 7,
      medium: 5,
      low: 2,
      info: 1,
    };
    
    let maxSeverity = 0;
    
    for (const issue of sslIssues) {
      if (issue?.severity && severityMapping[issue.severity]) {
        maxSeverity = Math.max(maxSeverity, severityMapping[issue.severity]);
      }
    }
    
    // If certificate is close to expiry (less than 15 days), increase severity
    const daysToExpiry = results?.findings?.ssl?.daysToExpiry;
    if (daysToExpiry !== undefined && daysToExpiry < 15) {
      maxSeverity = Math.max(maxSeverity, 5); // Medium severity at minimum
      if (daysToExpiry < 7) {
        maxSeverity = Math.max(maxSeverity, 7); // High severity if < 7 days
      }
    }
    
    return maxSeverity;
  }


  
  private getHeadersStatus(headerResults: any[]): string {
    const missingCriticalHeaders = headerResults.filter(h => 
      h.status === 'missing' && 
      ['Strict-Transport-Security', 'Content-Security-Policy'].includes(h.header)
    );
    
    const missingHeaders = headerResults.filter(h => h.status === 'missing');
    const invalidHeaders = headerResults.filter(h => h.status === 'invalid');
    
    if (missingCriticalHeaders.length > 0) return 'compromised';
    if (missingHeaders.length > 2 || invalidHeaders.length > 2) return 'warning';
    if (missingHeaders.length > 0 || invalidHeaders.length > 0) return 'needs-improvement';
    return 'secure';
  }

  private getSslStatus(severity: number): string {
    if (severity >= 7) return 'compromised';
    if (severity >= 4) return 'warning';
    if (severity >= 1) return 'needs-improvement';
    return 'secure';
  }
  
  private extractHeaderDetails(headerResults: any[], rawHeaders: any): string[] {
    const details: string[] = [];
    
    // Add missing headers info
    const missingHeaders = headerResults.filter(h => h.status === 'missing');
    if (missingHeaders.length > 0) {
      details.push(`Missing security headers: ${missingHeaders.map(h => h.header).join(', ')}`);
    }
    
    // Add invalid headers info
    const invalidHeaders = headerResults.filter(h => h.status === 'invalid');
    if (invalidHeaders.length > 0) {
      details.push(`Incorrectly configured headers: ${invalidHeaders.map(h => h.header).join(', ')}`);
    }
    
    // Add present headers info
    const presentHeaders = headerResults.filter(h => h.status === 'present');
    if (presentHeaders.length > 0) {
      details.push(`Properly configured headers: ${presentHeaders.map(h => h.header).join(', ')}`);
    }
    
    // Add header recommendations for top 3 issues
    const recommendations = [...missingHeaders, ...invalidHeaders]
      .slice(0, 3)
      .map(h => h.recommendation);
      
    details.push(...recommendations);
    
    return details;
  }

  async getScanResults(): Promise<ScanResult[]> {
    return this.scanResultRepository.find({
      order: {
        scanDate: 'DESC'
      }
    });
  }

  async getScanResultsById(id: number): Promise<ScanResult> {
    const result = await this.scanResultRepository.findOne({ where: { id } });
    if (!result) {
      throw new Error(`Scan result with ID ${id} not found`);
    }
    return result;
  }

  async scanHeaders(url: string): Promise<any> {
    if (!this.isValidUrl(url)) {
      throw new Error('Invalid URL format');
    }

    try {
      const response = await fetch(url);
      
      // Create a new instance of HeaderChecker with security headers
      const headerChecker = this.securityChecker.headerChecker;
      
      // Generate header report with status information
      const headerReport = headerChecker.generateHeaderReport(response.headers);
      
      this.logger.log(`Header security scan completed for ${url}`);
      
      // Collect raw headers
      const rawHeaders: Record<string, string> = {};
      response.headers.forEach((value, key) => {
        rawHeaders[key] = value;
      });
      
      return {
        url,
        report: headerReport,
        rawHeaders,
      };
    } catch (error) {
      this.logger.error(`Header scan failed: ${error.message}`, error.stack);
      throw new Error(`Header scan failed for ${url}: ${error.message}`);
    }
  }
}