import * as vscode from 'vscode';
import { Finding } from '../domain/model/Finding';
import { AiMetricsService } from '../infrastructure/services/AiMetricsService';

export function registerVulnerabilityCopilotCommands(context: vscode.ExtensionContext): void {
  
  // Command to fix vulnerabilities with Copilot
  const fixVulnerabilityCommand = vscode.commands.registerCommand(
    'devsecops.fixVulnerabilityWithCopilot',
    async (findingItem: any) => {
      if (!findingItem || !findingItem.finding) {
        void vscode.window.showErrorMessage('No vulnerability selected');
        return;
      }

      const finding: Finding = findingItem.finding;
      const sourceType = findingItem.sourceType || 'unknown';
      
      AiMetricsService.track('fix_with_copilot', finding, 'tree_context_menu');
      await executeVulnerabilityFixWithCopilot(finding, sourceType);
    }
  );

  // Command to explain vulnerabilities with Copilot
  const explainVulnerabilityCommand = vscode.commands.registerCommand(
    'devsecops.explainVulnerabilityWithCopilot',
    async (findingItem: any) => {
      if (!findingItem || !findingItem.finding) {
        void vscode.window.showErrorMessage('No vulnerability selected');
        return;
      }

      const finding: Finding = findingItem.finding;
      const sourceType = findingItem.sourceType || 'unknown';
      
      AiMetricsService.track('explain_with_copilot', finding, 'tree_context_menu');
      await executeVulnerabilityExplanationWithCopilot(finding, sourceType);
    }
  );

  // Command to generate dependency update solutions
  const generateDependencyUpdateCommand = vscode.commands.registerCommand(
    'devsecops.generateDependencyUpdate',
    async (findingItem: any) => {
      if (!findingItem || !findingItem.finding) {
        void vscode.window.showErrorMessage('No vulnerability selected');
        return;
      }

      const finding: Finding = findingItem.finding;
      const allFindings: Finding[] = findingItem.allFindings || [];

      AiMetricsService.track('generate_dependency_update', finding, 'tree_context_menu');
      await generateDependencyUpdateSolution(finding, allFindings);
    }
  );

  // Command to generate image update solutions
  const generateImageUpdateCommand = vscode.commands.registerCommand(
    'devsecops.generateImageUpdate',
    async (findingItem: any) => {
      if (!findingItem || !findingItem.finding) {
        void vscode.window.showErrorMessage('No vulnerability selected');
        return;
      }

      const finding: Finding = findingItem.finding;
      const allFindings: Finding[] = findingItem.allFindings || [];

      AiMetricsService.track('generate_image_update', finding, 'tree_context_menu');
      await generateImageUpdateSolution(finding, allFindings);
    }
  );

  context.subscriptions.push(
    fixVulnerabilityCommand,
    explainVulnerabilityCommand,
    generateDependencyUpdateCommand,
    generateImageUpdateCommand
  );
}

