import { ArrowUpDown, Filter, Search, X } from 'lucide-react';

import { type FormEvent, type RefObject } from 'react';

import { Button } from '@/Components/ui/button';
import { Input } from '@/Components/ui/input';
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/Components/ui/select';
import { Slider } from '@/Components/ui/slider';
import { cn } from '@/lib/utils';

export interface FilterOption {
  value: string;
  label: string;
  count?: number;
}

export interface FilterGroup {
  /** Filter key sent to the backend (e.g. "post_type"). */
  key: string;
  /** Visible label rendered above the dropdown (e.g. "Type"). */
  label: string;
  /** Currently selected raw value, or null when "all" is active. */
  value: string | null;
  /** Available options. The "all" choice is generated automatically. */
  options: FilterOption[];
  /** Label for the "all" option (e.g. "All types"). Defaults to `All ${label.toLowerCase()}`. */
  allLabel?: string;
  /** Optional total count to display in the "all" option (e.g. 351). */
  allCount?: number;
}

export interface SearchConfig {
  value: string;
  onChange: (value: string) => void;
  onSubmit: (e: FormEvent) => void;
  placeholder?: string;
  inputRef?: RefObject<HTMLInputElement>;
}

/**
 * Sort dropdown slot. Reuses {@link FilterOption} for its options and feeds the
 * same active-chip pipeline as dropdown filters. The active chip appears whenever
 * the current `value` differs from `defaultValue` (or the first option when no
 * default is given). An optional `direction` toggle renders an asc/desc button.
 */
export interface SortConfig {
  key: string;
  /** Visible label, e.g. "Sort". Also used as the chip prefix. */
  label: string;
  /** Current sort value. */
  value: string;
  /** Available sort options (reuses the FilterOption shape). */
  options: FilterOption[];
  /** Value considered "not active" for chip purposes. Defaults to the first option. */
  defaultValue?: string;
  onChange: (value: string) => void;
  /** Optional direction toggle. */
  direction?: { value: 'asc' | 'desc'; onToggle: () => void };
}

/**
 * Date-range slot. Renders two `<input type="date">` controls and a SINGLE
 * removable chip describing the active range.
 */
export interface DateRangeConfig {
  startKey: string;
  endKey: string;
  startValue: string | null;
  endValue: string | null;
  onChange: (
    startKey: string,
    start: string | null,
    endKey: string,
    end: string | null,
  ) => void;
  /** Chip prefix, default "Dates". */
  label?: string;
}

/**
 * Numeric slider slot. Renders a shadcn `Slider` with a live value label. An
 * active (above-min) slider feeds the active-chip pipeline; removing resets to min.
 */
export interface SliderConfig {
  key: string;
  label: string;
  value: number;
  min: number;
  max: number;
  step?: number;
  /** Live, on drag. */
  onChange: (key: string, value: number) => void;
  /** On release (e.g. debounced navigation). */
  onCommit?: (key: string, value: number) => void;
  /** Value-label formatter. */
  format?: (value: number) => string;
}

interface TableFilterBarProps {
  search?: SearchConfig;
  filters: FilterGroup[];
  onChange: (key: string, value: string | null) => void;
  onClearAll?: () => void;
  disabled?: boolean;
  /** Optional: number of rows currently shown (after filtering). */
  resultCount?: number;
  /** Optional: total number of rows available before filtering. */
  totalCount?: number;
  /** Optional sort dropdown + direction toggle. */
  sortConfig?: SortConfig;
  /** Optional date-range picker. */
  dateRange?: DateRangeConfig;
  /** Optional numeric sliders. */
  sliders?: SliderConfig[];
  className?: string;
}

const ALL_VALUE = '__all__';

/**
 * Signature active-filter chip treatment — see `.interface-design/system.md`.
 * Uses `text-primary-strong` (not `text-primary`) because chip text sits on the
 * tinted `bg-primary/5` surface, where the base shade fails WCAG AA contrast;
 * the `-strong` shade clears AAA in both light and dark mode.
 */
