/**
 * Automated Security Remediation System
 * Provides intelligent automated fixes for detected vulnerabilities
 */

import * as fs from "fs/promises";
import * as path from "path";
import { SecurityVulnerability, SecurityScanResult } from "./AISecurityScanner.js";
import { SecurityUtils } from "./SecurityConfig.js";
import { SecurityValidationError } from "./InputValidator.js";
import { LoggerFactory } from "@/utils/logger.js";

interface RemediationAction {
  id: string;
  type: "replace" | "insert" | "delete" | "config" | "file";
  target: {
    file?: string | undefined;
    line?: number | undefined;
    pattern?: RegExp | undefined;
    value?: string | undefined;
  };
  replacement: {
    content?: string;
    config?: Record<string, unknown>;
    action?: string;
  };
  backup: {
    enabled: boolean;
    path?: string;
  };
  validation: {
    test?: string;
    expected?: unknown;
  };
}

export interface RemediationResult {
  vulnerabilityId: string;
  success: boolean;
  action: string;
  details: string;
  timestamp: Date;
  backupPath?: string | undefined;
  validationResult?: boolean | undefined;
}

interface RemediationPlan {
  planId: string;
  vulnerabilities: SecurityVulnerability[];
  actions: RemediationAction[];
  estimatedDuration: number;
  riskLevel: "low" | "medium" | "high";
  requiresApproval: boolean;
}

/**
 * Automated Security Remediation Engine
 */
export class AutomatedRemediation {
  private readonly logger = LoggerFactory.security();
  private remediationHistory: RemediationResult[] = [];
  private backupDirectory = "security-backups";

  /**
   * Create remediation plan for scan results
   */
  async createRemediationPlan(scanResult: SecurityScanResult): Promise<RemediationPlan> {
    const planId = SecurityUtils.generateSecureToken(16);
    const remediableVulns = scanResult.vulnerabilities.filter((v) => v.remediation.automated);

    this.logger.info("Creating remediation plan", {
      planId,
      remediableVulnerabilities: remediableVulns.length,
      totalVulnerabilities: scanResult.vulnerabilities.length,
    });

    const actions: RemediationAction[] = [];
    let estimatedDuration = 0;
    let maxRiskLevel: "low" | "medium" | "high" = "low";

    for (const vulnerability of remediableVulns) {
      const action = await this.createRemediationAction(vulnerability);
      if (action) {
        actions.push(action);
        estimatedDuration += 30; // 30 seconds per action estimate

        // Update risk level
        if (vulnerability.severity === "critical" || vulnerability.severity === "high") {
          maxRiskLevel = "high";
        } else if (vulnerability.severity === "medium" && maxRiskLevel === "low") {
          maxRiskLevel = "medium";
        }
      }
    }

    const requiresApproval = maxRiskLevel === "high" || actions.length > 10;

    return {
      planId,
      vulnerabilities: remediableVulns,
      actions,
      estimatedDuration,
      riskLevel: maxRiskLevel,
      requiresApproval,
    };
  }

  /**
   * Create remediation action for a specific vulnerability
   */
  private async createRemediationAction(vulnerability: SecurityVulnerability): Promise<RemediationAction | null> {
    const actionId = SecurityUtils.generateSecureToken(12);

    switch (vulnerability.type) {
      case "SQL Injection":
        return this.createSQLInjectionRemediation(actionId, vulnerability);

      case "Cross-Site Scripting (XSS)":
        return this.createXSSRemediation(actionId, vulnerability);

      case "Path Traversal":
        return this.createPathTraversalRemediation(actionId, vulnerability);

      case "Credential Exposure":
        return this.createCredentialExposureRemediation(actionId, vulnerability);

      case "Insecure Configuration":
        return this.createConfigRemediation(actionId, vulnerability);

      case "Information Disclosure":
        return this.createInfoDisclosureRemediation(actionId, vulnerability);

      default:
        return null;
    }
  }

