"use client";

import { useSearchParams } from "next/navigation";
import { useState } from "react";

import { ComplianceScanInfo } from "@/components/compliance/compliance-header/compliance-scan-info";
import { ClearFiltersButton } from "@/components/filters/clear-filters-button";
import { EntityInfo } from "@/components/shadcn/entities/entity-info";
import {
  MultiSelect,
  MultiSelectContent,
  MultiSelectItem,
  MultiSelectSelectAll,
  MultiSelectSeparator,
  MultiSelectTrigger,
  MultiSelectValue,
} from "@/components/shadcn/select/multiselect";
import { useUrlFilters } from "@/hooks/use-url-filters";
import {
  getScanEntityLabel,
  isConnectionStatus,
  isScanEntity,
} from "@/lib/helper-filters";
import { cn } from "@/lib/utils";
import {
  FilterEntity,
  FilterOption,
  ProviderEntity,
  ScanEntity,
} from "@/types";
import { DATA_TABLE_FILTER_MODE, DataTableFilterMode } from "@/types/filters";
import { ProviderConnectionStatus } from "@/types/providers";

function isNonEmptyString(value: string | null | undefined): value is string {
  return Boolean(value);
}

export interface DataTableFilterCustomProps {
  filters: FilterOption[];
  /** Optional element to render at the start of the filters grid */
  prependElement?: React.ReactNode;
  /** Optional className override for the filters grid layout */
  gridClassName?: string;
  /** Hide the clear filters button and active badges (useful when parent manages this) */
  hideClearButton?: boolean;
  /**
   * Controls when filter selections are pushed to the URL.
   * - "instant" (default): each selection immediately updates the URL (legacy behavior, backward-compatible).
   * - "batch": selections accumulate in pending state; caller manages when to push URL.
   */
  mode?: DataTableFilterMode;
  /**
   * Called in "batch" mode when a filter value changes.
   * The key is the raw filter key (e.g. "filter[severity__in]" or "severity__in").
   * Only invoked when mode === "batch".
   */
  onBatchChange?: (filterKey: string, values: string[]) => void;
  /**
   * Returns the current selected values for a filter in "batch" mode.
   * Replaces reading from URL searchParams when mode === "batch".
   * Only used when mode === "batch".
   */
  getFilterValue?: (filterKey: string) => string[];
}

