import { logger } from '../../utils/logger';
import db from '../../config/database';

export interface SecurityVulnerability {
  id: string;
  title: string;
  description: string;
  severity: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
  endpoint: string;
  method: string;
  payload?: any;
  fixRecommendation: string;
  cvssScore?: number;
  discoveredAt: Date;
}

export interface PenetrationTestConfig {
  targetBaseUrl: string;
  endpoints: {
    path: string;
    method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
    payload?: any;
    authRequired?: boolean;
  }[];
  testDuration: number;
  concurrency: number;
  timeout: number;
}

export interface PenetrationTestResult {
  success: boolean;
  testId: string;
  targetUrl: string;
  startTime: Date;
  endTime: Date;
  duration: number;
  vulnerabilities: SecurityVulnerability[];
  totalTests: number;
  passedTests: number;
  failedTests: number;
  riskScore: number;
  summary: string;
  recommendations: string[];
}

export interface VulnerabilityStatistics {
  total: number;
  bySeverity: Record<'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL', number>;
  byEndpoint: Record<string, number>;
  byType: Record<string, number>;
  riskLevel: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
}

export default class SecurityPenetrationTest {
  private static readonly VULNERABILITY_TYPES = [
    'SQL_INJECTION',
    'XSS',
    'CSRF',
    'INSUFFICIENT_AUTH',
    'INSUFFICIENT_INPUT_VALIDATION',
    'SENSITIVE_DATA_EXPOSURE',
    'OPEN_REDIRECT',
    'BROKEN_ACCESS_CONTROL',
    'SERVER_SIDE_REQUEST_FORGERY',
    'INSECURE_DESERIALIZATION',
  ];

  static async runTest(config: PenetrationTestConfig): Promise<PenetrationTestResult> {
    const testId = this.generateTestId();
    const startTime = new Date();
    
    logger.info(`[SecurityPenetrationTest] Starting penetration test: testId=${testId}, target=${config.targetBaseUrl}`);

    const vulnerabilities: SecurityVulnerability[] = [];
    let totalTests = 0;
    let passedTests = 0;
    let failedTests = 0;

    for (const endpoint of config.endpoints) {
      const endpointTests = await this.testEndpoint(endpoint, config);
      vulnerabilities.push(...endpointTests.vulnerabilities);
      totalTests += endpointTests.totalTests;
      passedTests += endpointTests.passedTests;
      failedTests += endpointTests.failedTests;
    }

    const endTime = new Date();
    const duration = endTime.getTime() - startTime.getTime();
    const statistics = this.calculateVulnerabilityStatistics(vulnerabilities);
    const riskScore = this.calculateRiskScore(statistics);
    const recommendations = this.generateRecommendations(vulnerabilities);
    
    const result: PenetrationTestResult = {
      success: failedTests === 0,
      testId,
      targetUrl: config.targetBaseUrl,
      startTime,
      endTime,
      duration,
      vulnerabilities,
      totalTests,
      passedTests,
      failedTests,
      riskScore,
      summary: this.generateSummary(statistics),
      recommendations,
    };

    await this.saveTestResult(result);
    
    logger.info(`[SecurityPenetrationTest] Penetration test completed: testId=${testId}, riskScore=${riskScore}, vulnerabilities=${vulnerabilities.length}`);
    return result;
  }

  private static async testEndpoint(
    endpoint: PenetrationTestConfig['endpoints'][0],
    config: PenetrationTestConfig
  ): Promise<{
    vulnerabilities: SecurityVulnerability[];
    totalTests: number;
    passedTests: number;
    failedTests: number;
  }> {
    const vulnerabilities: SecurityVulnerability[] = [];
    let totalTests = 0;
    let passedTests = 0;
    let failedTests = 0;

    for (const vulnerabilityType of this.VULNERABILITY_TYPES) {
      totalTests++;
      
      try {
        const vulnerability = await this.testVulnerabilityType(
          vulnerabilityType,
          endpoint,
          config
        );
        
        if (vulnerability) {
          vulnerabilities.push(vulnerability);
          failedTests++;
        } else {
          passedTests++;
        }
      } catch (error: any) {
        failedTests++;
        logger.error(`[SecurityPenetrationTest] Error testing ${vulnerabilityType} on ${endpoint.path}: ${error.message}`);
      }
    }

    return {
      vulnerabilities,
      totalTests,
      passedTests,
      failedTests,
    };
  }

  private static async testVulnerabilityType(
    type: string,
    endpoint: PenetrationTestConfig['endpoints'][0],
    config: PenetrationTestConfig
  ): Promise<SecurityVulnerability | null> {
    switch (type) {
      case 'SQL_INJECTION':
        return this.testSQLInjection(endpoint, config);
      case 'XSS':
        return this.testXSS(endpoint, config);
      case 'CSRF':
        return this.testCSRF(endpoint, config);
      case 'INSUFFICIENT_AUTH':
        return this.testInsufficientAuth(endpoint, config);
      case 'INSUFFICIENT_INPUT_VALIDATION':
        return this.testInputValidation(endpoint, config);
      default:
        return this.generateMockVulnerability(type, endpoint);
    }
  }

