import { useSuspenseQuery } from '@suspensive/react-query';
import { capitalize, keys, upperFirst } from 'lodash-es';
import { Suspense, useCallback, useEffect, useMemo, useState } from 'react';
import {
  ActionFunctionArgs,
  generatePath,
  Outlet,
  useFetcher,
  useParams,
  useSearchParams,
} from 'react-router-dom';
import { toast } from 'sonner';
import { cn } from 'tailwind-preset';
import {
  Badge,
  Breadcrumb,
  BreadcrumbLink,
  Button,
  Card,
  Checkbox,
  CircleSpinner,
  Combobox,
  ComboboxOption,
  createColumnHelper,
  Dropdown,
  DropdownItem,
  DropdownSeparator,
  getRowSelectionColumn,
  Modal,
  RowSelectionState,
  SortingState,
  Table,
  TableNoDataElement,
  TableSkeleton,
} from 'ui-components';

import { getScanResultsApiClient } from '@/api/api';
import {
  ModelScanInfo,
  ModelScanResultsMaskRequestMaskActionEnum,
  ModelVulnerability,
  UtilsReportFiltersNodeTypeEnum,
  UtilsReportFiltersScanTypeEnum,
} from '@/api/generated';
import { DFLink } from '@/components/DFLink';
import { FilterBadge } from '@/components/filters/FilterBadge';
import { CompareScanInputModal } from '@/components/forms/CompareScanInputModal';
import { BalanceLineIcon } from '@/components/icons/common/BalanceLine';
import { BellLineIcon } from '@/components/icons/common/BellLine';
import { CaretDown } from '@/components/icons/common/CaretDown';
import { ClockLineIcon } from '@/components/icons/common/ClockLine';
import { DownloadLineIcon } from '@/components/icons/common/DownloadLine';
import { EllipsisIcon } from '@/components/icons/common/Ellipsis';
import { ErrorStandardLineIcon } from '@/components/icons/common/ErrorStandardLine';
import { EyeHideSolid } from '@/components/icons/common/EyeHideSolid';
import { EyeSolidIcon } from '@/components/icons/common/EyeSolid';
import { FileLineIcon } from '@/components/icons/common/FileLine';
import { FilterIcon } from '@/components/icons/common/Filter';
import { PopOutIcon } from '@/components/icons/common/PopOut';
import { TimesIcon } from '@/components/icons/common/Times';
import { TrashLineIcon } from '@/components/icons/common/TrashLine';
import { StopScanForm } from '@/components/scan-configure-forms/StopScanForm';
import { HistoryControlsSkeleton } from '@/components/scan-history/HistoryControlsSkeleton';
import { ScanHistoryDropdown } from '@/components/scan-history/ScanHistoryDropdown';
import { ScanStatusBadge } from '@/components/ScanStatusBadge';
import {
  ScanStatusDeletePending,
  ScanStatusInError,
  ScanStatusInProgress,
  ScanStatusNoData,
  ScanStatusStopped,
  ScanStatusStopping,
} from '@/components/ScanStatusMessage';
import { SeverityBadgeIcon } from '@/components/SeverityBadge';
import { VulnerabilityIcon } from '@/components/sideNavigation/icons/Vulnerability';
import { TruncatedText } from '@/components/TruncatedText';
import { getSeverityColorMap } from '@/constants/charts';
import { BreadcrumbWrapper } from '@/features/common/BreadcrumbWrapper';
import { useDownloadScan } from '@/features/common/data-component/downloadScanAction';
import { FilterWrapper } from '@/features/common/FilterWrapper';
import { SelectNotificationChannel } from '@/features/integrations/components/SelectNotificationChannel';
import { SuccessModalContent } from '@/features/settings/components/SuccessModalContent';
import { VulnerabilityThreatGraph } from '@/features/vulnerabilities/components/landing/TopAttackPaths';
import { SbomModal } from '@/features/vulnerabilities/components/SBOMModal';
import { SearchableCVEList } from '@/features/vulnerabilities/components/ScanResults/SearchableCVEList';
import { VulnerabilitiesCompare } from '@/features/vulnerabilities/components/ScanResults/VulnerabilitiesCompare';
import { VulnerabilityScanResultsPieChart } from '@/features/vulnerabilities/components/ScanResults/VulnerabilityScanResultsPieChart';
import { invalidateAllQueries, queries } from '@/queries';
import { queryClient } from '@/queries/client';
import { useTheme } from '@/theme/ThemeContext';
import {
  isCriticalSeverity,
  isHighSeverity,
  isLowSeverity,
  isMediumSeverity,
  isUnknownSeverity,
  ScanTypeEnum,
  VulnerabilitySeverityType,
} from '@/types/common';
import { get403Message, getResponseErrors } from '@/utils/403';
import { apiWrapper } from '@/utils/api';
import {
  getMaskedUnmaskedPrettyName,
  getSeverityPrettyName,
  SeverityEnum,
  SeverityEnumList,
  SeverityValueType,
} from '@/utils/enum';
import { abbreviateNumber } from '@/utils/number';
import {
  isScanComplete,
  isScanDeletePending,
  isScanFailed,
  isScanInProgress,
  isScanStopped,
  isScanStopping,
} from '@/utils/scan';
import {
  getOrderFromSearchParams,
  getPageFromSearchParams,
  useSortingState,
} from '@/utils/table';
import { usePageNavigation } from '@/utils/usePageNavigation';

enum ActionEnumType {
  MASK = 'mask',
  UNMASK = 'unmask',
  DELETE = 'delete',
  NOTIFY = 'notify',
  DELETE_SCAN = 'delete_scan',
}

const DEFAULT_PAGE_SIZE = 10;

type ActionData =
  | {
      action: Exclude<ActionEnumType, ActionEnumType.DELETE_SCAN>;
      success: boolean;
      message?: string;
    }
  | {
      action: ActionEnumType.DELETE_SCAN;
      success: boolean;
      nextScanId: string | null;
      message?: string;
    };

const action = async ({
  params: { scanId = '' },
  request,
}: ActionFunctionArgs): Promise<ActionData> => {
  const formData = await request.formData();
  const nodeIds = (formData.getAll('nodeIds[]') ?? []) as string[];
  const actionType = formData.get('actionType');
  const _scanId = scanId;
  const maskAction = formData.get('maskAction');
  if (!_scanId) {
    throw new Error('Scan ID is required');
  }

  if (actionType === ActionEnumType.DELETE || actionType === ActionEnumType.NOTIFY) {
    const notifyIndividual = formData.get('notifyIndividual')?.toString();
    const apiFunction =
      actionType === ActionEnumType.DELETE
        ? getScanResultsApiClient().deleteScanResult
        : getScanResultsApiClient().notifyScanResult;

    const apiFunctionApi = apiWrapper({
      fn: apiFunction,
    });
    const integrationIds = formData.getAll('integrationIds[]') as Array<string>;

    const result = await apiFunctionApi({
      modelScanResultsActionRequest: {
        result_ids: [...nodeIds],
        scan_id: _scanId,
        scan_type: ScanTypeEnum.VulnerabilityScan,
        notify_individual: notifyIndividual === 'on',
        integration_ids:
          actionType === ActionEnumType.NOTIFY
            ? integrationIds.map((id) => Number(id))
            : null,
      },
    });

    if (!result.ok) {
      if (result.error.response.status === 400 || result.error.response.status === 409) {
        const { message } = await getResponseErrors(result.error);
        return {
          action: actionType,
          success: false,
          message,
        };
      } else if (result.error.response.status === 403) {
        const message = await get403Message(result.error);
        if (actionType === ActionEnumType.DELETE) {
          return {
            action: actionType,
            success: false,
            message,
          };
        } else if (actionType === ActionEnumType.NOTIFY) {
          return {
            action: actionType,
            success: false,
            message,
          };
        }
      }
      throw result.error;
    }
    invalidateAllQueries();
    if (actionType === ActionEnumType.NOTIFY) toast.success('Notified successfully');
    return {
      action: actionType,
      success: true,
    };
  } else if (actionType === ActionEnumType.MASK || actionType === ActionEnumType.UNMASK) {
    const apiFunction =
      actionType === ActionEnumType.MASK
        ? getScanResultsApiClient().maskScanResult
        : getScanResultsApiClient().unmaskScanResult;
    const apiFunctionApi = apiWrapper({
      fn: apiFunction,
    });

    const result = await apiFunctionApi({
      modelScanResultsMaskRequest: {
        mask_action: maskAction as ModelScanResultsMaskRequestMaskActionEnum,
        result_ids: [...nodeIds],
        scan_id: _scanId,
        scan_type: ScanTypeEnum.VulnerabilityScan,
      },
    });
    if (!result.ok) {
      if (result.error.response.status === 400 || result.error.response.status === 409) {
        const { message } = await getResponseErrors(result.error);
        return {
          action: actionType,
          success: false,
          message,
        };
      } else if (result.error.response.status === 403) {
        const message = await get403Message(result.error);
        if (actionType === ActionEnumType.MASK) {
          toast.error(message);
          return {
            action: actionType,
            success: false,
            message,
          };
        } else if (actionType === ActionEnumType.UNMASK) {
          toast.error(message);
          return {
            action: actionType,
            success: false,
            message,
          };
        }
      }
      throw result.error;
    }
    invalidateAllQueries();
    toast.success(
      `${actionType === ActionEnumType.MASK ? 'Masked' : 'Unmasked'} successfully`,
    );
    return {
      action: actionType,
      success: true,
    };
  } else if (actionType === ActionEnumType.DELETE_SCAN) {
    const deleteScan = apiWrapper({
      fn: getScanResultsApiClient().deleteScanResultsForScanID,
    });

    const result = await deleteScan({
      scanId: formData.get('scanId') as string,
      scanType: ScanTypeEnum.VulnerabilityScan,
    });

    if (!result.ok) {
      if (result.error.response.status === 403) {
        const message = await get403Message(result.error);
        return {
          action: actionType,
          success: false,
          nextScanId: null,
          message,
        };
      }
      throw result.error;
    }
    await queryClient.resetQueries({
      queryKey: queries.common.scanHistories._def,
    });

    const historyData = await queryClient.ensureQueryData({
      ...queries.common.scanHistories({
        nodeId: formData.get('nodeId') as string,
        nodeType: formData.get('nodeType') as string,
        scanType: ScanTypeEnum.VulnerabilityScan,
        size: 1,
      }),
    });

    let nextScanId: string | null = null;
    if ('data' in historyData && historyData.data.length) {
      nextScanId = historyData.data[0].scanId;
    }

    return {
      action: actionType,
      nextScanId,
      success: true,
    };
  } else {
    throw new Error('Unknown action type');
  }
};