async function openCopilotChatWithPrompt(prompt: string): Promise<void> {
  const copilotChatExtension = vscode.extensions.getExtension('GitHub.copilot-chat');
  
  if (!copilotChatExtension || !copilotChatExtension.isActive) {
    void vscode.window.showInformationMessage('GitHub Copilot Chat extension is required for this feature');
    return;
  }

  // Check if this is an agent mode prompt (starts with @workspace)
  const isAgentMode = prompt.trim().startsWith('@workspace');

  try {
    // Try to open Copilot Chat without executing automatic commands
    const commands = [
      'workbench.action.chat.open',
      'workbench.panel.chat.view.copilot.focus',
      'workbench.view.extension.github-copilot-chat'
    ];
    
    let success = false;
    for (const command of commands) {
      try {
        await vscode.commands.executeCommand(command);
        success = true;
        break;
      } catch (error) {
        // Continue to next command
        continue;
      }
    }
    
    if (!success) {
      // Fallback: Just copy to clipboard and show message
      await vscode.env.clipboard.writeText(prompt);
      const message = isAgentMode 
        ? '⚠️ Copilot Agent prompt copied to clipboard. IMPORTANT: Switch to Agent mode in Copilot Chat before pasting to enable automatic code changes.'
        : 'Copilot prompt copied to clipboard. Please paste it in the Copilot Chat panel.';
      void vscode.window.showInformationMessage(
        message,
        'Open Copilot Chat'
      ).then(selection => {
        if (selection === 'Open Copilot Chat') {
          void vscode.commands.executeCommand('workbench.action.chat.open');
        }
      });
      return;
    }
    
    // Wait a bit for the panel to open, then paste and send the prompt automatically
    setTimeout(async () => {
      await vscode.env.clipboard.writeText(prompt);
      await vscode.commands.executeCommand('editor.action.clipboardPasteAction');
      
      // Show notification to review prompt
      const notificationMessage = isAgentMode
        ? '⚠️ IMPORTANT: Please switch to AGENT MODE in Copilot Chat (click the mode selector at the top) to enable automatic code changes. Then review the prompt and press Enter to send.'
        : 'Please review the prompt and then send it to chat';
      void vscode.window.showInformationMessage(
        notificationMessage,
        { modal: false }
      );
      
      // Try multiple commands to automatically send the message
      setTimeout(async () => {
        let sent = false;
        
        // First, try the most direct approach - simulate Enter key
        try {
          await vscode.commands.executeCommand('type', { text: '\n' });
          sent = true;
        } catch (error) {
          // If typing doesn't work, try chat-specific commands
          const sendCommands = [
            'workbench.action.chat.submit',
            'github.copilot.chat.submit',
            'chat.submit',
            'workbench.action.chat.acceptInput',
            'editor.action.submitInlineCommands'
          ];
          
          for (const sendCommand of sendCommands) {
            try {
              await vscode.commands.executeCommand(sendCommand);
              sent = true;
              break;
            } catch (error) {
              // Continue to next command
              continue;
            }
          }
        }
        
        if (!sent) {
          // If all else fails, notify user
          void vscode.window.showInformationMessage('Prompt pasted. Please review and press Enter to send.');
        }
      }, 500); // Increased delay for better reliability
    }, 1200); // Increased initial delay
    
  } catch (error) {
    void vscode.window.showErrorMessage(`Error using Copilot Chat: ${String(error)}`);
  }
}

async function executeVulnerabilityFixWithCopilot(finding: Finding, sourceType: string): Promise<void> {
  const prompt = generateFixPrompt(finding, sourceType);
  await openCopilotChatWithPrompt(prompt);
}

async function executeVulnerabilityExplanationWithCopilot(finding: Finding, sourceType: string): Promise<void> {
  const prompt = generateExplanationPrompt(finding, sourceType);
  await openCopilotChatWithPrompt(prompt);
}

async function generateDependencyUpdateSolution(finding: Finding, allFindings: Finding[] = []): Promise<void> {
  const prompt = generateDependencyUpdatePrompt(finding, allFindings);
  await openCopilotChatWithPrompt(prompt);
}

async function generateImageUpdateSolution(finding: Finding, allFindings: Finding[] = []): Promise<void> {
  const prompt = generateImageUpdatePrompt(finding, allFindings);
  await openCopilotChatWithPrompt(prompt);
}

function generateFixPrompt(finding: Finding, sourceType: string): string {
  const vulnerabilityId = finding.getId() || 'Unknown';
  const description = finding.getDescription() || 'No description available';
  const severity = finding.getSeverity() || 'Unknown';
  const where = finding.getWhere() || 'Unknown location';
  const module = finding.getModule() || 'Unknown';
  const tool = finding.getTool() || 'Unknown';

  let basePrompt = `Security Vulnerability Analysis and Solution

**Vulnerability Details:**
- ID: ${vulnerabilityId}
- Severity: ${severity}
- Module: ${module}
- Tool: ${tool}
- Location: ${where}
- Description: ${description}

**Scan Type:** ${sourceType}`;

  // Add specific context based on source type
  switch (sourceType) {
    case 'dependencies':
      basePrompt += `

**Dependency Vulnerability Context:**
This is a dependency vulnerability that may require updating package versions or changing dependency configurations.

If this is a Gradle project and a direct version bump in build.gradle doesn't resolve the vulnerability (common with transitive dependencies), use this alternative mitigation method:

${GRADLE_FORCED_VERSION_GUIDANCE}`;
      
      // Add impact paths if available
      if (finding.getAdditionalField('impact_paths_prompt')) {
        basePrompt += `\n\n**Impact Paths:**\n${(finding.getAdditionalField('impact_paths_prompt'))}`;
      }
      break;
      
    case 'image':
      basePrompt += `

**Container Image Vulnerability Context:**
This is a container image vulnerability that may require updating base images, packages, or container configurations.`;
      break;
      
    case 'iac':
      basePrompt += `

**Infrastructure as Code Vulnerability Context:**
This is an IaC vulnerability that may require updating configuration files, security policies, or infrastructure definitions.`;
      
      // Add validation rule for IaC findings
      const validationRule = finding.getValidationRuleCode();
      if (validationRule) {
        basePrompt += `\n\n**Validation Rule:** ${validationRule}`;
      }
      break;
  }

  basePrompt += `

**Request:**
Please provide a comprehensive solution that includes:
1. Root cause analysis
2. Specific remediation steps
3. Code examples or configuration changes
4. Security best practices
5. Prevention strategies for the future

Focus on practical, actionable solutions that can be implemented immediately.`;

  return basePrompt;
}