const CHIP_CLASS =
  'inline-flex items-center gap-1 rounded-full border border-primary/30 bg-primary/5 px-2.5 py-1 text-xs font-medium text-primary-strong';

interface ActiveChip {
  /** Stable key for React + remove handler routing. */
  id: string;
  /** Visible chip text, e.g. "Type: post" or 'Search: "react"'. */
  label: string;
  /** Accessible name for the remove button. */
  removeLabel: string;
  /** Clears just this chip's filter/search. */
  onRemove: () => void;
}

export function TableFilterBar({
  search,
  filters,
  onChange,
  onClearAll,
  disabled,
  resultCount,
  totalCount,
  sortConfig,
  dateRange,
  sliders,
  className,
}: TableFilterBarProps) {
  const activeFilterCount = filters.filter((f) => f.value !== null).length;
  const hasSearch = Boolean(search && search.value.trim().length > 0);

  // The sort value considered "not active": explicit default, else the first option.
  const sortDefaultValue = sortConfig
    ? (sortConfig.defaultValue ?? sortConfig.options[0]?.value)
    : undefined;
  // Only treat sort as active (chip + count + clearable) when a default is
  // actually resolvable — otherwise resetSort() is a no-op and we'd render an
  // unclearable chip / inflate the Clear count (e.g. empty options + no default).
  const hasSortDefault = sortDefaultValue !== undefined;
  const sortActive = Boolean(
    sortConfig && hasSortDefault && sortConfig.value !== sortDefaultValue,
  );

  const dateRangeActive = Boolean(
    dateRange && (dateRange.startValue || dateRange.endValue),
  );

  const activeSliders = (sliders ?? []).filter((s) => s.value > s.min);

  const activeCount =
    activeFilterCount +
    (hasSearch ? 1 : 0) +
    (sortActive ? 1 : 0) +
    (dateRangeActive ? 1 : 0) +
    activeSliders.length;
  const showClear = activeCount > 0;
  const showCount = resultCount !== undefined && totalCount !== undefined;

  const resetSort = () => {
    if (sortConfig && sortDefaultValue !== undefined) {
      sortConfig.onChange(sortDefaultValue);
    }
  };

  const resetSlider = (slider: SliderConfig) => {
    slider.onChange(slider.key, slider.min);
    slider.onCommit?.(slider.key, slider.min);
  };

  const clearDateRange = () => {
    if (dateRange) {
      dateRange.onChange(dateRange.startKey, null, dateRange.endKey, null);
    }
  };

  const handleClearAll = () => {
    if (search) search.onChange('');
    if (onClearAll) {
      onClearAll();
    } else {
      filters.forEach((f) => {
        if (f.value !== null) onChange(f.key, null);
      });
    }
    if (sortActive) resetSort();
    if (dateRangeActive) clearDateRange();
    activeSliders.forEach(resetSlider);
  };

  // Resolve each active filter + active search into a removable chip.
  const chips: ActiveChip[] = [];
  filters.forEach((filter) => {
    if (filter.value === null) return;
    const matched = filter.options.find((o) => o.value === filter.value);
    const optionLabel = matched?.label ?? filter.value;
    chips.push({
      id: `filter-chip-${filter.key}`,
      label: `${filter.label}: ${optionLabel}`,
      removeLabel: `Remove ${filter.label.toLowerCase()} filter`,
      onRemove: () => onChange(filter.key, null),
    });
  });
  if (search && hasSearch) {
    chips.push({
      id: 'search-chip',
      label: `Search: "${search.value.trim()}"`,
      removeLabel: 'Clear search',
      onRemove: () => search.onChange(''),
    });
  }
  if (sortConfig && sortActive) {
    const matched = sortConfig.options.find(
      (o) => o.value === sortConfig.value,
    );
    const optionLabel = matched?.label ?? sortConfig.value;
    chips.push({
      id: 'sort-chip',
      label: `${sortConfig.label}: ${optionLabel}`,
      removeLabel: `Remove ${sortConfig.label.toLowerCase()}`,
      onRemove: resetSort,
    });
  }
  if (dateRange && dateRangeActive) {
    const prefix = dateRange.label ?? 'Dates';
    const { startValue, endValue } = dateRange;
    let rangeLabel: string;
    if (startValue && endValue) {
      rangeLabel = `${startValue} to ${endValue}`;
    } else if (startValue) {
      rangeLabel = `From ${startValue}`;
    } else {
      rangeLabel = `Until ${endValue}`;
    }
    chips.push({
      id: 'date-range-chip',
      label: `${prefix}: ${rangeLabel}`,
      removeLabel: `Remove ${prefix.toLowerCase()}`,
      onRemove: clearDateRange,
    });
  }
  activeSliders.forEach((slider) => {
    const valueLabel = slider.format
      ? slider.format(slider.value)
      : String(slider.value);
    chips.push({
      id: `slider-chip-${slider.key}`,
      label: `${slider.label}: ${valueLabel}`,
      removeLabel: `Remove ${slider.label.toLowerCase()}`,
      onRemove: () => resetSlider(slider),
    });
  });

  return (
    <div
      className={cn(
        'mb-4 flex flex-col gap-3 rounded-lg border bg-card p-3',
        className,
      )}
      role="region"
      aria-label="Table filters"
    >
      <div className="flex flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-center">
        {search && (
          <form onSubmit={search.onSubmit} className="min-w-[16rem] flex-1">
            <label className="sr-only" htmlFor="table-filter-search">
              Search
            </label>
            <div className="relative">
              <Search
                className="absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground"
                aria-hidden="true"
              />
              <Input
                id="table-filter-search"
                ref={search.inputRef}
                type="search"
                placeholder={search.placeholder ?? 'Search…'}
                value={search.value}
                onChange={(e) => search.onChange(e.target.value)}
                className={cn('pl-9', hasSearch && 'pr-9')}
                disabled={disabled}
              />
              {hasSearch && (
                <button
                  type="button"
                  onClick={() => search.onChange('')}
                  disabled={disabled}
                  aria-label="Clear search"
                  className="absolute right-1.5 top-1/2 flex -translate-y-1/2 items-center justify-center rounded-full p-1.5 text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50"
                >
                  <X className="size-4" aria-hidden="true" />
                </button>
              )}
            </div>
          </form>
        )}

        {filters.length > 0 && (
          <div className="flex items-center gap-2 text-xs font-medium text-muted-foreground">
            <Filter className="size-3.5" aria-hidden="true" />
            <span className="sm:hidden">Filters</span>
          </div>
        )}

        {filters.map((filter) => {
          const isActive = filter.value !== null;
          const allLabel = filter.allLabel ?? `All ${filter.label.toLowerCase()}`;
          const triggerId = `filter-${filter.key}`;
          return (
            <Select
              key={filter.key}
              value={filter.value ?? ALL_VALUE}
              onValueChange={(next) =>
                onChange(filter.key, next === ALL_VALUE ? null : next)
              }
              disabled={disabled}
            >
              <SelectTrigger
                id={triggerId}
                className={cn(
                  'min-w-[10rem] sm:w-auto',
                  isActive && 'border-primary text-primary-strong ring-1 ring-primary/30',
                )}
                aria-label={`Filter by ${filter.label.toLowerCase()}`}
              >
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value={ALL_VALUE}>
                  {filter.allCount !== undefined
                    ? `${allLabel} (${filter.allCount})`
                    : allLabel}
                </SelectItem>
                {filter.options.map((opt) => (
                  <SelectItem key={opt.value} value={opt.value}>
                    {opt.count !== undefined ? `${opt.label} (${opt.count})` : opt.label}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
          );
        })}

        {sortConfig && (
          <div className="flex items-center gap-1.5">
            <Select
              value={sortConfig.value}
              onValueChange={(next) => sortConfig.onChange(next)}
              disabled={disabled}
            >
              <SelectTrigger
                id={`sort-${sortConfig.key}`}
                className={cn(
                  'min-w-[10rem] sm:w-auto',
                  sortActive &&
                    'border-primary text-primary-strong ring-1 ring-primary/30',
                )}
                aria-label={`${sortConfig.label} by`}
              >
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                {sortConfig.options.map((opt) => (
                  <SelectItem key={opt.value} value={opt.value}>
                    {opt.label}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
            {sortConfig.direction && (
              <button
                type="button"
                onClick={sortConfig.direction.onToggle}
                disabled={disabled}
                aria-label={`Toggle sort direction, currently ${
                  sortConfig.direction.value === 'asc' ? 'ascending' : 'descending'
                }`}
                className="flex items-center justify-center rounded-md border p-2 text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50"
              >
                <ArrowUpDown
                  className={cn(
                    'size-4',
                    sortConfig.direction.value === 'asc' && 'rotate-180',
                  )}
                  aria-hidden="true"
                />
              </button>
            )}
          </div>
        )}

        {dateRange && (
          <div className="flex items-center gap-1.5">
            <label className="sr-only" htmlFor={`date-${dateRange.startKey}`}>
              {dateRange.label ?? 'Dates'} from
            </label>
            <Input
              id={`date-${dateRange.startKey}`}
              type="date"
              value={dateRange.startValue ?? ''}
              onChange={(e) =>
                dateRange.onChange(
                  dateRange.startKey,
                  e.target.value || null,
                  dateRange.endKey,
                  dateRange.endValue,
                )
              }
              disabled={disabled}
              className="w-auto min-w-[8.5rem]"
            />
            <span className="text-xs text-muted-foreground" aria-hidden="true">
              to
            </span>
            <label className="sr-only" htmlFor={`date-${dateRange.endKey}`}>
              {dateRange.label ?? 'Dates'} until
            </label>
            <Input
              id={`date-${dateRange.endKey}`}
              type="date"
              value={dateRange.endValue ?? ''}
              onChange={(e) =>
                dateRange.onChange(
                  dateRange.startKey,
                  dateRange.startValue,
                  dateRange.endKey,
                  e.target.value || null,
                )
              }
              disabled={disabled}
              className="w-auto min-w-[8.5rem]"
            />
          </div>
        )}

        {(sliders ?? []).map((slider) => {
          const valueLabel = slider.format
            ? slider.format(slider.value)
            : String(slider.value);
          return (
            <div
              key={slider.key}
              className="flex min-w-[12rem] flex-col gap-1"
            >
              <div className="flex items-center justify-between text-xs font-medium text-muted-foreground">
                <label htmlFor={`slider-${slider.key}`}>{slider.label}</label>
                <span className="text-foreground">{valueLabel}</span>
              </div>
              <Slider
                id={`slider-${slider.key}`}
                aria-label={slider.label}
                value={[slider.value]}
                min={slider.min}
                max={slider.max}
                step={slider.step}
                disabled={disabled}
                onValueChange={(vals) => slider.onChange(slider.key, vals[0])}
                onValueCommit={(vals) =>
                  slider.onCommit?.(slider.key, vals[0])
                }
              />
            </div>
          );
        })}

        {showClear && (
          <Button
            type="button"
            variant="ghost"
            size="sm"
            onClick={handleClearAll}
            disabled={disabled}
            className="text-muted-foreground hover:text-foreground sm:ml-auto"
          >
            <X className="mr-1 size-3.5" aria-hidden="true" />
            Clear ({activeCount})
          </Button>
        )}
      </div>

      {(chips.length > 0 || showCount) && (
        <div className="flex flex-wrap items-center gap-2">
          {chips.map((chip) => (
            <span key={chip.id} className={CHIP_CLASS} aria-label={chip.label}>
              {chip.label}
              <button
                type="button"
                onClick={chip.onRemove}
                disabled={disabled}
                aria-label={chip.removeLabel}
                className="-mr-1 flex items-center justify-center rounded-full p-1.5 transition-colors hover:bg-primary/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50"
              >
                <X className="size-4" aria-hidden="true" />
              </button>
            </span>
          ))}
          {showCount && (
            <span className="ml-auto text-xs text-muted-foreground">
              Showing {resultCount} of {totalCount}
            </span>
          )}
        </div>
      )}
    </div>
  );
}
