import { useMemo } from 'react';

import type { FeatureFilters, FeatureMeta } from '../../../types';
import type { PercentileScale } from '../../../lib/format';
import { groupFeaturesByCategory } from '../../../lib/features';
import { getSpecificCrimeFeatureName, isSpecificCrimeFilterName } from '../../../lib/crime-filter';
import {
  getElectionVoteShareFeatureName,
  isElectionVoteShareFilterName,
} from '../../../lib/election-filter';
import { getEthnicityFeatureName, isEthnicityFilterName } from '../../../lib/ethnicity-filter';
import { getSchoolBackendFeatureName, isSchoolFilterName } from '../../../lib/school-filter';
import {
  getPoiDistanceFeatureName,
  isPoiDistanceFilterName,
} from '../../../lib/poi-distance-filter';
import type { TravelTimeEntry } from '../../../hooks/useTravelTime';
import { EthnicityFilterCard } from './EthnicityFilterCard';
import { PoiDistanceFilterCard } from './PoiDistanceFilterCard';
import { SchoolFilterCard } from './SchoolFilterCard';
import { SpecificCrimeFilterCard } from './SpecificCrimeFilterCard';
import { ElectionVoteShareFilterCard } from './ElectionVoteShareFilterCard';
import { EnumFeatureFilterCard } from './EnumFeatureFilterCard';
import { NumericFeatureFilterCard } from './NumericFeatureFilterCard';
import { TravelTimeFilterCards } from './TravelTimeFilterCards';
import { CollapsibleGroupHeader } from '../../ui/CollapsibleGroupHeader';

const TRANSPORT_GROUP = 'Transport';

interface ActiveFilterListProps {
  features: FeatureMeta[];
  filters: FeatureFilters;
  enabledFeatureList: FeatureMeta[];
  activeFeature: string | null;
  dragValue: [number, number] | null;
  pinnedFeature: string | null;
  travelTimeEntries: TravelTimeEntry[];
  filterImpacts?: Record<string, number>;
  percentileScales: Map<string, PercentileScale>;
  destinationDropdownPortal: boolean;
  isGroupExpanded: (name: string) => boolean;
  onToggleGroup: (name: string) => void;
  onFilterChange: (name: string, value: [number, number] | string[]) => void;
  onRemoveFilter: (name: string) => void;
  onDragStart: (name: string, initialValue?: [number, number]) => void;
  onDragChange: (value: [number, number]) => void;
  onDragEnd: () => void;
  onTogglePin: (name: string) => void;
  onShowInfo: (feature: FeatureMeta) => void;
  onTravelTimeRemoveEntry: (index: number) => void;
  onTravelTimeSetDestination: (
    index: number,
    slug: string,
    label: string,
    lat: number,
    lon: number
  ) => void;
  onTravelTimeRangeChange: (index: number, range: [number, number]) => void;
  onTravelTimeDragEnd: (index: number) => void;
  onTravelTimeToggleBest: (index: number) => void;
  onTravelTimeToggleNoChange: (index: number) => void;
  onTravelTimeToggleOneChange: (index: number) => void;
  onTravelTimeToggleNoBuses: (index: number) => void;
}