  /**
   * Create SQL injection remediation
   */
  private createSQLInjectionRemediation(actionId: string, vulnerability: SecurityVulnerability): RemediationAction {
    return {
      id: actionId,
      type: "replace",
      target: {
        file: vulnerability.location.file,
        line: vulnerability.location.line,
        pattern: /(SELECT|INSERT|UPDATE|DELETE).*?['"].*?['"].*?(WHERE|FROM|INTO)/gi,
      },
      replacement: {
        content:
          "// SECURITY FIX: Replace with parameterized query to prevent SQL injection\n" +
          "// Example: db.query('SELECT * FROM users WHERE id = ?', [userId])",
      },
      backup: {
        enabled: true,
      },
      validation: {
        test: 'grep -n "SELECT.*WHERE" $file | wc -l',
        expected: 0,
      },
    };
  }

  /**
   * Create XSS remediation
   */
  private createXSSRemediation(actionId: string, vulnerability: SecurityVulnerability): RemediationAction {
    return {
      id: actionId,
      type: "replace",
      target: {
        file: vulnerability.location.file,
        line: vulnerability.location.line,
        pattern: /innerHTML\s*=\s*([^;]+);?/gi,
      },
      replacement: {
        content: "textContent = $1; // XSS remediation: use textContent instead of innerHTML",
      },
      backup: {
        enabled: true,
      },
      validation: {
        test: 'grep -n "innerHTML" $file | wc -l',
        expected: 0,
      },
    };
  }

  /**
   * Create path traversal remediation
   */
  private createPathTraversalRemediation(actionId: string, vulnerability: SecurityVulnerability): RemediationAction {
    return {
      id: actionId,
      type: "replace",
      target: {
        file: vulnerability.location.file,
        line: vulnerability.location.line,
        pattern: /\.\.[\/\\]/g,
      },
      replacement: {
        content: "", // Remove path traversal sequences
      },
      backup: {
        enabled: true,
      },
      validation: {
        test: 'grep -n "\\.\\./" $file | wc -l',
        expected: 0,
      },
    };
  }

  /**
   * Create credential exposure remediation
   */
  private createCredentialExposureRemediation(
    actionId: string,
    vulnerability: SecurityVulnerability,
  ): RemediationAction {
    return {
      id: actionId,
      type: "replace",
      target: {
        file: vulnerability.location.file,
        line: vulnerability.location.line,
        pattern: /(password|secret|key|token)\s*[:=]\s*['"][^'"]+['"]/gi,
      },
      replacement: {
        content:
          "// SECURITY FIX: Credential moved to environment variable\n" +
          "// Add to .env: $1=your_actual_value\n" +
          "$1: process.env.$1 || (() => { throw new Error('$1 environment variable required'); })()",
      },
      backup: {
        enabled: true,
      },
      validation: {
        test: 'grep -n "password.*=.*[\'\\"]" $file | wc -l',
        expected: 0,
      },
    };
  }

  /**
   * Create configuration remediation
   */
  private createConfigRemediation(actionId: string, vulnerability: SecurityVulnerability): RemediationAction {
    return {
      id: actionId,
      type: "replace",
      target: {
        file: vulnerability.location.file,
        line: vulnerability.location.line,
        pattern: /(ssl|secure|verify)\s*[:=]\s*false/gi,
      },
      replacement: {
        content: "$1: true // Security fix: enabled secure configuration",
      },
      backup: {
        enabled: true,
      },
      validation: {
        test: 'grep -n "ssl.*false" $file | wc -l',
        expected: 0,
      },
    };
  }

  /**
   * Create information disclosure remediation
   */
  private createInfoDisclosureRemediation(actionId: string, vulnerability: SecurityVulnerability): RemediationAction {
    return {
      id: actionId,
      type: "replace",
      target: {
        file: vulnerability.location.file,
        line: vulnerability.location.line,
        pattern: /(debug|trace|error)\s*[:=]\s*true/gi,
      },
      replacement: {
        content: "$1: process.env.NODE_ENV !== 'production' // Security fix: disable in production",
      },
      backup: {
        enabled: true,
      },
      validation: {
        test: 'grep -n "debug.*true" $file | wc -l',
        expected: 0,
      },
    };
  }

  /**
   * Execute remediation plan
   */
  async executeRemediationPlan(
    plan: RemediationPlan,
    options: {
      dryRun?: boolean;
      requireConfirmation?: boolean;
    } = {},
  ): Promise<RemediationResult[]> {
    this.logger.info("Executing remediation plan", {
      planId: plan.planId,
      actionCount: plan.actions.length,
      dryRun: options.dryRun,
    });

    if (options.dryRun) {
      this.logger.info("Running in dry-run mode - no changes will be made");
      return this.simulateRemediationActions(plan.actions);
    }

    const results: RemediationResult[] = [];

    // Ensure backup directory exists
    await this.ensureBackupDirectory();

    for (const action of plan.actions) {
      try {
        this.logger.debug("Executing remediation action", {
          actionId: action.id,
          actionType: action.type,
          targetFile: action.target.file,
        });

        const result = await this.executeRemediationAction(action);
        results.push(result);

        if (!result.success) {
          this.logger.error("Remediation action failed", {
            actionId: action.id,
            details: result.details,
          });
        }
      } catch (_error) {
        this.logger.error("Remediation action threw error", {
          actionId: action.id,
          _error: String(_error),
        });
        results.push({
          vulnerabilityId: action.id,
          success: false,
          action: action.type,
          details: `Error: ${_error instanceof Error ? _error.message : String(_error)}`,
          timestamp: new Date(),
        });
      }
    }

    this.remediationHistory.push(...results);

    const successCount = results.filter((r) => r.success).length;
    this.logger.info("Remediation plan completed", {
      planId: plan.planId,
      successCount,
      totalActions: results.length,
      successRate: `${Math.round((successCount / results.length) * 100)}%`,
    });

    return results;
  }

  /**
   * Execute a single remediation action
   */
  private async executeRemediationAction(action: RemediationAction): Promise<RemediationResult> {
    const startTime = Date.now();

    try {
      let backupPath: string | undefined;

      // Create backup if enabled
      if (action.backup.enabled && action.target.file) {
        backupPath = await this.createBackup(action.target.file);
      }

      // Execute the action based on type
      switch (action.type) {
        case "replace":
          await this.executeReplaceAction(action);
          break;

        case "insert":
          await this.executeInsertAction(action);
          break;

        case "delete":
          await this.executeDeleteAction(action);
          break;

        case "config":
          await this.executeConfigAction(action);
          break;

        case "file":
          await this.executeFileAction(action);
          break;

        default:
          throw new Error(`Unknown action type: ${action.type}`);
      }

      // Validate the fix if validation is provided
      let validationResult: boolean | undefined;
      if (action.validation && action.target.file) {
        validationResult = await this.validateRemediation(action);
      }

      return {
        vulnerabilityId: action.id,
        success: true,
        action: action.type,
        details: `Remediation completed successfully in ${Date.now() - startTime}ms`,
        timestamp: new Date(),
        backupPath,
        validationResult,
      };
    } catch (_error) {
      return {
        vulnerabilityId: action.id,
        success: false,
        action: action.type,
        details: `Remediation failed: ${_error instanceof Error ? _error.message : String(_error)}`,
        timestamp: new Date(),
      };
    }
  }

  /**
   * Execute replace action
   */
  private async executeReplaceAction(action: RemediationAction): Promise<void> {
    if (!action.target.file || !action.target.pattern || !action.replacement.content) {
      throw new Error("Replace action missing required fields");
    }

    const content = await fs.readFile(action.target.file, "utf-8");
    const updatedContent = content.replace(action.target.pattern, action.replacement.content);

    if (content === updatedContent) {
      throw new Error("No changes made - pattern not found");
    }

    await fs.writeFile(action.target.file, updatedContent, "utf-8");
  }

  /**
   * Execute insert action
   */
  private async executeInsertAction(action: RemediationAction): Promise<void> {
    if (!action.target.file || !action.replacement.content) {
      throw new Error("Insert action missing required fields");
    }

    const content = await fs.readFile(action.target.file, "utf-8");
    const lines = content.split("\n");

    const insertIndex = action.target.line ? Math.max(0, action.target.line - 1) : lines.length;
    lines.splice(insertIndex, 0, action.replacement.content);

    await fs.writeFile(action.target.file, lines.join("\n"), "utf-8");
  }

  /**
   * Execute delete action
   */
  private async executeDeleteAction(action: RemediationAction): Promise<void> {
    if (!action.target.file) {
      throw new Error("Delete action missing file");
    }

    if (action.target.line) {
      // Delete specific line
      const content = await fs.readFile(action.target.file, "utf-8");
      const lines = content.split("\n");

      if (action.target.line > 0 && action.target.line <= lines.length) {
        lines.splice(action.target.line - 1, 1);
        await fs.writeFile(action.target.file, lines.join("\n"), "utf-8");
      }
    } else if (action.target.pattern) {
      // Delete pattern matches
      const content = await fs.readFile(action.target.file, "utf-8");
      const updatedContent = content.replace(action.target.pattern, "");
      await fs.writeFile(action.target.file, updatedContent, "utf-8");
    } else {
      throw new Error("Delete action missing target specification");
    }
  }

  /**
   * Execute config action
   */
  private async executeConfigAction(action: RemediationAction): Promise<void> {
    if (!action.target.file || !action.replacement.config) {
      throw new Error("Config action missing required fields");
    }

    const content = await fs.readFile(action.target.file, "utf-8");
    const config = JSON.parse(content);

    // Merge configuration changes
    Object.assign(config, action.replacement.config);

    await fs.writeFile(action.target.file, JSON.stringify(config, null, 2), "utf-8");
  }

  /**
   * Execute file action
   */
  private async executeFileAction(action: RemediationAction): Promise<void> {
    if (!action.replacement.action) {
      throw new Error("File action missing action specification");
    }

    switch (action.replacement.action) {
      case "delete":
        if (action.target.file) {
          await fs.unlink(action.target.file);
        }
        break;

      case "create":
        if (action.target.file && action.replacement.content) {
          await fs.writeFile(action.target.file, action.replacement.content, "utf-8");
        }
        break;

      default:
        throw new Error(`Unknown file action: ${action.replacement.action}`);
    }
  }

  /**
   * Create backup of file
   */
  private async createBackup(filePath: string): Promise<string> {
    const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
    const backupPath = path.join(this.backupDirectory, `${path.basename(filePath)}.${timestamp}.backup`);

    const content = await fs.readFile(filePath, "utf-8");
    await fs.writeFile(backupPath, content, "utf-8");

    this.logger.debug("Created backup file", { backupPath });
    return backupPath;
  }

  /**
   * Ensure backup directory exists
   */
  private async ensureBackupDirectory(): Promise<void> {
    try {
      await fs.access(this.backupDirectory);
    } catch {
      await fs.mkdir(this.backupDirectory, { recursive: true });
      this.logger.info("Created backup directory", { directory: this.backupDirectory });
    }
  }

  /**
   * Validate remediation
   */
  private async validateRemediation(action: RemediationAction): Promise<boolean> {
    if (!action.validation?.test || !action.target.file) {
      return true; // No validation specified
    }

    try {
      // This is a simplified validation - in practice you'd run the test command
      const content = await fs.readFile(action.target.file, "utf-8");

      // Simple pattern-based validation
      if (action.target.pattern) {
        const matches = content.match(action.target.pattern);
        return (matches?.length || 0) === (action.validation.expected || 0);
      }

      return true;
    } catch (_error) {
      this.logger.warn("Validation failed for action", {
        actionId: action.id,
        _error: String(_error),
      });
      return false;
    }
  }

  /**
   * Simulate remediation actions for dry run
   */
  private async simulateRemediationActions(actions: RemediationAction[]): Promise<RemediationResult[]> {
    const results: RemediationResult[] = [];

    for (const action of actions) {
      results.push({
        vulnerabilityId: action.id,
        success: true,
        action: `${action.type} (simulated)`,
        details: `Would ${action.type} in ${action.target.file || "configuration"}`,
        timestamp: new Date(),
      });
    }

    return results;
  }

  /**
   * Get remediation history
   */
  getRemediationHistory(): RemediationResult[] {
    return [...this.remediationHistory];
  }

  /**
   * Rollback remediation using backup
   */
  async rollbackRemediation(backupPath: string, targetFile: string): Promise<void> {
    try {
      const backupContent = await fs.readFile(backupPath, "utf-8");
      await fs.writeFile(targetFile, backupContent, "utf-8");
      this.logger.info("Rolled back file from backup", {
        targetFile,
        backupPath,
      });
    } catch (_error) {
      throw new SecurityValidationError(
        `Rollback failed: ${_error instanceof Error ? _error.message : String(_error)}`,
      );
    }
  }

  /**
   * Clean up old backups
   */
  async cleanupBackups(maxAge: number = 7 * 24 * 60 * 60 * 1000): Promise<void> {
    try {
      const files = await fs.readdir(this.backupDirectory);
      const now = Date.now();

      for (const file of files) {
        const filePath = path.join(this.backupDirectory, file);
        const stats = await fs.stat(filePath);

        if (now - stats.mtime.getTime() > maxAge) {
          await fs.unlink(filePath);
          this.logger.debug("Cleaned up old backup", { file });
        }
      }
    } catch (_error) {
      this.logger.warn("Backup cleanup failed", { _error: String(_error) });
    }
  }
}
