/* eslint-disable import/extensions */
import { ArrowPathIcon } from '@heroicons/react/24/outline';
import { Cvss3P1 } from 'ae-cvss-calculator';
import { t } from 'i18next';
import React, { useEffect, useState } from 'react';
import { toast } from 'sonner';

import PrimaryButton from '@/components/button/PrimaryButton';
import { postDescriptionCVSS } from '@/services/vulnerabilities';

import MetricGroup from './metricGroup';
import ScoreBox from './scoreBox';

type CVSSProp = {
  handleCvssChange: (value: string) => void;
  cvssStringInitial?: string;
  handleCvssRecomendation: () => string;
};

const generateCVSSVector = (
  AV: string,
  AC: string,
  PR: string,
  UI: string,
  S: string,
  C: string,
  I: string,
  A: string,
): string => {
  const cvssComponents = {
    AV,
    AC,
    PR,
    UI,
    S,
    C,
    I,
    A,
  };

  const componentMapping: Record<string, string> = {
    AV: 'AV',
    AC: 'AC',
    PR: 'PR',
    UI: 'UI',
    S: 'S',
    C: 'C',
    I: 'I',
    A: 'A',
  };

  const valueMapping: Record<string, string> = {
    Network: 'N',
    'Adjacent Network': 'A',
    Local: 'L',
    Physical: 'P',
    Low: 'L',
    High: 'H',
    None: 'N',
    Required: 'R',
    Unchanged: 'U',
    Changed: 'C',
  };

  let vectorString = 'CVSS:3.1/';

  Object.entries(cvssComponents).forEach(([key, value], index) => {
    if (value) {
      if (index > 0) {
        vectorString += '/';
      }
      vectorString += `${componentMapping[key]}:${valueMapping[value]}`;
    }
  });

  return vectorString;
};

