import { execSync } from 'child_process';
import { existsSync, readFileSync } from 'fs';
import { join } from 'path';

export interface SecurityVulnerability {
  id: string;
  severity: 'low' | 'medium' | 'high' | 'critical';
  type: 'xss' | 'injection' | 'csrf' | 'dependency' | 'configuration' | 'other';
  description: string;
  file?: string;
  line?: number;
  column?: number;
  recommendation: string;
  owaspCategory?: string;
}

export interface SecurityAuditResult {
  vulnerabilities: SecurityVulnerability[];
  summary: {
    total: number;
    critical: number;
    high: number;
    medium: number;
    low: number;
  };
  owaspCompliance: {
    score: number;
    categories: Record<string, boolean>;
  };
  timestamp: Date;
}

export interface SecurityAuditOptions {
  projectPath: string;
  includePatterns?: string[];
  excludePatterns?: string[];
  enableDependencyCheck?: boolean;
  enableCodeAnalysis?: boolean;
  enableConfigurationCheck?: boolean;
  owaspLevel?: 'basic' | 'standard' | 'advanced';
}

export class SecurityAuditor {
  private vulnerabilities: SecurityVulnerability[] = [];
  private options: SecurityAuditOptions;

  constructor(options: SecurityAuditOptions) {
    this.options = {
      includePatterns: ['**/*.ts', '**/*.js', '**/*.ordo'],
      excludePatterns: ['**/node_modules/**', '**/dist/**', '**/*.test.*'],
      enableDependencyCheck: true,
      enableCodeAnalysis: true,
      enableConfigurationCheck: true,
      owaspLevel: 'standard',
      ...options,
    };
  }

  async audit(): Promise<SecurityAuditResult> {
    this.vulnerabilities = [];

    if (this.options.enableDependencyCheck) {
      await this.auditDependencies();
    }

    if (this.options.enableCodeAnalysis) {
      await this.auditSourceCode();
    }

    if (this.options.enableConfigurationCheck) {
      await this.auditConfiguration();
    }

    return this.generateReport();
  }

  private async auditDependencies(): Promise<void> {
    try {
      const packageJsonPath = join(this.options.projectPath, 'package.json');
      if (!existsSync(packageJsonPath)) {
        return;
      }

      // Check for known vulnerable packages
      const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
      const dependencies = { ...packageJson.dependencies, ...packageJson.devDependencies };

      // Run npm audit if available
      try {
        const auditResult = execSync('npm audit --json', {
          cwd: this.options.projectPath,
          encoding: 'utf-8',
          stdio: 'pipe'
        });

        const audit = JSON.parse(auditResult);
        if (audit.vulnerabilities) {
          Object.entries(audit.vulnerabilities).forEach(([pkg, vuln]: [string, any]) => {
            this.vulnerabilities.push({
              id: `dep-${pkg}-${vuln.via?.[0]?.source || 'unknown'}`,
              severity: this.mapNpmSeverity(vuln.severity),
              type: 'dependency',
              description: `Vulnerable dependency: ${pkg} - ${vuln.via?.[0]?.title || 'Unknown vulnerability'}`,
              recommendation: `Update ${pkg} to version ${vuln.fixAvailable?.version || 'latest'}`,
              owaspCategory: 'A06:2021 – Vulnerable and Outdated Components'
            });
          });
        }
      } catch (error) {
        // npm audit failed, check manually for known vulnerable packages
        this.checkKnownVulnerablePackages(dependencies);
      }
    } catch (error) {
      console.warn('Failed to audit dependencies:', error);
    }
  }

  private checkKnownVulnerablePackages(dependencies: Record<string, string>): void {
    const knownVulnerable = [
      { name: 'lodash', versions: ['<4.17.21'], issue: 'Prototype pollution vulnerability' },
      { name: 'axios', versions: ['<0.21.2'], issue: 'SSRF vulnerability' },
      { name: 'express', versions: ['<4.17.3'], issue: 'Open redirect vulnerability' },
      { name: 'jsonwebtoken', versions: ['<8.5.1'], issue: 'Algorithm confusion vulnerability' },
    ];

    Object.entries(dependencies).forEach(([pkg, version]) => {
      const vulnerable = knownVulnerable.find(v => v.name === pkg);
      if (vulnerable) {
        this.vulnerabilities.push({
          id: `known-vuln-${pkg}`,
          severity: 'high',
          type: 'dependency',
          description: `Known vulnerable package: ${pkg}@${version} - ${vulnerable.issue}`,
          recommendation: `Update ${pkg} to a secure version`,
          owaspCategory: 'A06:2021 – Vulnerable and Outdated Components'
        });
      }
    });
  }