  private static testSQLInjection(
    endpoint: PenetrationTestConfig['endpoints'][0],
    config: PenetrationTestConfig
  ): SecurityVulnerability | null {
    const testPayloads = [
      "' OR '1'='1",
      "1' --",
      "1'; DROP TABLE users; --",
    ];

    for (const payload of testPayloads) {
      const testData = this.injectPayload(endpoint.payload, payload);
      if (this.simulateVulnerability('SQL_INJECTION', endpoint)) {
        return {
          id: `vuln_${Date.now()}_sql`,
          title: 'SQL Injection Vulnerability',
          description: 'Endpoint is vulnerable to SQL injection attacks',
          severity: 'HIGH',
          endpoint: endpoint.path,
          method: endpoint.method,
          payload: testData,
          fixRecommendation: 'Use parameterized queries and input validation',
          cvssScore: 8.6,
          discoveredAt: new Date(),
        };
      }
    }

    return null;
  }

  private static testXSS(
    endpoint: PenetrationTestConfig['endpoints'][0],
    config: PenetrationTestConfig
  ): SecurityVulnerability | null {
    const testPayloads = [
      '<script>alert("XSS")</script>',
      '<img src="x" onerror="alert(1)">',
      '<iframe src="javascript:alert(1)"></iframe>',
    ];

    for (const payload of testPayloads) {
      const testData = this.injectPayload(endpoint.payload, payload);
      if (this.simulateVulnerability('XSS', endpoint)) {
        return {
          id: `vuln_${Date.now()}_xss`,
          title: 'Cross-Site Scripting (XSS) Vulnerability',
          description: 'Endpoint is vulnerable to XSS attacks',
          severity: 'MEDIUM',
          endpoint: endpoint.path,
          method: endpoint.method,
          payload: testData,
          fixRecommendation: 'Sanitize user input and use Content-Security-Policy header',
          cvssScore: 6.1,
          discoveredAt: new Date(),
        };
      }
    }

    return null;
  }

  private static testCSRF(
    endpoint: PenetrationTestConfig['endpoints'][0],
    config: PenetrationTestConfig
  ): SecurityVulnerability | null {
    if (endpoint.method !== 'GET' && !this.simulateCSRFProtection()) {
      return {
        id: `vuln_${Date.now()}_csrf`,
        title: 'Cross-Site Request Forgery (CSRF) Vulnerability',
        description: 'Endpoint lacks CSRF protection',
        severity: 'MEDIUM',
        endpoint: endpoint.path,
        method: endpoint.method,
        fixRecommendation: 'Implement CSRF tokens and validate Origin/Referer headers',
        cvssScore: 6.8,
        discoveredAt: new Date(),
      };
    }

    return null;
  }

  private static testInsufficientAuth(
    endpoint: PenetrationTestConfig['endpoints'][0],
    config: PenetrationTestConfig
  ): SecurityVulnerability | null {
    if (endpoint.authRequired && !this.simulateAuthCheck()) {
      return {
        id: `vuln_${Date.now()}_auth`,
        title: 'Insufficient Authentication',
        description: 'Endpoint lacks proper authentication',
        severity: 'HIGH',
        endpoint: endpoint.path,
        method: endpoint.method,
        fixRecommendation: 'Implement proper authentication and authorization checks',
        cvssScore: 8.8,
        discoveredAt: new Date(),
      };
    }

    return null;
  }

  private static testInputValidation(
    endpoint: PenetrationTestConfig['endpoints'][0],
    config: PenetrationTestConfig
  ): SecurityVulnerability | null {
    const testPayloads = [
      { key: 'id', value: '999999999999999' },
      { key: 'name', value: 'a'.repeat(1000) },
      { key: 'email', value: 'invalid-email' },
    ];

    for (const payload of testPayloads) {
      const testData = this.injectPayload(endpoint.payload, payload.value, payload.key);
      if (this.simulateInputValidationFailure()) {
        return {
          id: `vuln_${Date.now()}_validation`,
          title: 'Insufficient Input Validation',
          description: 'Endpoint lacks proper input validation',
          severity: 'LOW',
          endpoint: endpoint.path,
          method: endpoint.method,
          payload: testData,
          fixRecommendation: 'Implement comprehensive input validation and sanitization',
          cvssScore: 3.1,
          discoveredAt: new Date(),
        };
      }
    }

    return null;
  }

  private static generateMockVulnerability(
    type: string,
    endpoint: PenetrationTestConfig['endpoints'][0]
  ): SecurityVulnerability | null {
    if (Math.random() > 0.8) {
      return {
        id: `vuln_${Date.now()}_${type.toLowerCase()}`,
        title: `${type.replace('_', ' ')} Vulnerability`,
        description: `Endpoint is vulnerable to ${type} attacks`,
        severity: Math.random() > 0.5 ? 'MEDIUM' : 'LOW',
        endpoint: endpoint.path,
        method: endpoint.method,
        fixRecommendation: `Implement proper protection against ${type}`,
        cvssScore: Math.random() * 5 + 3,
        discoveredAt: new Date(),
      };
    }
    return null;
  }

