import { useSuspenseQuery } from '@suspensive/react-query';
import { Suspense, useEffect, useMemo, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import { cn } from 'tailwind-preset';
import {
  Badge,
  Button,
  Combobox,
  ComboboxOption,
  createColumnHelper,
  Dropdown,
  DropdownItem,
  getRowSelectionColumn,
  RowSelectionState,
  SortingState,
  Table,
  TableNoDataElement,
  TableSkeleton,
} from 'ui-components';

import { ModelKubernetesCluster } from '@/api/generated';
import {
  ConfigureScanModal,
  ConfigureScanModalProps,
} from '@/components/ConfigureScanModal';
import { DFLink } from '@/components/DFLink';
import { FilterBadge } from '@/components/filters/FilterBadge';
import { SearchableClusterList } from '@/components/forms/SearchableClusterList';
import { SearchableUserDefinedTagList } from '@/components/forms/SearchableUserDefinedTagList';
import { CaretDown } from '@/components/icons/common/CaretDown';
import { EllipsisIcon } from '@/components/icons/common/Ellipsis';
import { FilterIcon } from '@/components/icons/common/Filter';
import { TagOutlineIcon } from '@/components/icons/common/TagOutline';
import { TimesIcon } from '@/components/icons/common/Times';
import { MalwareIcon } from '@/components/sideNavigation/icons/Malware';
import { PostureIcon } from '@/components/sideNavigation/icons/Posture';
import { SecretsIcon } from '@/components/sideNavigation/icons/Secrets';
import { VulnerabilityIcon } from '@/components/sideNavigation/icons/Vulnerability';
import { TruncatedText } from '@/components/TruncatedText';
import { FilterWrapper } from '@/features/common/FilterWrapper';
import { queries } from '@/queries';
import {
  ComplianceScanNodeTypeEnum,
  MalwareScanNodeTypeEnum,
  ScanTypeEnum,
  SecretScanNodeTypeEnum,
  VulnerabilityScanNodeTypeEnum,
} from '@/types/common';
import {
  getOrderFromSearchParams,
  getPageFromSearchParams,
  useSortingState,
} from '@/utils/table';

const DEFAULT_PAGE_SIZE = 25;

const FILTER_SEARCHPARAMS: Record<string, string> = {
  agentRunning: 'Agent running',
  userDefinedTags: 'Custom tags',
};

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

function Filters() {
  const [searchParams, setSearchParams] = useSearchParams();

  const [agentRunningSearchText, setAgentRunningSearchText] = useState('');
  const appliedFilterCount = getAppliedFiltersCount(searchParams);

  return (
    <FilterWrapper>
      <div className="flex gap-2">
        <SearchableClusterList
          defaultSelectedClusters={searchParams.getAll('clusters')}
          onClearAll={() => {
            setSearchParams((prev) => {
              prev.delete('clusters');
              prev.delete('page');
              return prev;
            });
          }}
          onChange={(value) => {
            setSearchParams((prev) => {
              prev.delete('clusters');
              value.forEach((cluster) => {
                prev.append('clusters', cluster);
              });
              prev.delete('page');
              return prev;
            });
          }}
        />
        <Combobox
          value={searchParams.getAll('agentRunning')}
          multiple
          onQueryChange={(query) => {
            setAgentRunningSearchText(query);
          }}
          onChange={(values) => {
            setSearchParams((prev) => {
              prev.delete('agentRunning');
              values.forEach((value) => {
                prev.append('agentRunning', value);
              });
              prev.delete('page');
              return prev;
            });
          }}
          getDisplayValue={() => FILTER_SEARCHPARAMS['agentRunning']}
        >
          {['Yes', 'No']
            .filter((item) => {
              if (!agentRunningSearchText.length) return true;
              return item.toLowerCase().includes(agentRunningSearchText.toLowerCase());
            })
            .map((item) => {
              return (
                <ComboboxOption key={item} value={item}>
                  {item}
                </ComboboxOption>
              );
            })}
        </Combobox>
        <SearchableUserDefinedTagList
          defaultSelectedTags={searchParams.getAll('userDefinedTags')}
          onChange={(value) => {
            setSearchParams((prev) => {
              prev.delete('userDefinedTags');
              value.forEach((tag) => {
                prev.append('userDefinedTags', tag);
              });
              prev.delete('page');
              return prev;
            });
          }}
          triggerVariant="button"
          resourceType="kubernetes_cluster"
          onClearAll={() => {
            setSearchParams((prev) => {
              prev.delete('userDefinedTags');
              prev.delete('page');
              return prev;
            });
          }}
        />
      </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]}: ${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 KubernetesTable = () => {
  const [selectedNodes, setSelectedNodes] = useState<ModelKubernetesCluster[]>([]);
  const [searchParams] = useSearchParams();
  const [filtersExpanded, setFiltersExpanded] = useState(true);

  return (
    <div className="px-4 pb-4">
      <div className="h-12 flex items-center">
        <BulkActions
          nodes={selectedNodes.map((cluster) => ({
            nodeId: cluster.node_id,
            agentRunning: cluster.agent_running,
          }))}
        />
        <Button
          variant="flat"
          className="ml-auto"
          startIcon={<FilterIcon />}
          endIcon={
            getAppliedFiltersCount(searchParams) > 0 ? (
              <Badge
                label={String(getAppliedFiltersCount(searchParams))}
                variant="filled"
                size="small"
                color="blue"
              />
            ) : null
          }
          size="sm"
          onClick={() => {
            setFiltersExpanded((prev) => !prev);
          }}
          data-testid="filterButtonIdForTable"
        >
          Filter
        </Button>
      </div>
      {filtersExpanded ? <Filters /> : null}
      <Suspense
        fallback={<TableSkeleton rows={DEFAULT_PAGE_SIZE} columns={3} size="default" />}
      >
        <DataTable setSelectedNodes={setSelectedNodes} />
      </Suspense>
    </div>
  );
};

const BulkActions = ({
  nodes,
}: {
  nodes: {
    nodeId: string;
    agentRunning: boolean;
  }[];
}) => {
  const [scanOptions, setScanOptions] =
    useState<ConfigureScanModalProps['scanOptions']>();
  const nodesWithAgentRunning = nodes.filter((node) => node.agentRunning);

  return (
    <>
      <Dropdown
        triggerAsChild
        align={'start'}
        disabled={!nodesWithAgentRunning.length}
        content={
          <>
            <DropdownItem
              onSelect={(e) => {
                e.preventDefault();
                setScanOptions({
                  showAdvancedOptions: nodesWithAgentRunning.length === 1,
                  scanType: ScanTypeEnum.VulnerabilityScan,
                  data: {
                    nodes: nodesWithAgentRunning.map((node) => {
                      return {
                        nodeId: node.nodeId,
                        nodeType: VulnerabilityScanNodeTypeEnum.kubernetes_cluster,
                      };
                    }),
                  },
                });
              }}
              icon={<VulnerabilityIcon />}
            >
              Start Vulnerability Scan
            </DropdownItem>
            <DropdownItem
              onSelect={(e) => {
                e.preventDefault();
                setScanOptions({
                  showAdvancedOptions: nodesWithAgentRunning.length === 1,
                  scanType: ScanTypeEnum.SecretScan,
                  data: {
                    nodes: nodesWithAgentRunning.map((node) => {
                      return {
                        nodeId: node.nodeId,
                        nodeType: SecretScanNodeTypeEnum.kubernetes_cluster,
                      };
                    }),
                  },
                });
              }}
              icon={<SecretsIcon />}
            >
              Start Secret Scan
            </DropdownItem>
            <DropdownItem
              onSelect={(e) => {
                e.preventDefault();
                setScanOptions({
                  showAdvancedOptions: nodesWithAgentRunning.length === 1,
                  scanType: ScanTypeEnum.MalwareScan,
                  data: {
                    nodes: nodesWithAgentRunning.map((node) => {
                      return {
                        nodeId: node.nodeId,
                        nodeType: MalwareScanNodeTypeEnum.kubernetes_cluster,
                      };
                    }),
                  },
                });
              }}
              icon={<MalwareIcon />}
            >
              Start Malware Scan
            </DropdownItem>
            <DropdownItem
              onSelect={(e) => {
                e.preventDefault();
                setScanOptions({
                  showAdvancedOptions: nodesWithAgentRunning.length === 1,
                  scanType: ScanTypeEnum.ComplianceScan,
                  data: {
                    nodeIds: nodesWithAgentRunning.map((node) => node.nodeId),
                    nodeType: ComplianceScanNodeTypeEnum.kubernetes_cluster,
                  },
                });
              }}
              icon={<PostureIcon />}
            >
              Start Posture Scan
            </DropdownItem>
          </>
        }
      >
        <Button
          color="default"
          variant="flat"
          size="sm"
          endIcon={<CaretDown />}
          disabled={!nodesWithAgentRunning.length}
        >
          Actions
        </Button>
      </Dropdown>
      {!!scanOptions && (
        <ConfigureScanModal
          open
          onOpenChange={() => setScanOptions(undefined)}
          scanOptions={scanOptions}
        />
      )}
    </>
  );
};

function useSearchClustersWithPagination() {
  const [searchParams] = useSearchParams();
  return useSuspenseQuery({
    ...queries.search.clustersWithPagination({
      page: getPageFromSearchParams(searchParams),
      pageSize: parseInt(searchParams.get('size') ?? String(DEFAULT_PAGE_SIZE)),
      order: getOrderFromSearchParams(searchParams),
      clusterIds: searchParams.getAll('clusters'),
      agentRunning: searchParams
        .getAll('agentRunning')
        .map((value) => (value === 'Yes' ? true : false)),
      userDefinedTags: searchParams.getAll('userDefinedTags'),
    }),
    keepPreviousData: true,
  });
}

const DataTable = ({
  setSelectedNodes,
}: {
  setSelectedNodes: React.Dispatch<React.SetStateAction<ModelKubernetesCluster[]>>;
}) => {
  const { data } = useSearchClustersWithPagination();
  const columnHelper = createColumnHelper<ModelKubernetesCluster>();
  const [rowSelectionState, setRowSelectionState] = useState<RowSelectionState>({});
  const [sort, setSort] = useSortingState();
  const [searchParams, setSearchParams] = useSearchParams();

  useEffect(() => {
    setSelectedNodes((prev) => {
      const newSelectedNodes: ModelKubernetesCluster[] = [];
      prev.forEach((node) => {
        if (rowSelectionState[node.node_id] === true) {
          newSelectedNodes.push(node);
        }
      });
      Object.keys(rowSelectionState).forEach((nodeId) => {
        if (!newSelectedNodes.find((node) => node.node_id === nodeId)) {
          newSelectedNodes.push(data.clusters.find((node) => node.node_id === nodeId)!);
        }
      });
      return newSelectedNodes;
    });
  }, [rowSelectionState, data]);

  const columns = useMemo(
    () => [
      getRowSelectionColumn(columnHelper, {
        minSize: 20,
        size: 20,
        maxSize: 20,
      }),
      columnHelper.display({
        id: 'actions',
        enableSorting: false,
        cell: (cell) => (
          <Dropdown
            triggerAsChild={true}
            align={'start'}
            content={
              <>
                <DropdownItem>
                  <DFLink
                    to={`../compute/host/table?clusters=${cell.row.original.node_id}`}
                    unstyled
                  >
                    Go to hosts
                  </DFLink>
                </DropdownItem>
                <DropdownItem>
                  <DFLink
                    to={`../compute/container/table?clusters=${cell.row.original.node_id}`}
                    unstyled
                  >
                    Go to containers
                  </DFLink>
                </DropdownItem>
                <DropdownItem>
                  <DFLink to={`../compute/pod/table?clusters=${name}`} unstyled>
                    Go to pods
                  </DFLink>
                </DropdownItem>
              </>
            }
          >
            <div className="cursor-pointer h-3 w-4 text-text-text-and-icon rotate-90">
              <EllipsisIcon />
            </div>
          </Dropdown>
        ),
        header: () => '',
        size: 35,
        minSize: 35,
        maxSize: 35,
        enableResizing: false,
      }),
      columnHelper.accessor('node_name', {
        cell: (info) => {
          let name = '';
          if (info.row.original.node_name.length > 0) {
            name = info.row.original.node_name;
          } else {
            name = info.row.original.node_id;
          }
          return (
            <div className="flex flex-col gap-1 items-start text-start py-2">
              <TruncatedText text={name} />
              {info.row.original?.tags?.length ? (
                <div className="flex gap-2 items-center flex-wrap">
                  {info.row.original.tags.map((tag) => {
                    return (
                      <Badge
                        startIcon={<TagOutlineIcon />}
                        key={tag}
                        label={tag}
                        variant="filled"
                        color="info"
                        size="small"
                      />
                    );
                  })}
                </div>
              ) : null}
            </div>
          );
          return <TruncatedText text={name} />;
        },
        header: () => 'Name',
        minSize: 140,
        size: 160,
        maxSize: 200,
      }),
      columnHelper.accessor('node_id', {
        cell: (info) => {
          return <TruncatedText text={info.getValue() ?? ''} />;
        },
        header: () => <span>Node id</span>,
        minSize: 200,
        size: 210,
        maxSize: 250,
      }),
      columnHelper.accessor('agent_running', {
        cell: (info) => {
          return (
            <TruncatedText
              text={info.getValue() ? 'Yes' : 'No'}
              className={cn({
                'text-status-success': info.getValue(),
              })}
            />
          );
        },
        header: () => <span>Agent running</span>,
        minSize: 60,
        size: 100,
        maxSize: 120,
      }),
    ],
    [],
  );

  return (
    <>
      <Table
        data={data.clusters ?? []}
        columns={columns}
        noDataElement={<TableNoDataElement text="No kubernetes clusters are connected" />}
        size="default"
        enableColumnResizing
        enablePagination
        manualPagination
        enableRowSelection
        approximatePagination
        rowSelectionState={rowSelectionState}
        onRowSelectionChange={setRowSelectionState}
        getRowId={(row) => row.node_id}
        totalRows={data.totalRows}
        pageIndex={data.currentPage}
        onPaginationChange={(updaterOrValue) => {
          let newPageIndex = 0;
          if (typeof updaterOrValue === 'function') {
            newPageIndex = updaterOrValue({
              pageIndex: data.currentPage,
              pageSize: parseInt(searchParams.get('size') ?? String(DEFAULT_PAGE_SIZE)),
            }).pageIndex;
          } else {
            newPageIndex = updaterOrValue.pageIndex;
          }
          setSearchParams((prev) => {
            prev.set('page', String(newPageIndex));
            return prev;
          });
        }}
        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);
        }}
        pageSize={parseInt(searchParams.get('size') ?? String(DEFAULT_PAGE_SIZE))}
        enablePageResize
        onPageResize={(newSize) => {
          setSearchParams((prev) => {
            prev.set('size', String(newSize));
            prev.delete('page');
            return prev;
          });
        }}
      />
    </>
  );
};
