/**
 * The Hearth Sentinel — Code Scanner
 *
 * Scans generated and existing code against the OWASP threat database.
 * Runs on every The Hearth generation and on daily scheduled scans.
 */

import { owaspThreatDb, OWASP_TOP_10_2021 } from './owasp-db';
import { ScanFinding, ScanResult } from './types';

/** Files/patterns to skip during scanning */
const SKIP_PATTERNS = [
  'node_modules', '.next', '.build', 'dist', '.git',
  '.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico', '.woff', '.woff2',
  '.mp4', '.mp3', '.pdf', 'yarn.lock', 'package-lock.json',
];

function shouldSkip(filePath: string): boolean {
  return SKIP_PATTERNS.some(p => filePath.includes(p));
}

/**
 * CodeScanner — Scans source files for OWASP vulnerability patterns
 */
export class CodeScanner {
  /**
   * Scan a single file's content against all OWASP patterns
   */
  scanFileContent(filePath: string, content: string): ScanFinding[] {
    if (shouldSkip(filePath)) return [];

    const findings: ScanFinding[] = [];
    const lines = content.split('\n');

    for (const category of OWASP_TOP_10_2021) {
      for (const pattern of category.patterns) {
        // Keyword-based detection
        for (const keyword of pattern.keywords) {
          const lowerKeyword = keyword.toLowerCase();
          for (let i = 0; i < lines.length; i++) {
            const line = lines[i];
            if (line.toLowerCase().includes(lowerKeyword)) {
              // Context-aware: skip comments and imports
              const trimmed = line.trim();
              if (trimmed.startsWith('//') || trimmed.startsWith('*') || trimmed.startsWith('import ')) continue;

              // Avoid duplicate findings for same line/pattern
              const existing = findings.find(
                f => f.filePath === filePath && f.lineNumber === i + 1 && f.vulnerability === pattern.name
              );
              if (!existing) {
                findings.push({
                  filePath,
                  lineNumber: i + 1,
                  codeSnippet: line.trim().substring(0, 200),
                  vulnerability: pattern.name,
                  severity: pattern.severity,
                  confidence: 0.7,
                  remediation: pattern.remediation,
                  owaspCategory: category.id,
                });
              }
            }
          }
        }

        // Regex-based detection
        if (pattern.regex) {
          try {
            const regex = new RegExp(pattern.regex, 'gi');
            for (let i = 0; i < lines.length; i++) {
              if (regex.test(lines[i])) {
                const existing = findings.find(
                  f => f.filePath === filePath && f.lineNumber === i + 1 && f.vulnerability === pattern.name
                );
                if (!existing) {
                  findings.push({
                    filePath,
                    lineNumber: i + 1,
                    codeSnippet: lines[i].trim().substring(0, 200),
                    vulnerability: pattern.name,
                    severity: pattern.severity,
                    confidence: 0.85,
                    remediation: pattern.remediation,
                    owaspCategory: category.id,
                  });
                }
                regex.lastIndex = 0;
              }
            }
          } catch {
            // Invalid regex, skip
          }
        }
      }
    }

    return findings;
  }

  /**
   * Scan generated code artifacts from The Hearth
   */
  scanArtifacts(artifacts: Array<{ filePath: string; content: string }>): ScanResult {
    const start = Date.now();
    const allFindings: ScanFinding[] = [];

    for (const artifact of artifacts) {
      const findings = this.scanFileContent(artifact.filePath, artifact.content);
      allFindings.push(...findings);
    }

    const criticalCount = allFindings.filter(f => f.severity === 'critical').length;
    const highCount = allFindings.filter(f => f.severity === 'high').length;
    const mediumCount = allFindings.filter(f => f.severity === 'medium').length;
    const lowCount = allFindings.filter(f => f.severity === 'low').length;

    return {
      scanId: `scan_${Date.now()}`,
      status: 'completed',
      totalFiles: artifacts.length,
      filesScanned: artifacts.filter(a => !shouldSkip(a.filePath)).length,
      findings: allFindings,
      criticalCount,
      highCount,
      mediumCount,
      lowCount,
      summary: this.generateSummary(allFindings, artifacts.length),
      duration: Date.now() - start,
    };
  }

  private generateSummary(findings: ScanFinding[], totalFiles: number): string {
    if (findings.length === 0) {
      return `Clean scan: ${totalFiles} files scanned, no OWASP vulnerabilities detected.`;
    }

    const critical = findings.filter(f => f.severity === 'critical').length;
    const high = findings.filter(f => f.severity === 'high').length;
    const categories = [...new Set(findings.map(f => f.owaspCategory).filter(Boolean))];

    let summary = `Found ${findings.length} potential vulnerabilities across ${totalFiles} files. `;
    if (critical > 0) summary += `⚠️ ${critical} CRITICAL. `;
    if (high > 0) summary += `${high} HIGH. `;
    summary += `OWASP categories: ${categories.join(', ')}.`;

    return summary;
  }

  getDbStats() {
    return owaspThreatDb.getStats();
  }
}

export const codeScanner = new CodeScanner();