  private static simulateVulnerability(type: string, endpoint: any): boolean {
    const vulnerabilityChance = {
      'SQL_INJECTION': 0.1,
      'XSS': 0.15,
      'CSRF': 0.2,
      'INSUFFICIENT_AUTH': 0.1,
      'INSUFFICIENT_INPUT_VALIDATION': 0.25,
    };

    return Math.random() < (vulnerabilityChance[type as keyof typeof vulnerabilityChance] || 0.05);
  }

  private static simulateCSRFProtection(): boolean {
    return Math.random() > 0.3;
  }

  private static simulateAuthCheck(): boolean {
    return Math.random() > 0.2;
  }

  private static simulateInputValidationFailure(): boolean {
    return Math.random() > 0.4;
  }

  private static injectPayload(basePayload: any, payload: any, key?: string): any {
    if (!basePayload) return { [key || 'test']: payload };
    
    const result = { ...basePayload };
    if (key && result[key] !== undefined) {
      result[key] = payload;
    } else {
      const firstKey = Object.keys(result)[0];
      if (firstKey) {
        result[firstKey] = payload;
      } else {
        result.test = payload;
      }
    }
    return result;
  }

  private static calculateVulnerabilityStatistics(
    vulnerabilities: SecurityVulnerability[]
  ): VulnerabilityStatistics {
    const stats: VulnerabilityStatistics = {
      total: vulnerabilities.length,
      bySeverity: { LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0 },
      byEndpoint: {},
      byType: {},
      riskLevel: 'LOW',
    };

    for (const vuln of vulnerabilities) {
      stats.bySeverity[vuln.severity]++;
      stats.byEndpoint[vuln.endpoint] = (stats.byEndpoint[vuln.endpoint] || 0) + 1;
      stats.byType[vuln.title] = (stats.byType[vuln.title] || 0) + 1;
    }

    if (stats.bySeverity.CRITICAL > 0) {
      stats.riskLevel = 'CRITICAL';
    } else if (stats.bySeverity.HIGH > 2) {
      stats.riskLevel = 'HIGH';
    } else if (stats.bySeverity.MEDIUM > 5) {
      stats.riskLevel = 'MEDIUM';
    }

    return stats;
  }

  private static calculateRiskScore(statistics: VulnerabilityStatistics): number {
    let score = 0;
    score += statistics.bySeverity.CRITICAL * 10;
    score += statistics.bySeverity.HIGH * 5;
    score += statistics.bySeverity.MEDIUM * 2;
    score += statistics.bySeverity.LOW * 0.5;
    return Math.min(100, score);
  }

  private static generateRecommendations(vulnerabilities: SecurityVulnerability[]): string[] {
    const recommendations = new Set<string>();
    
    for (const vuln of vulnerabilities) {
      recommendations.add(vuln.fixRecommendation);
    }

    if (vulnerabilities.length > 5) {
      recommendations.add('Implement a comprehensive security testing pipeline');
    }

    if (vulnerabilities.some(v => v.severity === 'CRITICAL' || v.severity === 'HIGH')) {
      recommendations.add('Conduct a full security audit before production deployment');
    }

    return Array.from(recommendations);
  }

  private static generateSummary(statistics: VulnerabilityStatistics): string {
    const severityCounts = Object.entries(statistics.bySeverity)
      .filter(([_, count]) => count > 0)
      .map(([severity, count]) => `${count} ${severity}`)
      .join(', ');

    return `Found ${statistics.total} vulnerabilities: ${severityCounts}. Risk level: ${statistics.riskLevel}.`;
  }

  private static async saveTestResult(result: PenetrationTestResult): Promise<void> {
    await db('cf_security_test').insert({
      id: result.testId,
      target_url: result.targetUrl,
      start_time: result.startTime,
      end_time: result.endTime,
      duration: result.duration,
      total_tests: result.totalTests,
      passed_tests: result.passedTests,
      failed_tests: result.failedTests,
      risk_score: result.riskScore,
      summary: result.summary,
      vulnerabilities: JSON.stringify(result.vulnerabilities),
      recommendations: JSON.stringify(result.recommendations),
      created_at: new Date(),
    });
  }

  static async getTestResults(limit: number = 10): Promise<any[]> {
    return await db('cf_security_test')
      .orderBy('created_at', 'desc')
      .limit(limit);
  }

  static async getVulnerabilityTrends(days: number = 30): Promise<any[]> {
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - days);

    const results = await db('cf_security_test')
      .where('created_at', '>=', cutoffDate)
      .orderBy('created_at');

    return results.map(result => ({
      date: result.created_at,
      totalVulnerabilities: JSON.parse(result.vulnerabilities).length,
      riskScore: result.risk_score,
      criticalVulnerabilities: JSON.parse(result.vulnerabilities).filter((v: any) => v.severity === 'CRITICAL').length,
      highVulnerabilities: JSON.parse(result.vulnerabilities).filter((v: any) => v.severity === 'HIGH').length,
    }));
  }

  private static generateTestId(): string {
    return `pen_test_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }
}