const DeleteConfirmationModal = ({
  showDialog,
  ids,
  setShowDialog,
  onDeleteSuccess,
}: {
  showDialog: boolean;
  ids: string[];
  setShowDialog: React.Dispatch<React.SetStateAction<boolean>>;
  onDeleteSuccess: () => void;
}) => {
  const fetcher = useFetcher<ActionData>();

  const onDeleteAction = useCallback(
    (actionType: string) => {
      const formData = new FormData();
      formData.append('actionType', actionType);
      ids.forEach((item) => formData.append('nodeIds[]', item));
      fetcher.submit(formData, {
        method: 'post',
      });
    },
    [ids, fetcher],
  );

  useEffect(() => {
    if (
      fetcher.state === 'idle' &&
      fetcher.data?.success &&
      fetcher.data.action === ActionEnumType.DELETE
    ) {
      onDeleteSuccess();
    }
  }, [fetcher]);

  return (
    <Modal
      size="s"
      open={showDialog}
      onOpenChange={() => setShowDialog(false)}
      title={
        !fetcher.data?.success ? (
          <div className="flex gap-3 items-center text-status-error">
            <span className="h-6 w-6 shrink-0">
              <ErrorStandardLineIcon />
            </span>
            Delete {ids.length > 1 ? 'vulnerabilities' : 'vulnerability'}
          </div>
        ) : undefined
      }
      footer={
        !fetcher.data?.success ? (
          <div className={'flex gap-x-4 justify-end'}>
            <Button
              size="md"
              onClick={() => setShowDialog(false)}
              type="button"
              variant="outline"
            >
              Cancel
            </Button>
            <Button
              size="md"
              color="error"
              loading={fetcher.state === 'submitting'}
              disabled={fetcher.state === 'submitting'}
              onClick={(e) => {
                e.preventDefault();
                onDeleteAction(ActionEnumType.DELETE);
              }}
            >
              Delete
            </Button>
          </div>
        ) : undefined
      }
    >
      {!fetcher.data?.success ? (
        <div className="grid">
          <span>The selected vulnerabilities will be deleted.</span>
          <br />
          <span>Are you sure you want to delete?</span>
          {fetcher.data?.message && (
            <p className="mt-2 text-p7 text-status-error">{fetcher.data?.message}</p>
          )}
        </div>
      ) : (
        <SuccessModalContent text="Deleted successfully!" />
      )}
    </Modal>
  );
};

const DeleteScanConfirmationModal = ({
  open,
  onOpenChange,
  scanId,
  nodeId,
  nodeType,
}: {
  scanId: string;
  nodeId: string;
  nodeType: string;
  open: boolean;
  onOpenChange: (
    open: boolean,
    status: {
      success: boolean;
      nextScanId: string | null;
    },
  ) => void;
}) => {
  const [deleteStatus, setDeleteStatus] = useState<{
    success: boolean;
    nextScanId: string | null;
  }>({
    success: false,
    nextScanId: null,
  });
  const fetcher = useFetcher<ActionData>();
  const onDeleteScan = () => {
    const formData = new FormData();
    formData.append('actionType', ActionEnumType.DELETE_SCAN);
    formData.append('scanId', scanId);
    formData.append('nodeId', nodeId);
    formData.append('nodeType', nodeType);
    fetcher.submit(formData, {
      method: 'post',
    });
  };

  useEffect(() => {
    if (fetcher.data?.success) {
      if (fetcher.data.action === ActionEnumType.DELETE_SCAN) {
        setDeleteStatus({
          success: true,
          nextScanId: fetcher.data.nextScanId,
        });
      }
    }
  }, [fetcher]);
  return (
    <Modal
      open={open}
      onOpenChange={(open) => {
        onOpenChange(open, deleteStatus);
      }}
      size="s"
      title={
        !fetcher.data?.success ? (
          <div className="flex gap-3 items-center text-status-error">
            <span className="h-6 w-6 shrink-0">
              <ErrorStandardLineIcon />
            </span>
            Delete scan
          </div>
        ) : undefined
      }
      footer={
        !fetcher.data?.success ? (
          <div className={'flex gap-x-4 justify-end'}>
            <Button
              size="md"
              onClick={() => onOpenChange(false, deleteStatus)}
              type="button"
              variant="outline"
            >
              Cancel
            </Button>
            <Button
              size="md"
              color="error"
              loading={fetcher.state === 'submitting'}
              disabled={fetcher.state === 'submitting'}
              onClick={(e) => {
                e.preventDefault();
                onDeleteScan();
              }}
            >
              Delete
            </Button>
          </div>
        ) : undefined
      }
    >
      {!fetcher.data?.success ? (
        <div className="grid">
          <span>
            Are you sure you want to delete this scan? This action cannot be undone.
          </span>
          {fetcher.data?.message && (
            <p className="text-p7 text-status-error">{fetcher.data?.message}</p>
          )}
          <div className="flex items-center justify-right gap-4"></div>
        </div>
      ) : (
        <SuccessModalContent text="Deleted successfully!" />
      )}
    </Modal>
  );
};