  private async auditSourceCode(): Promise<void> {
    const glob = await import('glob');
    const files = glob.globSync(this.options.includePatterns!, {
      cwd: this.options.projectPath,
      ignore: this.options.excludePatterns || [],
    });

    for (const file of files) {
      await this.auditFile(join(this.options.projectPath, file));
    }
  }

  private async auditFile(filePath: string): Promise<void> {
    try {
      const content = readFileSync(filePath, 'utf-8');
      const lines = content.split('\n');

      lines.forEach((line, index) => {
        this.checkForXSSVulnerabilities(line, filePath, index + 1);
        this.checkForSQLInjection(line, filePath, index + 1);
        this.checkForCSRFVulnerabilities(line, filePath, index + 1);
        this.checkForInsecureCrypto(line, filePath, index + 1);
        this.checkForHardcodedSecrets(line, filePath, index + 1);
      });
    } catch (error) {
      console.warn(`Failed to audit file ${filePath}:`, error);
    }
  }

  private checkForXSSVulnerabilities(line: string, file: string, lineNumber: number): void {
    // Check for dangerous innerHTML usage
    if (line.includes('innerHTML') && !line.includes('sanitize') && !line.includes('DOMPurify')) {
      this.vulnerabilities.push({
        id: `xss-innerHTML-${file}-${lineNumber}`,
        severity: 'high',
        type: 'xss',
        description: 'Potential XSS vulnerability: Direct innerHTML usage without sanitization',
        file,
        line: lineNumber,
        recommendation: 'Use DOMPurify.sanitize() or textContent instead of innerHTML',
        owaspCategory: 'A03:2021 – Injection'
      });
    }

    // Check for dangerous eval usage
    if (line.includes('eval(') || line.includes('Function(')) {
      this.vulnerabilities.push({
        id: `xss-eval-${file}-${lineNumber}`,
        severity: 'critical',
        type: 'xss',
        description: 'Critical XSS vulnerability: Use of eval() or Function() constructor',
        file,
        line: lineNumber,
        recommendation: 'Avoid using eval() or Function() constructor. Use safer alternatives',
        owaspCategory: 'A03:2021 – Injection'
      });
    }

    // Check for unescaped template literals in HTML context
    if (line.includes('${') && (line.includes('<') || line.includes('>'))) {
      this.vulnerabilities.push({
        id: `xss-template-${file}-${lineNumber}`,
        severity: 'medium',
        type: 'xss',
        description: 'Potential XSS vulnerability: Unescaped template literal in HTML context',
        file,
        line: lineNumber,
        recommendation: 'Ensure template literals are properly escaped when used in HTML',
        owaspCategory: 'A03:2021 – Injection'
      });
    }
  }

