// Import libs
import clear from 'clear';
import fs from 'fs';
import path from 'path';
import Papa from "papaparse";

// Import helpers
import print from '../helpers/print';
import prompt from '../helpers/prompt';
import showChooser from '../helpers/showChooser';
import scrapeCVE from '../helpers/scrapeCVE';

// Import shared types
import WizReportItem from '../types/WizReportItem';
import WizCSVRow from '../types/WizCSVRow';
import WizSeverity from '../types/WizSeverity';

// Get project directory
const currDir = (process.env.PWD || process.env.CWD) ?? __dirname;

/**
 * Review current CVEs (from Wiz CSV) and decide how to handle each one
 * @author Allison Zhang
 * @author Gabe Abrams
 */
const reviewCVEs = async () => {
  // Clear the screen and show title
  clear();
  print.title('Import Wiz CSV');
  console.log('\nFind the desired Wiz Report CSV in your directory and drag/drop it into this window.\n');

  // Get the file path
  const wizReportPath = prompt('> ', true).replace(/['"]/g, '').trim();

  // Read in the trivy results
  if (!fs.existsSync(wizReportPath)) {
    clear();
    print.title('Error [File Not Found]');
    console.error(`\nFile not found: ${wizReportPath}\n`);
    print.enterToContinue();
    return;
  }

  // Convert to object
  let wizResults;
  try {
    wizResults = Papa.parse<WizCSVRow>(fs.readFileSync(wizReportPath, 'utf8'), {
      header: true,
      skipEmptyLines: true,
      delimitersToGuess: [',', ';', '\t'],
      dynamicTyping: true,
    });
  } catch (error) {
    clear();
    print.title('Error [CSV Invalid]');
    console.error(`\nError parsing CSV file: ${error}\n`);
    print.enterToContinue();
    return;
  }

  // Make sure that the schema is correct
  if (!wizResults || !wizResults.data) {
    clear();
    print.title('Error [Invalid Wiz Report]');
    console.error('\nWiz results should use \',\', \';\', and \'\\t\'. Please provide a Wiz scan CSV file.\n');
    print.enterToContinue();
    return;
  }

  let wizRows: WizReportItem[] = [];
  for (const row of wizResults.data) {
    let wizRow: WizReportItem = {
      wizURL: row.WizURL,
      cve: row.Name,
      severity: WizSeverity.Low, // Default to low, will update based on value in CSV
      remediation: row.Remediation,
      locationPath: row.LocationPath,
      package: row.DetailedName,
      version: row.Version,
      fixedVersion: row.FixedVersion,
      advisoryLink: row.Link,
      awsAccount: row.SubscriptionName,
    };
    switch (row.Severity) {
      case 'Critical':
        wizRow.severity = WizSeverity.Critical;
        break;
      case 'High':
        wizRow.severity = WizSeverity.High;
        break;
      case 'Medium':
        wizRow.severity = WizSeverity.Medium;
        break;
      case 'Low':
        wizRow.severity = WizSeverity.Low;
        break;
      default:
        wizRow.severity = WizSeverity.Low;
    };
    wizRows.push(wizRow);
  };

  // List of explanations for reviewed CVEs
  const reviewedCVEs: { 
    cve: string,
    title: string,
    explanation: string,
  }[] = [];

  // If no vulnerabilities, exit
  if (!wizRows || wizRows.length === 0) {
    clear();
    print.title('Success!');
    console.log('\nNo vulnerabilities found! 🎉\n');
    print.enterToContinue();
    return;
  } else {
    // Loop through vulnerabilities
    for (let i: number  = 0; i < wizRows.length; i++) {
      clear();
      let information: { [key: string]: string }
      try {
        information = await scrapeCVE(wizRows[i].advisoryLink);
      } catch (error) {
        console.error(`Error scraping CVE information: ${error}`);
        information = { title: 'Unknown', description: 'Unknown' };
      }
      print.title(`Severity: ${wizRows[i].severity} | Vulnerability ${i+1} of ${wizRows.length}`);
      console.log(`CVE: ${wizRows[i].cve}\n`);
      console.log(`Title: ${information.title}\n`);
      console.log(`Description: ${information.description}\n`);
      console.log(`Version (Installed): ${wizRows[i].version}`);
      console.log(`Version (Fixed): ${wizRows[i].fixedVersion}`);
      console.log(`Primary URL: ${wizRows[i].wizURL}`);
      console.log(`Advisory Link: ${wizRows[i].advisoryLink}\n`);
      console.log(`Remediation:\n${wizRows[i].remediation}\n`);
      console.log(`Location Path: ${wizRows[i].locationPath}\n`);
      console.log(`AWS Account: ${wizRows[i].awsAccount}\n`);

      // Ask user what to do with this vulnerability
      const vulnerabilityOptions = showChooser({
        question: 'Review Vulnerability:',
        options: [
          {
            description: 'Reviewed (Not a problem)',
            tag: 'R',
          },
          {
            description: 'Snooze (Will fix later)',
            tag: 'S',
          },
        ],
        dontClear: true,
      })

      // If reviewing, ask for explanation, then add to reviewed CVEs list
      if (vulnerabilityOptions.tag === 'R') {
        console.log('\nPlease explain why this vulnerability can be ignored.');
        console.log('Explanation: ');
        let explanation = prompt('', true).trim();
        while (!explanation || explanation.length === 0) {
          console.log('Please provide an explanation: ');
          explanation = prompt('', true);
        }
        reviewedCVEs.push({
          cve: wizRows[i].cve,
          title: information.title,
          explanation: explanation,
        });
      }

      // If snoozing, just continue
    }
  }

  // Write to .reviewed-cves file
  const reviewedCVEsPath = path.join(currDir, '.reviewed-cves');

  // Append all reviewed CVEs to the file
  let reviewedCVEsContent: string = '';
  reviewedCVEs.forEach((cve) => {
    reviewedCVEsContent += `\n# ${cve.title}\n# Note: ${cve.explanation}\n`;
  });
  try {
    fs.appendFileSync(reviewedCVEsPath, reviewedCVEsContent);
  } catch (error) {
    print.title('Error [File Append]');
    console.error(`\nError appending reviewed CVEs to ${reviewedCVEsPath}: ${error}\n`);
    print.enterToContinue();
    return;
  }
};

export default reviewCVEs;