const NotifyModal = ({
  open,
  ids,
  closeModal,
}: {
  open: boolean;
  ids: string[];
  closeModal: React.Dispatch<React.SetStateAction<boolean>>;
}) => {
  const fetcher = useFetcher<ActionData>();

  return (
    <Modal
      size="s"
      open={open}
      onOpenChange={() => closeModal(false)}
      title={
        !fetcher.data?.success ? (
          <div className="flex gap-3 items-center text-text-text-and-icon">
            <span className="h-6 w-6 shrink-0">
              <BellLineIcon />
            </span>
            Notify vulnerabilities
          </div>
        ) : undefined
      }
    >
      {!fetcher.data?.success ? (
        <fetcher.Form method="post">
          <input
            type="text"
            name="actionType"
            hidden
            readOnly
            value={ActionEnumType.NOTIFY}
          />
          {ids.map((id) => (
            <input key={id} type="text" name="nodeIds[]" hidden readOnly value={id} />
          ))}

          <div className="grid">
            <span>The selected vulnerabilities will be notified.</span>
            <br />
            <SelectNotificationChannel />
            <br />
            {ids.length > 1 ? (
              <>
                <span>Do you want to notify each vulnerability separately?</span>
                <div className="mt-2">
                  <Checkbox label="Yes notify them separately" name="notifyIndividual" />
                </div>
              </>
            ) : null}

            {fetcher.data?.message && (
              <p className="mt-2 text-p7 text-status-error">{fetcher.data?.message}</p>
            )}
          </div>

          <div className={'flex gap-x-3 justify-end pt-3 mx-2'}>
            <Button
              size="md"
              onClick={() => closeModal(false)}
              type="button"
              variant="outline"
            >
              Cancel
            </Button>
            <Button
              size="md"
              loading={fetcher.state === 'submitting'}
              disabled={fetcher.state === 'submitting'}
              type="submit"
            >
              Notify
            </Button>
          </div>
        </fetcher.Form>
      ) : (
        <SuccessModalContent text="Notified successfully!" />
      )}
    </Modal>
  );
};

const ScanHistory = () => {
  return (
    <div className="flex items-center h-12">
      <span className="h-3.5 w-3.5 text-text-input-value">
        <ClockLineIcon />
      </span>
      <span className="pl-2 pr-3 text-t3 text-text-text-and-icon uppercase">
        scan time
      </span>
      <Suspense fallback={<HistoryControlsSkeleton />}>
        <HistoryControls />
      </Suspense>
    </div>
  );
};

const HistoryControls = () => {
  const [isSubmitting, setIsSubmitting] = useState(false);
  const { data, fetchStatus } = useScanResults();
  const { scanStatusResult } = data;
  const { scan_id, node_id, node_type, created_at, status, node_name } =
    scanStatusResult ?? {};
  const { navigate, goBack } = usePageNavigation();
  const { downloadScan } = useDownloadScan((state) => {
    setIsSubmitting(state === 'submitting');
  });

  const [openStopScanModal, setOpenStopScanModal] = useState(false);
  const [showScanCompareModal, setShowScanCompareModal] = useState<boolean>(false);
  const [showSbomModal, setShowSbomModal] = useState(false);
  const [scanIdToDelete, setScanIdToDelete] = useState<string | null>(null);

  const [compareBaseScanInfo, setCompareBaseScanInfo] = useState<{
    scanId: string;
    createdAt: number;
  } | null>(null);

  const [compareToScanInfo, setCompareToScanInfo] = useState<{
    scanId: string;
    createdAt: number;
  } | null>(null);

  const [showCompareToScanSelectionModal, setShowCompareToScanSelectionModal] =
    useState(false);

  if (!scan_id || !node_id || !node_type) {
    throw new Error('Scan Type, Node Type and Node Id are required');
  }
  if (!created_at) {
    return null;
  }

  return (
    <>
      {showCompareToScanSelectionModal && (
        <CompareScanInputModal
          showDialog={showCompareToScanSelectionModal}
          setShowDialog={(open, compareToScanInfo) => {
            if (!open) {
              if (compareToScanInfo) {
                setCompareToScanInfo(compareToScanInfo);
                setShowScanCompareModal(true);
              } else {
                setCompareBaseScanInfo(null);
                setCompareToScanInfo(null);
              }
              setShowCompareToScanSelectionModal(false);
            }
          }}
          nodeId={node_id}
          nodeType={node_type}
          scanType={ScanTypeEnum.VulnerabilityScan}
          baseScanInfo={compareBaseScanInfo!}
        />
      )}
      {showScanCompareModal && (
        <VulnerabilitiesCompare
          open={showScanCompareModal}
          onOpenChange={setShowScanCompareModal}
          compareInput={{
            baseScanId: compareBaseScanInfo?.scanId ?? '',
            baseScanTime: compareBaseScanInfo?.createdAt ?? 0,
            toScanId: compareToScanInfo?.scanId ?? '',
            toScanTime: compareToScanInfo?.createdAt ?? 0,
          }}
        />
      )}
      <div className="flex items-center relative flex-grow gap-4">
        {openStopScanModal && (
          <StopScanForm
            open={openStopScanModal}
            closeModal={setOpenStopScanModal}
            scanIds={[scan_id]}
            scanType={ScanTypeEnum.VulnerabilityScan}
          />
        )}

        <ScanHistoryDropdown
          selectedScan={{
            id: scan_id,
            status: scanStatusResult?.status ?? '',
            timestamp: scanStatusResult?.created_at ?? 0,
          }}
          nodeInfo={{
            nodeId: node_id,
            nodeType: node_type,
            scanType: ScanTypeEnum.VulnerabilityScan,
          }}
          onScanDownloadClick={(scanId) => {
            downloadScan({
              scanId: scanId,
              scanType: UtilsReportFiltersScanTypeEnum.Vulnerability,
              nodeType: node_type as UtilsReportFiltersNodeTypeEnum,
            });
          }}
          onScanClick={(scanId) => {
            navigate(
              generatePath('/vulnerability/scan-results/:scanId', {
                scanId: encodeURIComponent(scanId),
              }),
              {
                replace: true,
              },
            );
          }}
          onScanDeleteClick={(scanId) => {
            setScanIdToDelete(scanId);
          }}
          onScanCompareClick={({ scanId, createdAt }) => {
            setCompareBaseScanInfo({
              scanId,
              createdAt,
            });
            setShowCompareToScanSelectionModal(true);
          }}
        />
        {scanIdToDelete && (
          <DeleteScanConfirmationModal
            scanId={scanIdToDelete}
            nodeId={node_id}
            nodeType={node_type}
            open={!!scanIdToDelete}
            onOpenChange={(open, status) => {
              if (open) return;
              if (status.success && scanIdToDelete === scan_id) {
                // if deleting current scan Id
                if (status.nextScanId) {
                  navigate(
                    generatePath('/vulnerability/scan-results/:scanId', {
                      scanId: encodeURIComponent(status.nextScanId),
                    }),
                    {
                      replace: true,
                    },
                  );
                } else {
                  goBack();
                }
              }
              setScanIdToDelete(null);
            }}
          />
        )}
        <div className="h-3 w-[1px] dark:bg-bg-grid-border bg-bg-border-form"></div>
        <ScanStatusBadge status={status ?? ''} className="text-p1" />
        {!isScanInProgress(status ?? '') && !isScanDeletePending(status ?? '') ? (
          <>
            <div className="h-3 w-[1px] dark:bg-bg-grid-border bg-bg-border-form"></div>

            <div className="flex">
              <Button
                variant="flat"
                startIcon={
                  <span className="h-3 w-3">
                    <DownloadLineIcon />
                  </span>
                }
                disabled={fetchStatus !== 'idle' || isSubmitting}
                loading={isSubmitting}
                size="md"
                onClick={() => {
                  downloadScan({
                    scanId: scan_id,
                    scanType: UtilsReportFiltersScanTypeEnum.Vulnerability,
                    nodeType: node_type as UtilsReportFiltersNodeTypeEnum,
                  });
                }}
              >
                Download
              </Button>
              <Button
                variant="flat"
                startIcon={
                  <span className="h-3 w-3">
                    <TrashLineIcon />
                  </span>
                }
                disabled={fetchStatus !== 'idle'}
                onClick={() => setScanIdToDelete(scan_id)}
              >
                Delete
              </Button>
              <>
                {isScanComplete(status ?? '') && (
                  <Button
                    variant="flat"
                    startIcon={
                      <span className="h-3 w-3">
                        <BalanceLineIcon />
                      </span>
                    }
                    disabled={fetchStatus !== 'idle'}
                    onClick={() => {
                      setCompareBaseScanInfo({
                        scanId: scan_id,
                        createdAt: created_at,
                      });
                      setShowCompareToScanSelectionModal(true);
                    }}
                  >
                    Compare scan
                  </Button>
                )}
              </>
              <Button
                variant="flat"
                startIcon={
                  <span className="h-3 w-3">
                    <FileLineIcon />
                  </span>
                }
                disabled={fetchStatus !== 'idle'}
                size="md"
                type="button"
                onClick={() => {
                  setShowSbomModal(true);
                }}
              >
                View/Download SBOM
              </Button>
            </div>
          </>
        ) : (
          <>
            {!isScanDeletePending(status ?? '') ? (
              <Button
                type="button"
                variant="flat"
                size="sm"
                className="absolute right-0 top-0"
                onClick={(e) => {
                  e.preventDefault();
                  setOpenStopScanModal(true);
                }}
              >
                Cancel scan
              </Button>
            ) : null}
          </>
        )}
      </div>
      {showSbomModal ? (
        <SbomModal
          scanId={scan_id}
          nodeName={node_name ?? ''}
          nodeType={node_type}
          onClose={() => {
            setShowSbomModal(false);
          }}
        />
      ) : null}
    </>
  );
};