export const DataTableFilterCustom = ({
  filters,
  prependElement,
  gridClassName,
  hideClearButton = false,
  mode = DATA_TABLE_FILTER_MODE.INSTANT,
  onBatchChange,
  getFilterValue,
}: DataTableFilterCustomProps) => {
  const { updateFilter } = useUrlFilters();
  const searchParams = useSearchParams();
  const [openFilterKey, setOpenFilterKey] = useState<string | null>(null);

  const buildSearchConfig = (filter: FilterOption) => {
    const label = filter.labelCheckboxGroup.toLowerCase();
    return {
      placeholder: `Search ${label}...`,
      emptyMessage: `No ${label} found.`,
    };
  };

  // Helper function to get entity from valueLabelMapping
  const getEntityForValue = (
    filter: FilterOption,
    value: string,
  ): FilterEntity | undefined => {
    if (!filter.valueLabelMapping) return undefined;
    const entry = filter.valueLabelMapping.find((mapping) => mapping[value]);
    return entry ? entry[value] : undefined;
  };

  // Helper function to get badge label from entity
  const getBadgeLabel = (
    entity: FilterEntity | undefined,
    value: string,
  ): string => {
    if (!entity) return value;

    if (isScanEntity(entity as ScanEntity)) {
      // Match the summary-strip chip: "Scan: {provider} - {name}". Without the
      // "Scan:" prefix, the trigger badge would just say "AWS Prod - Nightly",
      // which reads as a generic account tag and hides that it's a scan filter.
      const label = getScanEntityLabel(entity as ScanEntity);
      return label ? `Scan: ${label}` : value;
    }
    if (isConnectionStatus(entity)) {
      const connectionStatus = entity as ProviderConnectionStatus;
      return connectionStatus.label;
    }
    // Provider entity
    const providerEntity = entity as ProviderEntity;
    return providerEntity.alias || providerEntity.uid || value;
  };

  // Render custom content for entity (scan, provider, or connection status)
  const renderEntityContent = (entity: FilterEntity) => {
    if (isScanEntity(entity as ScanEntity)) {
      return <ComplianceScanInfo scan={entity as ScanEntity} />;
    }
    if (isConnectionStatus(entity)) {
      const connectionStatus = entity as ProviderConnectionStatus;
      return <span>{connectionStatus.label}</span>;
    }
    // Provider entity
    const providerEntity = entity as ProviderEntity;
    return (
      <EntityInfo
        cloudProvider={providerEntity.provider}
        entityAlias={providerEntity.alias ?? undefined}
        entityId={providerEntity.uid}
        showCopyAction={false}
      />
    );
  };

  const getSearchKeywords = (
    entity: FilterEntity | undefined,
    value: string,
    displayLabel: string,
  ): string[] => {
    if (!entity) {
      return [displayLabel, value];
    }

    if (isScanEntity(entity as ScanEntity)) {
      const label = getScanEntityLabel(entity as ScanEntity);
      return [displayLabel, value, label].filter(isNonEmptyString);
    }

    if (isConnectionStatus(entity)) {
      return [displayLabel, value, (entity as ProviderConnectionStatus).label];
    }

    const providerEntity = entity as ProviderEntity;
    return [
      displayLabel,
      value,
      providerEntity.alias,
      providerEntity.uid,
      providerEntity.provider,
    ].filter(isNonEmptyString);
  };

  // Sort filters by index property, with fallback to original order for filters without index
  const sortedFilters = () => {
    return [...filters].sort((a, b) => {
      // If both have index, sort by index
      if (a.index !== undefined && b.index !== undefined) {
        return a.index - b.index;
      }
      // If only one has index, prioritize the one with index
      if (a.index !== undefined) return -1;
      if (b.index !== undefined) return 1;
      // If neither has index, maintain original order
      return 0;
    });
  };

  const pushDropdownFilter = (filter: FilterOption, values: string[]) => {
    if (mode === DATA_TABLE_FILTER_MODE.BATCH && onBatchChange) {
      // In batch mode, notify the caller instead of updating the URL
      onBatchChange(filter.key, values);
      return;
    }

    // Instant mode (default): push to URL immediately
    // If this filter defaults to "all selected" and the user selected all items,
    // clear the URL param to represent "no specific filter" (i.e., all).
    const allSelected =
      filter.values.length > 0 && values.length === filter.values.length;

    if (filter.defaultToSelectAll && allSelected) {
      updateFilter(filter.key, null);
      return;
    }

    updateFilter(filter.key, values.length > 0 ? values : null);
  };

  const getSelectedValues = (filter: FilterOption): string[] => {
    if (mode === DATA_TABLE_FILTER_MODE.BATCH && getFilterValue) {
      // In batch mode, read from pending state provided by the caller
      return getFilterValue(filter.key);
    }

    // Instant mode (default): read from URL searchParams
    const filterKey = filter.key.startsWith("filter[")
      ? filter.key
      : `filter[${filter.key}]`;
    const paramValue = searchParams.get(filterKey);

    // If defaultToSelectAll is true and no filter param exists,
    // treat it as "all selected" by returning all values
    if (!paramValue && filter.defaultToSelectAll) {
      return filter.values;
    }

    return paramValue ? paramValue.split(",") : [];
  };

  return (
    <div
      className={cn(
        "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5",
        gridClassName,
      )}
    >
      {prependElement}
      {sortedFilters().map((filter) => {
        const selectedValues = getSelectedValues(filter);

        return (
          <MultiSelect
            key={filter.key}
            open={openFilterKey === filter.key}
            onOpenChange={(open) => setOpenFilterKey(open ? filter.key : null)}
            values={selectedValues}
            onValuesChange={(values) => pushDropdownFilter(filter, values)}
          >
            <MultiSelectTrigger size="default">
              <MultiSelectValue
                placeholder={`All ${filter.labelCheckboxGroup}`}
              />
            </MultiSelectTrigger>
            <MultiSelectContent
              search={buildSearchConfig(filter)}
              width={filter.width ?? "default"}
            >
              <MultiSelectSelectAll>Select All</MultiSelectSelectAll>
              <MultiSelectSeparator />
              {filter.values.map((value) => {
                const entity = getEntityForValue(filter, value);
                const displayLabel = filter.labelFormatter
                  ? filter.labelFormatter(value)
                  : value;
                return (
                  <MultiSelectItem
                    key={value}
                    value={value}
                    badgeLabel={getBadgeLabel(entity, displayLabel)}
                    keywords={getSearchKeywords(entity, value, displayLabel)}
                  >
                    {entity ? renderEntityContent(entity) : displayLabel}
                  </MultiSelectItem>
                );
              })}
            </MultiSelectContent>
          </MultiSelect>
        );
      })}
      {!hideClearButton && (
        <div className="flex items-center justify-start">
          <ClearFiltersButton />
        </div>
      )}
    </div>
  );
};