export function ActiveFilterList({
  features,
  filters,
  enabledFeatureList,
  activeFeature,
  dragValue,
  pinnedFeature,
  travelTimeEntries,
  filterImpacts,
  percentileScales,
  destinationDropdownPortal,
  isGroupExpanded,
  onToggleGroup,
  onFilterChange,
  onRemoveFilter,
  onDragStart,
  onDragChange,
  onDragEnd,
  onTogglePin,
  onShowInfo,
  onTravelTimeRemoveEntry,
  onTravelTimeSetDestination,
  onTravelTimeRangeChange,
  onTravelTimeDragEnd,
  onTravelTimeToggleBest,
  onTravelTimeToggleNoChange,
  onTravelTimeToggleOneChange,
  onTravelTimeToggleNoBuses,
}: ActiveFilterListProps) {
  const travelCards = (
    <TravelTimeFilterCards
      entries={travelTimeEntries}
      activeFeature={activeFeature}
      dragValue={dragValue}
      pinnedFeature={pinnedFeature}
      filterImpacts={filterImpacts}
      destinationDropdownPortal={destinationDropdownPortal}
      onTogglePin={onTogglePin}
      onTravelTimeRemoveEntry={onTravelTimeRemoveEntry}
      onTravelTimeSetDestination={onTravelTimeSetDestination}
      onTravelTimeRangeChange={onTravelTimeRangeChange}
      onTravelTimeDragEnd={onTravelTimeDragEnd}
      onTravelTimeToggleBest={onTravelTimeToggleBest}
      onTravelTimeToggleNoChange={onTravelTimeToggleNoChange}
      onTravelTimeToggleOneChange={onTravelTimeToggleOneChange}
      onTravelTimeToggleNoBuses={onTravelTimeToggleNoBuses}
      onDragStart={onDragStart}
      onDragChange={onDragChange}
    />
  );

  const groupedFeatures = useMemo(() => {
    const groups = groupFeaturesByCategory(enabledFeatureList);
    const transportGroup = groups.find((group) => group.name === TRANSPORT_GROUP);
    const otherGroups = groups.filter((group) => group.name !== TRANSPORT_GROUP);

    if (transportGroup) return [transportGroup, ...otherGroups];
    if (travelTimeEntries.length > 0)
      return [{ name: TRANSPORT_GROUP, features: [] }, ...otherGroups];
    return otherGroups;
  }, [enabledFeatureList, travelTimeEntries.length]);

  const renderFeatureCard = (feature: FeatureMeta) => {
    if (isSchoolFilterName(feature.name)) {
      const schoolBackendName = getSchoolBackendFeatureName(feature.name);
      return (
        <SchoolFilterCard
          key={feature.name}
          features={features}
          schoolFeature={feature}
          filters={filters}
          activeFeature={activeFeature}
          dragValue={dragValue}
          pinnedFeature={pinnedFeature}
          filterImpact={schoolBackendName ? filterImpacts?.[schoolBackendName] : undefined}
          onFilterChange={onFilterChange}
          onDragStart={onDragStart}
          onDragChange={onDragChange}
          onDragEnd={onDragEnd}
          onTogglePin={onTogglePin}
          onShowInfo={onShowInfo}
          onRemove={() => onRemoveFilter(feature.name)}
        />
      );
    }

    if (isSpecificCrimeFilterName(feature.name)) {
      const specificCrimeBackendName = getSpecificCrimeFeatureName(feature.name);
      return (
        <SpecificCrimeFilterCard
          key={feature.name}
          features={features}
          crimeFeature={feature}
          filters={filters}
          activeFeature={activeFeature}
          dragValue={dragValue}
          pinnedFeature={pinnedFeature}
          filterImpact={
            specificCrimeBackendName ? filterImpacts?.[specificCrimeBackendName] : undefined
          }
          percentileScale={
            specificCrimeBackendName ? percentileScales.get(specificCrimeBackendName) : undefined
          }
          onFilterChange={onFilterChange}
          onDragStart={onDragStart}
          onDragChange={onDragChange}
          onDragEnd={onDragEnd}
          onTogglePin={onTogglePin}
          onShowInfo={onShowInfo}
          onRemove={() => onRemoveFilter(feature.name)}
        />
      );
    }

    if (isElectionVoteShareFilterName(feature.name)) {
      const electionVoteShareBackendName = getElectionVoteShareFeatureName(feature.name);
      return (
        <ElectionVoteShareFilterCard
          key={feature.name}
          features={features}
          voteShareFeature={feature}
          filters={filters}
          activeFeature={activeFeature}
          dragValue={dragValue}
          pinnedFeature={pinnedFeature}
          filterImpact={
            electionVoteShareBackendName ? filterImpacts?.[electionVoteShareBackendName] : undefined
          }
          percentileScale={
            electionVoteShareBackendName
              ? percentileScales.get(electionVoteShareBackendName)
              : undefined
          }
          onFilterChange={onFilterChange}
          onDragStart={onDragStart}
          onDragChange={onDragChange}
          onDragEnd={onDragEnd}
          onTogglePin={onTogglePin}
          onShowInfo={onShowInfo}
          onRemove={() => onRemoveFilter(feature.name)}
        />
      );
    }

    if (isEthnicityFilterName(feature.name)) {
      const ethnicityBackendName = getEthnicityFeatureName(feature.name);
      return (
        <EthnicityFilterCard
          key={feature.name}
          features={features}
          ethnicityFeature={feature}
          filters={filters}
          activeFeature={activeFeature}
          dragValue={dragValue}
          pinnedFeature={pinnedFeature}
          filterImpact={ethnicityBackendName ? filterImpacts?.[ethnicityBackendName] : undefined}
          percentileScale={
            ethnicityBackendName ? percentileScales.get(ethnicityBackendName) : undefined
          }
          onFilterChange={onFilterChange}
          onDragStart={onDragStart}
          onDragChange={onDragChange}
          onDragEnd={onDragEnd}
          onTogglePin={onTogglePin}
          onShowInfo={onShowInfo}
          onRemove={() => onRemoveFilter(feature.name)}
        />
      );
    }

    if (isPoiDistanceFilterName(feature.name)) {
      const poiBackendName = getPoiDistanceFeatureName(feature.name);
      return (
        <PoiDistanceFilterCard
          key={feature.name}
          features={features}
          poiFeature={feature}
          filters={filters}
          activeFeature={activeFeature}
          dragValue={dragValue}
          pinnedFeature={pinnedFeature}
          filterImpact={poiBackendName ? filterImpacts?.[poiBackendName] : undefined}
          percentileScale={poiBackendName ? percentileScales.get(poiBackendName) : undefined}
          onFilterChange={onFilterChange}
          onDragStart={onDragStart}
          onDragChange={onDragChange}
          onDragEnd={onDragEnd}
          onTogglePin={onTogglePin}
          onShowInfo={onShowInfo}
          onRemove={() => onRemoveFilter(feature.name)}
        />
      );
    }

    return feature.type === 'enum' ? (
      <EnumFeatureFilterCard
        key={feature.name}
        feature={feature}
        filters={filters}
        pinnedFeature={pinnedFeature}
        filterImpact={filterImpacts?.[feature.name]}
        onFilterChange={onFilterChange}
        onTogglePin={onTogglePin}
        onShowInfo={onShowInfo}
        onRemoveFilter={onRemoveFilter}
      />
    ) : (
      <NumericFeatureFilterCard
        key={feature.name}
        feature={feature}
        filters={filters}
        activeFeature={activeFeature}
        dragValue={dragValue}
        pinnedFeature={pinnedFeature}
        filterImpact={filterImpacts?.[feature.name]}
        percentileScale={percentileScales.get(feature.name)}
        onFilterChange={onFilterChange}
        onDragStart={onDragStart}
        onDragChange={onDragChange}
        onDragEnd={onDragEnd}
        onTogglePin={onTogglePin}
        onShowInfo={onShowInfo}
        onRemoveFilter={onRemoveFilter}
      />
    );
  };

  return (
    <div>
      {groupedFeatures.map((group) => {
        const travelCount = group.name === TRANSPORT_GROUP ? travelTimeEntries.length : 0;
        const count = group.features.length + travelCount;
        if (count === 0) return null;
        const expanded = isGroupExpanded(group.name);
        return (
          <div key={group.name} className="shrink-0">
            <CollapsibleGroupHeader
              name={group.name}
              expanded={expanded}
              onToggle={() => onToggleGroup(group.name)}
              className="sticky top-0 z-30 px-3 py-2.5 text-sm font-bold text-navy-950 bg-warm-200 dark:bg-navy-900 dark:text-warm-100 hover:bg-warm-200 dark:hover:bg-warm-800"
            >
              <span className="text-xs font-medium text-warm-400 dark:text-warm-500">{count}</span>
            </CollapsibleGroupHeader>
            {expanded && (
              <div className="px-2 py-1.5 space-y-3.5">
                {group.name === TRANSPORT_GROUP && travelCards}
                {group.features.map((feature) => renderFeatureCard(feature))}
              </div>
            )}
          </div>
        );
      })}
    </div>
  );
}