const ActionDropdown = ({
  ids,
  trigger,
  setIdsToDelete,
  setShowDeleteDialog,
  onTableAction,
  nodeType,
  isDockerImageNameEmpty,
}: {
  ids: string[];
  trigger: React.ReactNode;
  setIdsToDelete: React.Dispatch<React.SetStateAction<string[]>>;
  setShowDeleteDialog: React.Dispatch<React.SetStateAction<boolean>>;
  onTableAction: (
    ids: string[],
    actionType: string,
    maskAction?: ModelScanResultsMaskRequestMaskActionEnum,
  ) => void;
  nodeType: string;
  isDockerImageNameEmpty: boolean;
}) => {
  const isHost = nodeType === 'host';
  const [openNotifyModal, setOpenNotifyModal] = useState<boolean>(false);

  return (
    <>
      {openNotifyModal && (
        <NotifyModal open={true} closeModal={setOpenNotifyModal} ids={ids} />
      )}
      <Dropdown
        triggerAsChild={true}
        align={'start'}
        content={
          <>
            {isHost ? (
              <DropdownItem
                onClick={() =>
                  onTableAction(
                    ids,
                    ActionEnumType.MASK,
                    ModelScanResultsMaskRequestMaskActionEnum.Entity,
                  )
                }
              >
                Mask vulnerability for this host
              </DropdownItem>
            ) : (
              <>
                {!isDockerImageNameEmpty && (
                  <>
                    <DropdownItem
                      onClick={() =>
                        onTableAction(
                          ids,
                          ActionEnumType.MASK,
                          ModelScanResultsMaskRequestMaskActionEnum.ImageTag,
                        )
                      }
                    >
                      Mask vulnerability for this image tag
                    </DropdownItem>
                    <DropdownItem
                      onClick={() =>
                        onTableAction(
                          ids,
                          ActionEnumType.MASK,
                          ModelScanResultsMaskRequestMaskActionEnum.AllImageTag,
                        )
                      }
                    >
                      Mask vulnerability for this image(all tags)
                    </DropdownItem>
                  </>
                )}
              </>
            )}
            <DropdownItem
              onClick={() =>
                onTableAction(
                  ids,
                  ActionEnumType.MASK,
                  ModelScanResultsMaskRequestMaskActionEnum.Global,
                )
              }
            >
              Mask vulnerability across hosts and images
            </DropdownItem>

            <DropdownSeparator />
            {isHost ? (
              <DropdownItem
                onClick={() =>
                  onTableAction(
                    ids,
                    ActionEnumType.UNMASK,
                    ModelScanResultsMaskRequestMaskActionEnum.Entity,
                  )
                }
              >
                Un-mask vulnerability for this host
              </DropdownItem>
            ) : (
              <>
                {!isDockerImageNameEmpty && (
                  <>
                    <DropdownItem
                      onClick={() =>
                        onTableAction(
                          ids,
                          ActionEnumType.UNMASK,
                          ModelScanResultsMaskRequestMaskActionEnum.ImageTag,
                        )
                      }
                    >
                      Un-mask vulnerability for this image tag
                    </DropdownItem>
                    <DropdownItem
                      onClick={() =>
                        onTableAction(
                          ids,
                          ActionEnumType.UNMASK,
                          ModelScanResultsMaskRequestMaskActionEnum.AllImageTag,
                        )
                      }
                    >
                      Un-mask vulnerability for this image(all tags)
                    </DropdownItem>
                  </>
                )}
              </>
            )}
            <DropdownItem
              onClick={() =>
                onTableAction(
                  ids,
                  ActionEnumType.UNMASK,
                  ModelScanResultsMaskRequestMaskActionEnum.Global,
                )
              }
            >
              Un-mask vulnerability across hosts and images
            </DropdownItem>

            <DropdownSeparator />
            <DropdownItem
              onClick={() => {
                setOpenNotifyModal(true);
              }}
            >
              Notify
            </DropdownItem>
            <DropdownSeparator />
            <DropdownItem
              onClick={() => {
                setIdsToDelete(ids);
                setShowDeleteDialog(true);
              }}
              color="error"
            >
              Delete
            </DropdownItem>
          </>
        }
      >
        {trigger}
      </Dropdown>
    </>
  );
};

const BulkActions = ({
  ids,
  setIdsToDelete,
  setShowDeleteDialog,
  onTableAction,
}: {
  ids: string[];
  setIdsToDelete: React.Dispatch<React.SetStateAction<string[]>>;
  setShowDeleteDialog: React.Dispatch<React.SetStateAction<boolean>>;
  onTableAction: (
    ids: string[],
    actionType: string,
    maskAction?: ModelScanResultsMaskRequestMaskActionEnum,
  ) => void;
}) => {
  const [openNotifyModal, setOpenNotifyModal] = useState<boolean>(false);
  const { data } = useScanResults();
  const { scanStatusResult, data: scanResults } = data;

  const isHost = scanStatusResult?.node_type === 'host';
  const isDockerImageNameEmpty = scanResults?.dockerImageName?.trim()?.length === 0;

  return (
    <>
      {openNotifyModal && (
        <NotifyModal open={true} closeModal={setOpenNotifyModal} ids={ids} />
      )}
      <Dropdown
        triggerAsChild
        align={'start'}
        disabled={!ids.length}
        content={
          <>
            {isHost ? (
              <DropdownItem
                onClick={() =>
                  onTableAction(
                    ids,
                    ActionEnumType.MASK,
                    ModelScanResultsMaskRequestMaskActionEnum.Entity,
                  )
                }
              >
                Mask vulnerabilities for this host
              </DropdownItem>
            ) : (
              <>
                {!isDockerImageNameEmpty && (
                  <>
                    <DropdownItem
                      onClick={() =>
                        onTableAction(
                          ids,
                          ActionEnumType.MASK,
                          ModelScanResultsMaskRequestMaskActionEnum.ImageTag,
                        )
                      }
                    >
                      Mask vulnerabilities for this image tag
                    </DropdownItem>
                    <DropdownItem
                      onClick={() =>
                        onTableAction(
                          ids,
                          ActionEnumType.MASK,
                          ModelScanResultsMaskRequestMaskActionEnum.AllImageTag,
                        )
                      }
                    >
                      Mask vulnerabilities for this image(all tags)
                    </DropdownItem>
                  </>
                )}
              </>
            )}
            <DropdownItem
              onClick={() =>
                onTableAction(
                  ids,
                  ActionEnumType.MASK,
                  ModelScanResultsMaskRequestMaskActionEnum.Global,
                )
              }
            >
              Mask vulnerabilities across hosts and images
            </DropdownItem>
          </>
        }
      >
        <Button
          color="default"
          variant="flat"
          size="sm"
          startIcon={<EyeSolidIcon />}
          endIcon={<CaretDown />}
          disabled={!ids.length}
        >
          Mask
        </Button>
      </Dropdown>
      <Dropdown
        triggerAsChild
        align={'start'}
        disabled={!ids.length}
        content={
          <>
            {isHost ? (
              <DropdownItem
                onClick={() =>
                  onTableAction(
                    ids,
                    ActionEnumType.UNMASK,
                    ModelScanResultsMaskRequestMaskActionEnum.Entity,
                  )
                }
              >
                Un-mask vulnerabilities for this host
              </DropdownItem>
            ) : (
              <>
                {!isDockerImageNameEmpty && (
                  <>
                    <DropdownItem
                      onClick={() =>
                        onTableAction(
                          ids,
                          ActionEnumType.UNMASK,
                          ModelScanResultsMaskRequestMaskActionEnum.ImageTag,
                        )
                      }
                    >
                      Un-mask vulnerabilities for this image tag
                    </DropdownItem>
                    <DropdownItem
                      onClick={() =>
                        onTableAction(
                          ids,
                          ActionEnumType.UNMASK,
                          ModelScanResultsMaskRequestMaskActionEnum.AllImageTag,
                        )
                      }
                    >
                      Un-mask vulnerabilities for this image(all tags)
                    </DropdownItem>
                  </>
                )}
              </>
            )}
            <DropdownItem
              onClick={() =>
                onTableAction(
                  ids,
                  ActionEnumType.UNMASK,
                  ModelScanResultsMaskRequestMaskActionEnum.Global,
                )
              }
            >
              Un-mask vulnerabilities across hosts and images
            </DropdownItem>
          </>
        }
      >
        <Button
          color="default"
          variant="flat"
          size="sm"
          startIcon={<EyeHideSolid />}
          endIcon={<CaretDown />}
          disabled={!ids.length}
        >
          Unmask
        </Button>
      </Dropdown>
      <Button
        variant="flat"
        size="sm"
        startIcon={<BellLineIcon />}
        disabled={!ids.length}
        onClick={() => {
          setOpenNotifyModal(true);
        }}
      >
        Notify
      </Button>
      <Button
        color="error"
        variant="flat"
        size="sm"
        startIcon={<TrashLineIcon />}
        disabled={!ids.length}
        onClick={() => {
          setIdsToDelete(ids);
          setShowDeleteDialog(true);
        }}
      >
        Delete
      </Button>
    </>
  );
};