function generateExplanationPrompt(finding: Finding, sourceType: string): string {
  const vulnerabilityId = finding.getId() || 'Unknown';
  const description = finding.getDescription() || 'No description available';
  const severity = finding.getSeverity() || 'Unknown';
  const where = finding.getWhere() || 'Unknown location';

  let basePrompt = `Security Vulnerability Analysis

**Vulnerability Details:**
- ID: ${vulnerabilityId}
- Severity: ${severity}
- Location: ${where}
- Description: ${description}
- Scan Type: ${sourceType}`;

  // Add validation rule for IaC findings
  if (sourceType === 'iac') {
    const validationRule = finding.getValidationRuleCode();
    if (validationRule) {
      basePrompt += `\n- Validation Rule: ${validationRule}`;
    }
  }

  // Add impact paths for dependencies
  const impactPathsPrompt = finding.getAdditionalField('impact_paths_prompt');
  if (sourceType === 'dependencies' && impactPathsPrompt) {
    basePrompt += `\n\n**Impact Paths:**\n${impactPathsPrompt}`;
  }

  basePrompt += `

**Request:**
Please explain this security vulnerability in detail, including:
1. What this vulnerability means
2. Why it's dangerous (potential impact)
3. How attackers could exploit it
4. What systems or data could be affected
5. Industry standards or compliance implications
6. Similar vulnerabilities to be aware of

Provide a clear, educational explanation suitable for developers and security teams.`;

  return basePrompt;
}

const GRADLE_FORCED_VERSION_GUIDANCE = `**Gradle Alternative Mitigation (use only if this is a Gradle project):**

1. Check whether a \`dependencyMgmt.gradle\` file already exists in the project root. If it does NOT exist yet, create it there. This script forces specific versions for transitive dependencies that carry vulnerabilities/conflicts and/or excludes transitive dependencies that are unnecessary or problematic. If the file already exists, reuse it and just add/update the required rules instead of overwriting the rest of its content.

2. Its content should look like this:
\`\`\`gradle
// Applied lazily to every configuration of the project
configurations.configureEach {

    /**
     * -------------------------------
     *  Force dependency versions
     * -------------------------------
     */
    resolutionStrategy.eachDependency { DependencyResolveDetails details ->

        // Example 1: force by group (applies to every dependency of that group)
        if (details.requested.group == 'io.example') {
            details.useVersion("\${exampleVersion}")
        }

        // Example 2: force by group and name (applies only to a specific dependency)
        if (details.requested.group == 'com.example' && details.requested.name == 'example-lib') {
            details.useVersion("\${exampleVersion}")
        }
    }
}
\`\`\`

3. Apply the script in both the root project and the specific module (check first that the line isn't already present to avoid duplicates):
   - In the root \`build.gradle\`, inside the \`allprojects\` block, add: \`apply from: "\${rootDir}/dependencyMgmt.gradle"\`
   - Additionally (recommended in practice, even though \`allprojects\` should be enough), add the same line as the **first line** of \`/applications/app-service/build.gradle\` (or the equivalent app module) so the configuration is reliably applied at every level.`;