  private checkForSQLInjection(line: string, file: string, lineNumber: number): void {
    // Check for string concatenation in SQL queries
    const sqlKeywords = ['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'DROP', 'CREATE'];
    const hasSQLKeyword = sqlKeywords.some(keyword =>
      line.toUpperCase().includes(keyword) && line.includes('+')
    );

    if (hasSQLKeyword) {
      this.vulnerabilities.push({
        id: `sql-injection-${file}-${lineNumber}`,
        severity: 'high',
        type: 'injection',
        description: 'Potential SQL injection vulnerability: String concatenation in SQL query',
        file,
        line: lineNumber,
        recommendation: 'Use parameterized queries or prepared statements',
        owaspCategory: 'A03:2021 – Injection'
      });
    }
  }

  private checkForCSRFVulnerabilities(line: string, file: string, lineNumber: number): void {
    // Check for forms without CSRF protection
    if (line.includes('<form') && !line.includes('csrf') && !line.includes('token')) {
      this.vulnerabilities.push({
        id: `csrf-form-${file}-${lineNumber}`,
        severity: 'medium',
        type: 'csrf',
        description: 'Potential CSRF vulnerability: Form without CSRF protection',
        file,
        line: lineNumber,
        recommendation: 'Add CSRF token to forms',
        owaspCategory: 'A01:2021 – Broken Access Control'
      });
    }
  }

  private checkForInsecureCrypto(line: string, file: string, lineNumber: number): void {
    const insecureAlgorithms = ['md5', 'sha1', 'des', 'rc4'];
    const hasInsecureAlgo = insecureAlgorithms.some(algo =>
      line.toLowerCase().includes(algo)
    );

    if (hasInsecureAlgo) {
      this.vulnerabilities.push({
        id: `crypto-weak-${file}-${lineNumber}`,
        severity: 'medium',
        type: 'other',
        description: 'Weak cryptographic algorithm detected',
        file,
        line: lineNumber,
        recommendation: 'Use strong cryptographic algorithms like SHA-256, AES, etc.',
        owaspCategory: 'A02:2021 – Cryptographic Failures'
      });
    }
  }

  private checkForHardcodedSecrets(line: string, file: string, lineNumber: number): void {
    const secretPatterns = [
      /password\s*=\s*['"][^'"]+['"]/i,
      /api[_-]?key\s*=\s*['"][^'"]+['"]/i,
      /secret\s*=\s*['"][^'"]+['"]/i,
      /token\s*=\s*['"][^'"]+['"]/i,
    ];

    secretPatterns.forEach(pattern => {
      if (pattern.test(line)) {
        this.vulnerabilities.push({
          id: `hardcoded-secret-${file}-${lineNumber}`,
          severity: 'high',
          type: 'other',
          description: 'Hardcoded secret or credential detected',
          file,
          line: lineNumber,
          recommendation: 'Use environment variables or secure configuration management',
          owaspCategory: 'A07:2021 – Identification and Authentication Failures'
        });
      }
    });
  }

  private async auditConfiguration(): Promise<void> {
    // Check for security headers configuration
    const configFiles = ['ordojs.config.ts', 'ordojs.config.js', 'next.config.js', 'vite.config.ts'];

    for (const configFile of configFiles) {
      const configPath = join(this.options.projectPath, configFile);
      if (existsSync(configPath)) {
        const content = readFileSync(configPath, 'utf-8');

        if (!content.includes('helmet') && !content.includes('security')) {
          this.vulnerabilities.push({
            id: `config-no-security-headers-${configFile}`,
            severity: 'medium',
            type: 'configuration',
            description: 'Missing security headers configuration',
            file: configFile,
            recommendation: 'Configure security headers using helmet or similar middleware',
            owaspCategory: 'A05:2021 – Security Misconfiguration'
          });
        }
      }
    }

    // Check for HTTPS configuration
    const packageJsonPath = join(this.options.projectPath, 'package.json');
    if (existsSync(packageJsonPath)) {
      const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
      const scripts = packageJson.scripts || {};

      const hasHttpsConfig = Object.values(scripts).some((script: any) =>
        typeof script === 'string' && script.includes('https')
      );

      if (!hasHttpsConfig) {
        this.vulnerabilities.push({
          id: 'config-no-https',
          severity: 'low',
          type: 'configuration',
          description: 'No HTTPS configuration found in development scripts',
          recommendation: 'Configure HTTPS for development and production environments',
          owaspCategory: 'A02:2021 – Cryptographic Failures'
        });
      }
    }
  }

  private generateReport(): SecurityAuditResult {
    const summary = this.vulnerabilities.reduce(
      (acc, vuln) => {
        acc.total++;
        acc[vuln.severity]++;
        return acc;
      },
      { total: 0, critical: 0, high: 0, medium: 0, low: 0 }
    );

    const owaspCategories = {
      'A01:2021 – Broken Access Control': false,
      'A02:2021 – Cryptographic Failures': false,
      'A03:2021 – Injection': false,
      'A04:2021 – Insecure Design': false,
      'A05:2021 – Security Misconfiguration': false,
      'A06:2021 – Vulnerable and Outdated Components': false,
      'A07:2021 – Identification and Authentication Failures': false,
      'A08:2021 – Software and Data Integrity Failures': false,
      'A09:2021 – Security Logging and Monitoring Failures': false,
      'A10:2021 – Server-Side Request Forgery': false,
    };

    // Mark categories as compliant if no vulnerabilities found
    this.vulnerabilities.forEach(vuln => {
      if (vuln.owaspCategory && vuln.owaspCategory in owaspCategories) {
        (owaspCategories as any)[vuln.owaspCategory] = false;
      }
    });

    // Calculate compliance score
    const compliantCategories = Object.values(owaspCategories).filter(Boolean).length;
    const totalCategories = Object.keys(owaspCategories).length;
    const complianceScore = Math.round((compliantCategories / totalCategories) * 100);

    return {
      vulnerabilities: this.vulnerabilities,
      summary,
      owaspCompliance: {
        score: complianceScore,
        categories: owaspCategories,
      },
      timestamp: new Date(),
    };
  }

  private mapNpmSeverity(severity: string): 'low' | 'medium' | 'high' | 'critical' {
    switch (severity?.toLowerCase()) {
      case 'critical': return 'critical';
      case 'high': return 'high';
      case 'moderate': return 'medium';
      case 'low': return 'low';
      default: return 'medium';
    }
  }
}