const FILTER_SEARCHPARAMS: Record<string, string> = {
  visibility: 'Masked/Unmasked',
  severity: 'CVE severity',
  cveId: 'CVE ID',
  attackVector: 'Attack vector',
  language: 'CVE type',
  exploitable: 'Exploitable',
};

const getPrettyNameForAppliedFilters = ({
  key,
  value,
}: {
  key: string;
  value: string;
}) => {
  switch (key) {
    case 'severity':
      return getSeverityPrettyName(value as SeverityValueType);
    case 'visibility':
      return getMaskedUnmaskedPrettyName(value);
    case 'exploitable':
      return 'Most Exploitable';
    case 'attackVector':
      return capitalize(value);
    case 'language':
      return capitalize(value);
    default:
      return value;
  }
};

const getAppliedFiltersCount = (searchParams: URLSearchParams) => {
  return Object.keys(FILTER_SEARCHPARAMS).reduce((prev, curr) => {
    return prev + searchParams.getAll(curr).length;
  }, 0);
};

const Filters = () => {
  const [searchParams, setSearchParams] = useSearchParams();
  const [maskedQuery, setMaskedQuery] = useState('');
  const [severityQuery, setSeverityQuery] = useState('');

  const appliedFilterCount = getAppliedFiltersCount(searchParams);

  const params = useParams();
  const scanId = params?.scanId ?? '';

  return (
    <FilterWrapper>
      <div className="flex gap-2">
        <Combobox
          getDisplayValue={() => FILTER_SEARCHPARAMS['visibility']}
          multiple
          value={searchParams.getAll('visibility')}
          onChange={(values) => {
            setSearchParams((prev) => {
              prev.delete('visibility');
              values.forEach((value) => {
                prev.append('visibility', value);
              });
              prev.delete('page');
              return prev;
            });
          }}
          onQueryChange={(query) => {
            setMaskedQuery(query);
          }}
          clearAllElement="Clear"
          onClearAll={() => {
            setSearchParams((prev) => {
              prev.delete('visibility');
              prev.delete('page');
              return prev;
            });
          }}
        >
          {['masked', 'unmasked']
            .filter((item) => {
              if (!maskedQuery.length) return true;
              return item.includes(maskedQuery.toLowerCase());
            })
            .map((item) => {
              return (
                <ComboboxOption key={item} value={item}>
                  {capitalize(item)}
                </ComboboxOption>
              );
            })}
        </Combobox>
        <Combobox
          getDisplayValue={() => FILTER_SEARCHPARAMS['severity']}
          multiple
          value={searchParams.getAll('severity')}
          onChange={(values) => {
            setSearchParams((prev) => {
              prev.delete('severity');
              values.forEach((value) => {
                prev.append('severity', value);
              });
              prev.delete('page');
              return prev;
            });
          }}
          onQueryChange={(query) => {
            setSeverityQuery(query);
          }}
          clearAllElement="Clear"
          onClearAll={() => {
            setSearchParams((prev) => {
              prev.delete('severity');
              prev.delete('page');
              return prev;
            });
          }}
        >
          {SeverityEnumList.filter((item) => {
            if (!severityQuery.length) return true;
            return item.includes(severityQuery.toLowerCase());
          }).map((item) => {
            return (
              <ComboboxOption key={item} value={item}>
                {getSeverityPrettyName(item)}
              </ComboboxOption>
            );
          })}
        </Combobox>
        <SearchableCVEList
          scanId={scanId}
          defaultSelectedCVEIds={searchParams.getAll('cveId')}
          onChange={(values) => {
            setSearchParams((prev) => {
              prev.delete('cveId');
              values.forEach((value) => {
                prev.append('cveId', value);
              });
              prev.delete('page');
              return prev;
            });
          }}
          onClearAll={() => {
            setSearchParams((prev) => {
              prev.delete('cveId');
              prev.delete('page');
              return prev;
            });
          }}
        />
        <Suspense
          fallback={
            <Combobox
              startIcon={<CircleSpinner size="sm" className="w-3 h-3" />}
              placeholder={FILTER_SEARCHPARAMS['language']}
              multiple
              onQueryChange={() => {
                // no operation
              }}
            />
          }
        >
          <FilterByLanguage />
        </Suspense>
        <Suspense
          fallback={
            <Combobox
              startIcon={<CircleSpinner size="sm" className="w-3 h-3" />}
              placeholder={FILTER_SEARCHPARAMS['attackVector']}
              multiple
              onQueryChange={() => {
                // no operation
              }}
            />
          }
        >
          <FilterByAttackVector />
        </Suspense>
        <FilterByExploitable />
      </div>
      {appliedFilterCount > 0 ? (
        <div className="flex gap-2.5 mt-4 flex-wrap items-center">
          {Array.from(searchParams)
            .filter(([key]) => {
              return Object.keys(FILTER_SEARCHPARAMS).includes(key);
            })
            .map(([key, value]) => {
              return (
                <FilterBadge
                  key={`${key}-${value}`}
                  onRemove={() => {
                    setSearchParams((prev) => {
                      const existingValues = prev.getAll(key);
                      prev.delete(key);
                      existingValues.forEach((existingValue) => {
                        if (existingValue !== value) prev.append(key, existingValue);
                      });
                      prev.delete('page');
                      return prev;
                    });
                  }}
                  text={`${FILTER_SEARCHPARAMS[key]}: ${getPrettyNameForAppliedFilters({ key, value })}`}
                />
              );
            })}
          <Button
            variant="flat"
            color="default"
            startIcon={<TimesIcon />}
            onClick={() => {
              setSearchParams((prev) => {
                Object.keys(FILTER_SEARCHPARAMS).forEach((key) => {
                  prev.delete(key);
                });
                prev.delete('page');
                return prev;
              });
            }}
            size="sm"
          >
            Clear all
          </Button>
        </div>
      ) : null}
    </FilterWrapper>
  );
};

export const useScanResults = () => {
  const [searchParams] = useSearchParams();
  const params = useParams();
  const scanId = params?.scanId ?? '';
  return useSuspenseQuery({
    ...queries.vulnerability.scanResults({
      scanId,
      page: getPageFromSearchParams(searchParams),
      pageSize: parseInt(searchParams.get('size') ?? String(DEFAULT_PAGE_SIZE)),
      order: getOrderFromSearchParams(searchParams) || {
        sortBy: 'cve_severity',
        descending: true,
      },
      severity: searchParams.getAll('severity'),
      visibility: searchParams.getAll('visibility'),
      cve: searchParams.getAll('cveId'),
      language: searchParams.getAll('language'),
      attackVector: searchParams.getAll('attackVector'),
      exploitable: searchParams.getAll('exploitable'),
    }),
    keepPreviousData: true,
  });
};

const useGetFieldFilters = (fieldName: string) => {
  const params = useParams();
  const scanId = params?.scanId ?? '';
  return useSuspenseQuery({
    ...queries.vulnerability.searchScanResultFilters({
      scanId,
      fieldName: fieldName,
      size: 999,
      searchText: '',
    }),
  });
};