function generateDependencyUpdatePrompt(finding: Finding, allFindings: Finding[] = []): string {
  // Scope to dependency findings from this same scan; fall back to the single selected finding
  const dependencyFindings = allFindings.filter((f) => f.getModule() === 'engine_dependencies');
  const findingsToFix = dependencyFindings.length > 0 ? dependencyFindings : [finding];
  const count = findingsToFix.length;

  // Keep each entry to the essentials (CVE + fixed version) so the prompt stays short even with many findings
  const vulnerabilityList = findingsToFix
    .map((f) => {
      const cve = f.getId() || 'Unknown';
      const packageName = f.getAdditionalField('package_name') || 'Unknown package';
      const fixedVersion = f.getAdditionalField('fixed_version') || 'No fix available';
      return `- ${cve} | ${packageName} → fix: ${fixedVersion}`;
    })
    .join('\n');

  return `Dependency Update Strategy - Fix All Vulnerabilities in This Scan

**Scan Summary:** ${count} dependency vulnerabilit${count === 1 ? 'y was' : 'ies were'} found in this scan and must ALL be remediated together.

**Vulnerabilities (CVE | Package → Fixed Version):**
${vulnerabilityList}

**Request:**
Your goal is to resolve **all ${count} vulnerabilit${count === 1 ? 'y' : 'ies'} listed above** with a single, consolidated update plan for this project (not just the vulnerability that was clicked). Please provide:

1. **Consolidated Update Plan:**
   - The exact target version to use for each package (if a package appears more than once, use the highest fixed version required)
   - Group the updates by package manager/manifest file (package.json, requirements.txt, pom.xml, go.mod, etc.)

2. **Commands:**
   - Ready-to-run package manager commands (npm/yarn/pnpm/pip/maven/gradle/go) that apply all the updates at once
   - Lock file regeneration steps

3. **Compatibility & Risk:**
   - Potential breaking changes introduced by these specific updates
   - Any conflicts between the required fixed versions of the listed packages
   - Testing steps to validate the project after updating everything together

4. **If a direct update isn't possible for any package:**
   - Suggest safe alternatives or temporary mitigations for that specific CVE

5. **If this is a Gradle project:**
   - Apply the Gradle Alternative Mitigation described below instead

${GRADLE_FORCED_VERSION_GUIDANCE}

Provide one unified, actionable solution that addresses every vulnerability in the list above.`;
}

function generateImageUpdatePrompt(finding: Finding, allFindings: Finding[] = []): string {
  // Scope to image findings from this same scan; fall back to the single selected finding
  const imageFindings = allFindings.filter((f) => f.getModule() === 'engine_container' || f.getModule() === 'engine_image');
  const findingsToFix = imageFindings.length > 0 ? imageFindings : [finding];
  const count = findingsToFix.length;

  // Keep each entry to the essentials (CVE + fixed version) so the prompt stays short even with many findings
  const vulnerabilityList = findingsToFix
    .map((f) => {
      const cve = f.getId() || 'Unknown';
      const packageName = f.getAdditionalField('package_name') || 'Unknown package';
      const fixedVersion = f.getAdditionalField('fixed_version') || 'No fix available';
      return `- ${cve} | ${packageName} → fix: ${fixedVersion}`;
    })
    .join('\n');

  return `Container Image Update Strategy - Fix All Vulnerabilities in This Scan

**Scan Summary:** ${count} image vulnerabilit${count === 1 ? 'y was' : 'ies were'} found in this scan and must ALL be remediated together.

**Vulnerabilities (CVE | Package → Fixed Version):**
${vulnerabilityList}

**Request:**
Your goal is to resolve **all ${count} vulnerabilit${count === 1 ? 'y' : 'ies'} listed above** with a single, consolidated update plan for this container image (not just the vulnerability that was clicked). Please provide:

1. **Consolidated Update Plan:**
   - The exact target version to use for each package (if a package appears more than once, use the highest fixed version required)
   - Whether updating the base image (e.g. a newer tag/digest) resolves multiple vulnerabilities at once, versus patching individual OS packages

2. **Commands/Changes:**
   - Ready-to-apply Dockerfile changes (base image tag, RUN commands with package manager updates: apt/yum/apk/etc.)

3. **Compatibility & Risk:**
   - Potential breaking changes introduced by these specific updates
   - Any conflicts between the required fixed versions of the listed packages
   - Testing steps to validate the image still works after updating everything together

4. **If a direct update isn't possible for any package:**
   - Suggest safe alternatives or temporary mitigations for that specific CVE

Provide one unified, actionable solution that addresses every vulnerability in the list above.`;
}