const CVSSCalculator: React.FC<CVSSProp> = ({
  cvssStringInitial,
  handleCvssChange,
  handleCvssRecomendation,
}) => {
  const [AV, setAV] = useState('');
  const [AC, setAC] = useState('');
  const [PR, setPR] = useState('');
  const [UI, setUI] = useState('');
  const [S, setS] = useState('');
  const [C, setC] = useState('');
  const [I, setI] = useState('');
  const [A, setA] = useState('');

  const [recommendedAV, setRecomendedAV] = useState('');
  const [recommendedAC, setRecomendedAC] = useState('');
  const [recommendedPR, setRecomendedPR] = useState('');
  const [recommendedUI, setRecomendedUI] = useState('');
  const [recommendedS, setRecomendedS] = useState('');
  const [recommendedC, setRecomendedC] = useState('');
  const [recommendedI, setRecomendedI] = useState('');
  const [recommendedA, setRecomendedA] = useState('');

  const [changed, setChanged] = useState(false);

  const noneLowHighOptions = [
    { label: t('cvss.none'), value: 'None' },
    { label: t('cvss.low'), value: 'Low' },
    { label: t('cvss.high'), value: 'High' },
  ];

  const avOptions = [
    { label: t('cvss.network'), value: 'Network' },
    { label: t('cvss.adjacentNetwork'), value: 'Adjacent Network' },
    { label: t('cvss.local'), value: 'Local' },
    { label: t('cvss.physical'), value: 'Physical' },
  ];

  const acOptions = [
    { label: t('cvss.low'), value: 'Low' },
    { label: t('cvss.high'), value: 'High' },
  ];

  const uiOptions = [
    { label: t('cvss.none'), value: 'None' },
    { label: t('cvss.required'), value: 'Required' },
  ];

  const scopeOptions = [
    { label: t('cvss.unchanged'), value: 'Unchanged' },
    { label: t('cvss.changed'), value: 'Changed' },
  ];

  const valueMappings: Record<string, Record<string, string>> = {
    AV: { N: 'Network', A: 'Adjacent Network', L: 'Local', P: 'Physical' },
    AC: { H: 'High', L: 'Low' },
    PR: { N: 'None', L: 'Low', H: 'High' },
    UI: { N: 'None', R: 'Required' },
    S: { U: 'Unchanged', C: 'Changed' },
    C: { H: 'High', L: 'Low', N: 'None' },
    I: { H: 'High', L: 'Low', N: 'None' },
    A: { H: 'High', L: 'Low', N: 'None' },
  };

  const parseCVSSVector = (vector: string) => {
    const parts = vector.split('/').slice(1);
    parts.forEach(part => {
      if (part === '') {
        setChanged(true);
        return;
      }
      const [key, value] = part.split(':');
      const mappedValue = valueMappings[key][value] || '';
      switch (key) {
        case 'AV':
          setAV(mappedValue);
          break;

        case 'AC':
          setAC(mappedValue);
          break;

        case 'PR':
          setPR(mappedValue);
          break;

        case 'UI':
          setUI(mappedValue);
          break;

        case 'S':
          setS(mappedValue);
          break;

        case 'C':
          setC(mappedValue);
          break;

        case 'I':
          setI(mappedValue);
          break;

        case 'A':
          setA(mappedValue);
          break;

        default:
          break;
      }
    });
    setChanged(true);
  };

  const parseCVSSRecommendationVector = (vector: string) => {
    const parts = vector.split('/').slice(1);
    parts.forEach(part => {
      const [key, value] = part.split(':');
      const mappedValue = valueMappings[key][value] || '';
      switch (key) {
        case 'AV':
          setRecomendedAV(mappedValue);
          break;

        case 'AC':
          setRecomendedAC(mappedValue);
          break;

        case 'PR':
          setRecomendedPR(mappedValue);
          break;

        case 'UI':
          setRecomendedUI(mappedValue);
          break;

        case 'S':
          setRecomendedS(mappedValue);
          break;

        case 'C':
          setRecomendedC(mappedValue);
          break;

        case 'I':
          setRecomendedI(mappedValue);
          break;

        case 'A':
          setRecomendedA(mappedValue);
          break;

        default:
          break;
      }
    });
    setChanged(true);
  };

  const [currentScore, setCurrentScore] = useState<number>(0);
  const [isComplete, setIsComplete] = useState<boolean>(false);

  useEffect(() => {
    if (cvssStringInitial) {
      parseCVSSVector(cvssStringInitial);
    } else {
      setChanged(true);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  useEffect(() => {
    if (changed) {
      const cvssVector = generateCVSSVector(AV, AC, PR, UI, S, C, I, A);
      handleCvssChange(cvssVector);
      if (cvssVector.length === 44) {
        const cvss3 = new Cvss3P1(cvssVector);
        setIsComplete(true);
        setCurrentScore(cvss3.calculateExactOverallScore());
      } else {
        setIsComplete(false);
      }
    }
  }, [AV, AC, PR, UI, S, C, I, A, changed, handleCvssChange]);

  const cleanRecommendations = () => {
    setRecomendedAV('');
    setRecomendedAC('');
    setRecomendedPR('');
    setRecomendedUI('');
    setRecomendedS('');
    setRecomendedC('');
    setRecomendedI('');
    setRecomendedA('');
  };

  const [isLoadingRecommendation, setIsLoadingRecommendation] = useState(false);

  const recommendCVSS = async () => {
    const vulnDescription = handleCvssRecomendation();
    if (vulnDescription === '') {
      return;
    }
    setIsLoadingRecommendation(true);

    try {
      const cvssString = (await postDescriptionCVSS({ vuln: vulnDescription }))
        .result;
      parseCVSSRecommendationVector(cvssString);
    } catch (err) {
      toast.error(t('errorRecommendingCVSS'));
    } finally {
      setIsLoadingRecommendation(false);
    }
  };

  return (
    <div className="relative w-full p-6 bg-slate-700 border border-gray-200 rounded-lg">
      <ScoreBox isComplete={isComplete} score={currentScore} />
      <h2 className="text-xl font-semibold text-center mb-8">
        {t('cvss.title')}
      </h2>
      <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
        <div>
          <MetricGroup
            highlightedOption={recommendedAV}
            label={t('cvss.attackVector')}
            onSelect={setAV}
            options={avOptions}
            selectedOption={AV}
          />
          <MetricGroup
            highlightedOption={recommendedAC}
            label={t('cvss.attackComplexity')}
            onSelect={setAC}
            options={acOptions}
            selectedOption={AC}
          />
          <MetricGroup
            highlightedOption={recommendedPR}
            label={t('cvss.privilegesRequired')}
            onSelect={setPR}
            options={noneLowHighOptions}
            selectedOption={PR}
          />
          <MetricGroup
            highlightedOption={recommendedUI}
            label={t('cvss.userInteraction')}
            onSelect={setUI}
            options={uiOptions}
            selectedOption={UI}
          />
        </div>
        <div>
          <MetricGroup
            highlightedOption={recommendedS}
            label={t('cvss.scope')}
            onSelect={setS}
            options={scopeOptions}
            selectedOption={S}
          />
          <MetricGroup
            highlightedOption={recommendedC}
            label={t('cvss.confidentialityImpact')}
            onSelect={setC}
            options={noneLowHighOptions}
            selectedOption={C}
          />
          <MetricGroup
            highlightedOption={recommendedI}
            label={t('cvss.integrityImpact')}
            onSelect={setI}
            options={noneLowHighOptions}
            selectedOption={I}
          />
          <MetricGroup
            highlightedOption={recommendedA}
            label={t('cvss.availabilityImpact')}
            onSelect={setA}
            options={noneLowHighOptions}
            selectedOption={A}
          />
        </div>
      </div>
      <hr />
      <div className="flex justify-between pt-4">
        <div className="flex items-center space-x-4">
          <PrimaryButton onClick={recommendCVSS}>
            {t('recommendCVSS')}
          </PrimaryButton>
          {isLoadingRecommendation ? (
            <ArrowPathIcon className="h-8 w-8 animate-spin text-blue-500" />
          ) : null}
        </div>
        <PrimaryButton onClick={cleanRecommendations}>
          {t('cleanRecommendations')}
        </PrimaryButton>
      </div>
    </div>
  );
};

export default CVSSCalculator;