const useTop5Vulnerabilities = () => {
  const params = useParams();
  const scanId = params?.scanId ?? '';
  const [searchParams] = useSearchParams();
  return useSuspenseQuery({
    ...queries.vulnerability.top5VulnerabilitiesForScan({
      scanId,
      severity: searchParams.getAll('severity'),
      visibility: searchParams.getAll('visibility'),
      cve: searchParams.getAll('cveId'),
      language: searchParams.getAll('language'),
      attackVector: searchParams.getAll('attackVector'),
      exploitable: searchParams.getAll('exploitable'),
    }),
  });
};

const FilterByLanguage = () => {
  const [searchParams, setSearchParams] = useSearchParams();
  const [languageQuery, setLanguageQuery] = useState('');

  const {
    data: { data: languages },
  } = useGetFieldFilters('cve_type');

  return (
    <Combobox
      getDisplayValue={() => FILTER_SEARCHPARAMS['language']}
      multiple
      value={searchParams.getAll('language')}
      onChange={(values) => {
        setSearchParams((prev) => {
          prev.delete('language');
          values.forEach((value) => {
            prev.append('language', value);
          });
          prev.delete('page');
          return prev;
        });
      }}
      onQueryChange={(query) => {
        setLanguageQuery(query);
      }}
      clearAllElement="Clear"
      onClearAll={() => {
        setSearchParams((prev) => {
          prev.delete('language');
          prev.delete('page');
          return prev;
        });
      }}
    >
      {languages
        .filter((item) => {
          if (!languageQuery.length) return true;
          return item.includes(languageQuery.toLowerCase());
        })
        .map((item) => {
          return (
            <ComboboxOption key={item} value={item}>
              {capitalize(item)}
            </ComboboxOption>
          );
        })}
    </Combobox>
  );
};
const FilterByAttackVector = () => {
  const [searchParams, setSearchParams] = useSearchParams();
  const [attackVectorQuery, setAttackVectorQuery] = useState('');

  const {
    data: { data: attackVector },
  } = useGetFieldFilters('parsed_attack_vector');

  return (
    <Combobox
      getDisplayValue={() => FILTER_SEARCHPARAMS['attackVector']}
      multiple
      value={searchParams.getAll('attackVector')}
      onChange={(values) => {
        setSearchParams((prev) => {
          prev.delete('attackVector');
          values.forEach((value) => {
            prev.append('attackVector', value);
          });
          prev.delete('page');
          return prev;
        });
      }}
      onQueryChange={(query) => {
        setAttackVectorQuery(query);
      }}
      clearAllElement="Clear"
      onClearAll={() => {
        setSearchParams((prev) => {
          prev.delete('attackVector');
          prev.delete('page');
          return prev;
        });
      }}
    >
      {attackVector
        .filter((item) => {
          if (!attackVectorQuery.length) return true;
          return item.includes(attackVectorQuery.toLowerCase());
        })
        .map((item) => {
          return (
            <ComboboxOption key={item} value={item}>
              {capitalize(item)}
            </ComboboxOption>
          );
        })}
    </Combobox>
  );
};
const FilterByExploitable = () => {
  const [searchParams, setSearchParams] = useSearchParams();

  return (
    <Combobox
      getDisplayValue={() => FILTER_SEARCHPARAMS['exploitable']}
      multiple
      value={searchParams.getAll('exploitable')}
      onChange={(values) => {
        setSearchParams((prev) => {
          prev.delete('exploitable');
          values.forEach((value) => {
            prev.append('exploitable', value);
          });
          prev.delete('page');
          return prev;
        });
      }}
      onQueryChange={() => {
        // nothing
      }}
      clearAllElement="Clear"
      onClearAll={() => {
        setSearchParams((prev) => {
          prev.delete('exploitable');
          prev.delete('page');
          return prev;
        });
      }}
    >
      <ComboboxOption key={'most_exploitable'} value={'most_exploitable'}>
        Most exploitable
      </ComboboxOption>
    </Combobox>
  );
};

const CVEResults = () => {
  const [searchParams] = useSearchParams();
  const [rowSelectionState, setRowSelectionState] = useState<RowSelectionState>({});
  const [showDeleteDialog, setShowDeleteDialog] = useState(false);
  const [idsToDelete, setIdsToDelete] = useState<string[]>([]);
  const [filtersExpanded, setFiltersExpanded] = useState(true);
  const fetcher = useFetcher<ActionData>();

  const onTableAction = useCallback(
    (
      ids: string[],
      actionType: string,
      maskAction?: ModelScanResultsMaskRequestMaskActionEnum,
    ) => {
      const formData = new FormData();
      formData.append('actionType', actionType);

      if (actionType === ActionEnumType.MASK || actionType === ActionEnumType.UNMASK) {
        formData.append('maskAction', maskAction?.toString() ?? '');
      }

      ids.forEach((item) => formData.append('nodeIds[]', item));
      fetcher.submit(formData, {
        method: 'post',
      });
    },
    [fetcher],
  );

  const selectedIds = useMemo(() => {
    return Object.keys(rowSelectionState);
  }, [rowSelectionState]);

  return (
    <div className="self-start pb-6">
      <div className="mt-4 h-12 flex items-center">
        <Suspense>
          <BulkActions
            ids={selectedIds}
            onTableAction={onTableAction}
            setIdsToDelete={setIdsToDelete}
            setShowDeleteDialog={setShowDeleteDialog}
          />
        </Suspense>
        <div className="pr-2 ml-auto flex items-center gap-1">
          <Button
            className="pr-0"
            color="default"
            variant="flat"
            size="sm"
            startIcon={<FilterIcon />}
            onClick={() => {
              setFiltersExpanded((prev) => !prev);
            }}
            data-testid="filterButtonIdForTable"
          >
            Filter
          </Button>
          {getAppliedFiltersCount(searchParams) > 0 ? (
            <Badge
              label={String(getAppliedFiltersCount(searchParams))}
              variant="filled"
              size="small"
              color="blue"
            />
          ) : null}
        </div>
      </div>
      {filtersExpanded ? <Filters /> : null}
      <div className="dark:shadow-none shadow-md">
        <Suspense fallback={<TableSkeleton columns={7} rows={10} />}>
          <CVETable
            onTableAction={onTableAction}
            setShowDeleteDialog={setShowDeleteDialog}
            setIdsToDelete={setIdsToDelete}
            rowSelectionState={rowSelectionState}
            setRowSelectionState={setRowSelectionState}
          />
        </Suspense>
      </div>
      {showDeleteDialog && (
        <DeleteConfirmationModal
          showDialog={showDeleteDialog}
          ids={idsToDelete}
          setShowDialog={setShowDeleteDialog}
          onDeleteSuccess={() => {
            setRowSelectionState({});
          }}
        />
      )}
    </div>
  );
};
const TablePlaceholder = ({
  scanStatus,
  message,
}: {
  scanStatus: string;
  message: string;
}) => {
  if (isScanFailed(scanStatus)) {
    return (
      <div className="flex items-center justify-center min-h-[384px]">
        <ScanStatusInError errorMessage={message} />
      </div>
    );
  }
  if (isScanStopped(scanStatus)) {
    return (
      <div className="flex items-center justify-center h-[384px]">
        <ScanStatusStopped errorMessage={message ?? ''} />
      </div>
    );
  }
  if (isScanStopping(scanStatus)) {
    return (
      <div className="flex items-center justify-center h-[384px]">
        <ScanStatusStopping />
      </div>
    );
  }
  if (isScanInProgress(scanStatus)) {
    return (
      <div className="flex items-center justify-center min-h-[384px]">
        <ScanStatusInProgress />
      </div>
    );
  }
  if (isScanDeletePending(scanStatus)) {
    return (
      <div className="flex items-center justify-center min-h-[384px]">
        <ScanStatusDeletePending />
      </div>
    );
  }
  return <TableNoDataElement text="No data available" />;
};
const CVETable = ({
  onTableAction,
  setIdsToDelete,
  setShowDeleteDialog,
  rowSelectionState,
  setRowSelectionState,
}: {
  onTableAction: (
    ids: string[],
    actionType: string,
    maskAction?: ModelScanResultsMaskRequestMaskActionEnum,
  ) => void;
  setIdsToDelete: React.Dispatch<React.SetStateAction<string[]>>;
  setShowDeleteDialog: React.Dispatch<React.SetStateAction<boolean>>;
  rowSelectionState: RowSelectionState;
  setRowSelectionState: React.Dispatch<React.SetStateAction<RowSelectionState>>;
}) => {
  const { mode: theme } = useTheme();
  const [searchParams, setSearchParams] = useSearchParams();
  const { data } = useScanResults();
  const { data: scanResultData, scanStatusResult } = data;
  const columnHelper = createColumnHelper<ModelVulnerability>();
  const [sort, setSort] = useSortingState();

  const nodeType = scanStatusResult?.node_type ?? '';
  const isDockerImageNameEmpty = scanResultData?.dockerImageName?.trim()?.length === 0;

  const columns = useMemo(() => {
    const columns = [
      getRowSelectionColumn(columnHelper, {
        size: 30,
        minSize: 30,
        maxSize: 30,
      }),
      columnHelper.display({
        id: 'actions',
        enableSorting: false,
        cell: (cell) => (
          <ActionDropdown
            ids={[cell.row.original.node_id]}
            setIdsToDelete={setIdsToDelete}
            setShowDeleteDialog={setShowDeleteDialog}
            onTableAction={onTableAction}
            nodeType={nodeType}
            isDockerImageNameEmpty={isDockerImageNameEmpty}
            trigger={
              <button className="h-[16px] w-[16px] text-text-text-and-icon rotate-90">
                <EllipsisIcon />
              </button>
            }
          />
        ),
        header: () => '',
        size: 30,
        minSize: 30,
        maxSize: 30,
        enableResizing: false,
      }),
      columnHelper.accessor('cve_id', {
        cell: (info) => (
          <DFLink
            to={{
              pathname: `./${encodeURIComponent(info.row.original.node_id)}`,
              search: searchParams.toString(),
            }}
            className="flex items-center gap-x-[6px]"
          >
            <div className="w-4 h-4 shrink-0 text-text-text-and-icon">
              <VulnerabilityIcon />
            </div>
            <div className="truncate">{info.getValue()}</div>
          </DFLink>
        ),
        header: () => 'CVE ID',
        minSize: 160,
        size: 160,
        maxSize: 160,
      }),
      columnHelper.accessor('cve_caused_by_package', {
        cell: (info) => <TruncatedText text={info.getValue()} />,
        header: () => 'Package',
        minSize: 160,
        size: 160,
        maxSize: 160,
      }),
      columnHelper.accessor('cve_cvss_score', {
        cell: (info) => (
          <div className="text-p3 text-text-text-and-icon">{info.getValue()}</div>
        ),
        header: () => <TruncatedText text="CVSS score" />,
        minSize: 50,
        size: 60,
        maxSize: 60,
      }),
      columnHelper.accessor('cve_severity', {
        cell: (info) => (
          <div className="text-p4 text-text-text-and-icon gap-1 inline-flex">
            <SeverityBadgeIcon
              severity={info.getValue() as VulnerabilitySeverityType}
              theme={theme}
            />
            {upperFirst(info.getValue())}
          </div>
        ),
        header: () => 'Severity',
        minSize: 70,
        size: 80,
        maxSize: 90,
      }),
      columnHelper.accessor('cve_description', {
        enableSorting: false,
        cell: (info) => {
          return <TruncatedText text={info.getValue() || 'No description available'} />;
        },
        header: () => 'Description',
        minSize: 220,
        size: 220,
        maxSize: 230,
      }),
      columnHelper.accessor('cve_link', {
        enableSorting: false,
        cell: (info) => {
          if (!info.getValue().length) return '-';
          return (
            <DFLink to={info.getValue()} target="_blank" rel="noopener noreferrer">
              <div className="h-[16px] w-[16px]">
                <PopOutIcon />
              </div>
            </DFLink>
          );
        },
        header: () => 'Link',
        minSize: 40,
        size: 40,
        maxSize: 45,
        enableResizing: false,
      }),
    ];

    return columns;
  }, [setSearchParams, nodeType, theme]);

  return (
    <Table
      size="default"
      data={scanResultData?.tableData ?? []}
      columns={columns}
      enableRowSelection
      rowSelectionState={rowSelectionState}
      onRowSelectionChange={setRowSelectionState}
      enablePagination
      manualPagination
      enableColumnResizing
      approximatePagination
      totalRows={scanResultData?.pagination?.totalRows}
      pageSize={parseInt(searchParams.get('size') ?? String(DEFAULT_PAGE_SIZE))}
      pageIndex={scanResultData?.pagination?.currentPage}
      getRowId={(row) => row.node_id}
      enableSorting
      manualSorting
      sortingState={sort}
      onSortingChange={(updaterOrValue) => {
        let newSortState: SortingState = [];
        if (typeof updaterOrValue === 'function') {
          newSortState = updaterOrValue(sort);
        } else {
          newSortState = updaterOrValue;
        }
        setSearchParams((prev) => {
          if (!newSortState.length) {
            prev.delete('sortby');
            prev.delete('desc');
          } else {
            prev.set('sortby', String(newSortState[0].id));
            prev.set('desc', String(newSortState[0].desc));
          }
          return prev;
        });
        setSort(newSortState);
      }}
      onPaginationChange={(updaterOrValue) => {
        let newPageIndex = 0;
        if (typeof updaterOrValue === 'function') {
          newPageIndex = updaterOrValue({
            pageIndex: scanResultData?.pagination?.currentPage ?? 0,
            pageSize: parseInt(searchParams.get('size') ?? String(DEFAULT_PAGE_SIZE)),
          }).pageIndex;
        } else {
          newPageIndex = updaterOrValue.pageIndex;
        }
        setSearchParams((prev) => {
          prev.set('page', String(newPageIndex));
          return prev;
        });
      }}
      getTrProps={(row) => {
        if (row.original.masked) {
          return {
            className: 'opacity-40',
          };
        }
        return {};
      }}
      enablePageResize
      onPageResize={(newSize) => {
        setSearchParams((prev) => {
          prev.set('size', String(newSize));
          prev.delete('page');
          return prev;
        });
      }}
      noDataElement={
        <TablePlaceholder
          scanStatus={scanStatusResult?.status ?? ''}
          message={scanStatusResult?.status_message ?? ''}
        />
      }
      getTdProps={(cell) => {
        const severity = cell.row.original.cve_severity;
        return {
          className: cn(
            'relative',
            'first:before:content-[""]',
            'first:before:absolute',
            'first:before:h-full',
            'first:before:w-1',
            'first:before:left-0',
            'first:before:top-px',
            {
              'first:before:bg-severity-critical': isCriticalSeverity(severity),
              'first:before:bg-severity-high': isHighSeverity(severity),
              'first:before:bg-severity-medium': isMediumSeverity(severity),
              'first:before:bg-severity-low': isLowSeverity(severity),
              'first:before:bg-severity-unknown': isUnknownSeverity(severity),
            },
          ),
        };
      }}
    />
  );
};

const Header = () => {
  return (
    <BreadcrumbWrapper>
      <>
        <Breadcrumb>
          <BreadcrumbLink asChild icon={<VulnerabilityIcon />} isLink>
            <DFLink to={'/vulnerability'} unstyled>
              Vulnerabilities
            </DFLink>
          </BreadcrumbLink>
          <Suspense
            fallback={
              <BreadcrumbLink isLast>
                <CircleSpinner size="sm" />
              </BreadcrumbLink>
            }
          >
            <DynamicBreadcrumbs />
          </Suspense>
        </Breadcrumb>
      </>
    </BreadcrumbWrapper>
  );
};

const DynamicBreadcrumbs = () => {
  const { data } = useScanResults();
  const { scanStatusResult } = data;
  const { node_type, node_name } = scanStatusResult ?? {};
  let nodeType = node_type;
  if (node_type === 'image') {
    nodeType = 'container_image';
  }

  return (
    <>
      <BreadcrumbLink isLink asChild>
        <DFLink to={`/vulnerability/scans?nodeType=${nodeType}`} unstyled>
          {capitalize(nodeType?.replace('_', ' ') ?? '')}
        </DFLink>
      </BreadcrumbLink>
      <BreadcrumbLink isLast>
        <span className="inherit cursor-auto">{node_name}</span>
      </BreadcrumbLink>
    </>
  );
};
const SeverityCounts = ({
  severityCounts,
}: {
  severityCounts: {
    [k: string]: number;
  };
}) => {
  const { mode } = useTheme();
  const [, setSearchParams] = useSearchParams();

  return (
    <>
      {Object.keys(severityCounts)?.map((key) => {
        return (
          <div className="flex gap-x-2 w-full items-center" key={key}>
            <div
              className="h-3 w-3 rounded-full"
              style={{
                backgroundColor:
                  getSeverityColorMap(mode)[
                    key.toLowerCase() as VulnerabilitySeverityType
                  ],
              }}
            ></div>
            <button
              className="capitalize text-p4 text-text-icon"
              onClick={() => {
                setSearchParams((prev) => {
                  prev.delete('page');
                  Object.keys(FILTER_SEARCHPARAMS).forEach((key) => {
                    prev.delete(key);
                  });
                  prev.append('severity', key.toLowerCase());
                  return prev;
                });
              }}
            >
              {key}
            </button>
            <div className="ml-auto text-p11 text-text-input-value">
              {abbreviateNumber(severityCounts?.[key] ?? 0)}
            </div>
          </div>
        );
      })}
    </>
  );
};
const ScanStatusWrapper = ({
  children,
  scanStatusResult,
  displayNoData,
}: {
  children: React.ReactNode;
  scanStatusResult: ModelScanInfo | undefined;
  displayNoData?: boolean;
}) => {
  if (isScanFailed(scanStatusResult?.status ?? '')) {
    return (
      <div className="flex items-center justify-center h-[140px]">
        <ScanStatusInError errorMessage={scanStatusResult?.status_message ?? ''} />
      </div>
    );
  }

  if (isScanStopped(scanStatusResult?.status ?? '')) {
    return (
      <div className="flex items-center justify-center h-[140px]">
        <ScanStatusStopped errorMessage={scanStatusResult?.status_message ?? ''} />
      </div>
    );
  }

  if (isScanStopping(scanStatusResult?.status ?? '')) {
    return (
      <div className="flex items-center justify-center h-[140px]">
        <ScanStatusStopping />
      </div>
    );
  }

  if (isScanInProgress(scanStatusResult?.status ?? '')) {
    return (
      <div className="flex items-center justify-center h-[140px]">
        <ScanStatusInProgress />
      </div>
    );
  }

  if (isScanDeletePending(scanStatusResult?.status ?? '')) {
    return (
      <div className="flex items-center justify-center h-[140px]">
        <ScanStatusDeletePending />
      </div>
    );
  }

  if (displayNoData) {
    return (
      <div className="flex items-center justify-center h-[140px]">
        <ScanStatusNoData />
      </div>
    );
  }

  return <>{children}</>;
};
const SeverityCountWidget = () => {
  const {
    data: { data, scanStatusResult },
  } = useScanResults();

  const severityCounts: {
    [k: string]: number;
  } = data?.severityCounts ?? {};

  const [, setSearchParams] = useSearchParams();

  return (
    <ScanStatusWrapper scanStatusResult={scanStatusResult}>
      <div className="flex items-center">
        <div className="h-[140px] w-[140px]">
          <VulnerabilityScanResultsPieChart
            data={severityCounts}
            onChartClick={({ name }: { name: string; value: string | number | Date }) => {
              setSearchParams((prev) => {
                prev.delete('page');
                Object.keys(FILTER_SEARCHPARAMS).forEach((key) => {
                  prev.delete(key);
                });
                prev.append('severity', name.toLowerCase());
                return prev;
              });
            }}
          />
        </div>
        <div className="flex flex-1 justify-center">
          <div className="flex flex-col flex-1 max-w-[160px]">
            {keys(severityCounts).length === 0 ? (
              <div className="flex flex-col flex-1">
                <ScanStatusNoData />
              </div>
            ) : (
              <div className="flex flex-col flex-1 max-w-[160px] gap-y-[6px]">
                <SeverityCounts severityCounts={severityCounts} />
              </div>
            )}
          </div>
        </div>
      </div>
    </ScanStatusWrapper>
  );
};

const Top5Widget = () => {
  const { mode: theme } = useTheme();
  const { data } = useTop5Vulnerabilities();
  const [searchParams] = useSearchParams();

  const { data: scanResult } = useScanResults();
  const { scanStatusResult } = scanResult;

  return (
    <ScanStatusWrapper
      scanStatusResult={scanStatusResult}
      displayNoData={!data.data || data.data?.length === 0}
    >
      <table className="table-fixed w-full">
        <tbody>
          {data.data?.map((cve) => {
            return (
              <tr key={cve.node_id}>
                <td className="w-[60%] px-0 pt-0 pb-1 overflow-hidden">
                  <div className="flex gap-x-[8px] items-center">
                    <div
                      className={cn('w-[3px] rounded h-[18px] shrink-0', {
                        'bg-severity-critical':
                          cve.cve_severity === SeverityEnum.Critical,
                        'bg-severity-high': cve.cve_severity === SeverityEnum.High,
                        'bg-severity-medium': cve.cve_severity === SeverityEnum.Medium,
                        'bg-severity-low': cve.cve_severity === SeverityEnum.Low,
                        'bg-severity-unknown':
                          !cve.cve_severity || cve.cve_severity === SeverityEnum.Unknown,
                      })}
                    ></div>
                    <div className="w-[14px] h-[14px] shrink-0">
                      <VulnerabilityIcon />
                    </div>
                    <DFLink
                      to={{
                        pathname: `./${encodeURIComponent(cve.node_id)}`,
                        search: searchParams.toString(),
                      }}
                      className="flex items-center gap-3 overflow-hidden"
                    >
                      <TruncatedText className="text-p7" text={cve.cve_id} />
                    </DFLink>
                  </div>
                </td>
                <td className="w-[20%] px-0 pt-0">
                  <div className="flex items-center justify-center text-p3 text-text-text-and-icon">
                    {cve.cve_cvss_score}
                  </div>
                </td>
                <td className="w-[20%] px-0 pt-0 pb-1">
                  <div className="flex gap-1">
                    <SeverityBadgeIcon
                      severity={cve.cve_severity as VulnerabilitySeverityType}
                      theme={theme}
                    />
                    <span className="text-p4 text-text-text-and-icon">
                      {upperFirst(cve.cve_severity)}
                    </span>
                  </div>
                </td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </ScanStatusWrapper>
  );
};

const Widgets = () => {
  return (
    <div className="flex gap-4 flex-nowrap min-h-[200px]">
      <Card className="flex-1 px-4 py-1.5 flex flex-col">
        <div className="text-h6 text-text-input-value py-1">Total vulnerabilities</div>
        <div className="mt-2 flex-1 pl-4">
          <Suspense
            fallback={
              <div className="flex items-center justify-center min-h-[100px]">
                <CircleSpinner size="md" />
              </div>
            }
          >
            <SeverityCountWidget />
          </Suspense>
        </div>
      </Card>
      <Card className="flex-1 px-4 py-1.5 flex flex-col">
        <div className="text-h6 text-text-input-value py-1">Top 5 vulnerabilities</div>
        <div className="mt-2 flex-1">
          <Suspense
            fallback={
              <div className="flex items-center justify-center min-h-[100px]">
                <CircleSpinner size="md" />
              </div>
            }
          >
            <Top5Widget />
          </Suspense>
        </div>
      </Card>
      <Card className="flex-1 px-4 py-1.5 flex flex-col">
        <div className="text-h6 text-text-input-value py-1">Top attack paths</div>
        <Suspense
          fallback={
            <div className="flex items-center justify-center flex-1">
              <CircleSpinner size="md" />
            </div>
          }
        >
          <TopAttackPathsWidget />
        </Suspense>
      </Card>
    </div>
  );
};

const TopAttackPathsWidget = () => {
  const { data } = useScanResults();
  const nodeId = data?.scanStatusResult?.node_id;

  return (
    <div className="flex-1">
      <ScanStatusWrapper
        scanStatusResult={data?.scanStatusResult}
        displayNoData={!nodeId}
      >
        <VulnerabilityThreatGraph nodeIds={[nodeId ?? '']} direction="LR" hideToolbar />
      </ScanStatusWrapper>
    </div>
  );
};

const VulnerabilityScanResults = () => {
  return (
    <>
      <Header />
      <div className="mx-4">
        <ScanHistory />
        <Widgets />
        <CVEResults />
        <Outlet />
      </div>
    </>
  );
};

export const module = {
  action,
  element: <VulnerabilityScanResults />,
};
