// src/components/ui/design-toolbar.tsx — shared design-toolbar kit (ELEVA-87).
// Generic primitives extracted from the keywords reservoir toolbar (design
// ELEVA-79): panel shell, wide search with ⌘K, facet dropdowns with active
// state + count badge, active-filter chips row, meta row (results count +
// compact pagination), numeric-range popover and columns-visibility menu.
// Page toolbars compose these with their own domain facets/labels.
"use client";

import type {
  ColumnFiltersState,
  VisibilityState,
} from "@tanstack/react-table";
import {
  ChevronDownIcon,
  ChevronLeftIcon,
  ChevronRightIcon,
  Columns3Icon,
  FilterIcon,
  SearchIcon,
  SlidersHorizontalIcon,
  XIcon,
} from "lucide-react";
import type React from "react";
import { useEffect, useRef } from "react";
import { Button } from "@/components/ui/button";
import {
  DropdownMenu,
  DropdownMenuCheckboxItem,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuRadioGroup,
  DropdownMenuRadioItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover";
import { cn } from "@/lib/utils";

/* ------------------------------------------------------------------ */
/* ColumnFiltersState helpers (single-value facets stored as string[]) */
/* ------------------------------------------------------------------ */

/** Read a single-value facet (TanStack stores it as string[]); "all" = unset. */
export const facetValue = (filters: ColumnFiltersState, id: string): string =>
  ((filters.find((f) => f.id === id)?.value as string[]) ?? [])[0] ?? "all";

/** Set or clear a single-value facet stored as string[]. */
export function setFacet(
  filters: ColumnFiltersState,
  id: string,
  value: string,
): ColumnFiltersState {
  const rest = filters.filter((f) => f.id !== id);
  if (!value || value === "all") return rest;
  return [...rest, { id, value: [value] }];
}

/** Read a multi-value facet as string[] (TanStack stores facet values as string[]). */
export const facetValues = (
  filters: ColumnFiltersState,
  id: string,
): string[] => (filters.find((f) => f.id === id)?.value as string[]) ?? [];

/** Toggle one value of a multi-value facet stored as string[]. Empty → cleared. */
export function toggleFacet(
  filters: ColumnFiltersState,
  id: string,
  value: string,
): ColumnFiltersState {
  const current = facetValues(filters, id);
  const next = current.includes(value)
    ? current.filter((v) => v !== value)
    : [...current, value];
  const rest = filters.filter((f) => f.id !== id);
  return next.length ? [...rest, { id, value: next }] : rest;
}

/** Clear every value of a facet. */
export const clearFacet = (
  filters: ColumnFiltersState,
  id: string,
): ColumnFiltersState => filters.filter((f) => f.id !== id);

export type RangeValue = [number | undefined, number | undefined];
export interface RangeDef {
  id: string;
  label: string;
}

export function rangeValue(
  filters: ColumnFiltersState,
  id: string,
): RangeValue {
  return (
    (filters.find((f) => f.id === id)?.value as RangeValue) ?? [
      undefined,
      undefined,
    ]
  );
}

export function setRange(
  filters: ColumnFiltersState,
  id: string,
  idx: 0 | 1,
  raw: string,
): ColumnFiltersState {
  const next = [...rangeValue(filters, id)] as RangeValue;
  next[idx] = raw === "" ? undefined : Number(raw);
  const rest = filters.filter((f) => f.id !== id);
  if (next[0] == null && next[1] == null) return rest;
  return [...rest, { id, value: next }];
}

export const activeRangeCount = (
  filters: ColumnFiltersState,
  ranges: RangeDef[],
): number => ranges.filter((r) => filters.some((f) => f.id === r.id)).length;

/** Compact chip-style label for an active range filter. */
export function formatRange([min, max]: RangeValue): string {
  if (min != null && max != null) return `${min} – ${max}`;
  if (min != null) return `≥ ${min}`;
  return `≤ ${max}`;
}

/* ------------------------------------------------------------------ */
/* panel shell + layout atoms                                          */
/* ------------------------------------------------------------------ */

/** Rounded panel shell the toolbar / chips / meta / table rows compose into. */
export function ToolbarPanel({
  className,
  ...props
}: React.ComponentProps<"div">) {
  return (
    <div
      className={cn("rounded-2xl border bg-card/30", className)}
      {...props}
    />
  );
}

/** Top toolbar row of the panel (search + facets left, actions right). */
export function ToolbarRow({
  className,
  ...props
}: React.ComponentProps<"div">) {
  return (
    <div
      className={cn(
        "flex flex-wrap items-center gap-2.5 border-b p-4 lg:flex-nowrap",
        className,
      )}
      {...props}
    />
  );
}

/** Vertical hairline divider between toolbar groups. */
export function ToolbarDivider({ className }: { className?: string }) {
  return (
    <div
      className={cn("hidden h-6 w-px shrink-0 bg-border lg:block", className)}
    />
  );
}

/* ------------------------------------------------------------------ */
/* search                                                              */
/* ------------------------------------------------------------------ */

/**
 * Permanent wide search input with leading icon and ⌘K shortcut. The global
 * keydown binding is opt-out (`shortcut={false}`) so two mounted instances
 * never compete for the same key.
 */
export function ToolbarSearch({
  value,
  onChange,
  placeholder,
  ariaLabel,
  shortcut = true,
  className,
}: {
  value: string;
  onChange: (v: string) => void;
  placeholder: string;
  ariaLabel: string;
  /** Bind ⌘K / Ctrl+K to focus this field (default true). */
  shortcut?: boolean;
  className?: string;
}) {
  const searchRef = useRef<HTMLInputElement>(null);

  // ⌘K focuses the search field (no global command palette in the app).
  useEffect(() => {
    if (!shortcut) return;
    function onKey(e: KeyboardEvent) {
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
        e.preventDefault();
        searchRef.current?.focus();
      }
    }
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, [shortcut]);

  return (
    <div
      className={cn(
        "relative w-full min-w-[180px] max-w-[340px] flex-1 basis-[220px]",
        className,
      )}
    >
      <SearchIcon
        className="-translate-y-1/2 absolute top-1/2 left-3 size-4 text-muted-foreground"
        aria-hidden
      />
      <Input
        ref={searchRef}
        placeholder={placeholder}
        value={value}
        onChange={(e) => onChange(e.target.value)}
        className={cn(
          "h-10 rounded-[11px] bg-muted/30 pl-9 text-[13.5px] focus-visible:border-blue-500/55 focus-visible:ring-[3px] focus-visible:ring-blue-500/15",
          shortcut ? "pr-12" : "pr-3",
        )}
        aria-label={ariaLabel}
      />
      {shortcut ? (
        <kbd className="-translate-y-1/2 pointer-events-none absolute top-1/2 right-2.5 rounded-md border bg-muted px-1.5 py-0.5 font-semibold text-[11px] text-muted-foreground">
          ⌘K
        </kbd>
      ) : null}
    </div>
  );
}

/* ------------------------------------------------------------------ */
/* facet triggers + dropdowns                                          */
/* ------------------------------------------------------------------ */

/** Blue count badge shown inside an active facet trigger. */
export function FacetCountBadge({ count }: { count: number }) {
  return (
    <span className="grid h-4 min-w-4 place-items-center rounded-full bg-blue-600 px-1 font-bold text-[10.5px] text-white">
      {count}
    </span>
  );
}

/** Design-styled filter trigger (hairline border, blue active state). */
export function FilterTrigger({
  active,
  children,
  ...props
}: React.ComponentProps<"button"> & { active?: boolean }) {
  return (
    <button
      type="button"
      {...props}
      className={cn(
        "inline-flex h-10 items-center gap-1.5 whitespace-nowrap rounded-[11px] border bg-card px-3 font-medium text-[13px] text-muted-foreground transition-colors",
        "hover:bg-accent hover:text-foreground",
        active &&
          "border-blue-500/40 bg-blue-500/10 text-blue-700 hover:bg-blue-500/15 hover:text-blue-700 dark:text-blue-200 dark:hover:text-blue-100",
        props.className,
      )}
    >
      {children}
      <ChevronDownIcon
        className={cn(
          "size-3.5 text-muted-foreground/70",
          active && "text-blue-500",
        )}
        aria-hidden
      />
    </button>
  );
}

/**
 * Inner items of a single-select facet menu: « Tous » radio + one radio per
 * option. Must be rendered inside a radix `DropdownMenuContent` (it relies on
 * the DropdownMenu radio-group context). Shared by {@link FacetDropdown} and
 * the reservoir/research `FacetRow` panel.
 */
export function FacetMenuContent({
  allLabel,
  value,
  options,
  onChange,
}: {
  allLabel: string;
  value: string;
  options: { value: string; label: React.ReactNode }[];
  onChange: (v: string) => void;
}) {
  return (
    <DropdownMenuRadioGroup value={value} onValueChange={onChange}>
      <DropdownMenuRadioItem value="all">{allLabel}</DropdownMenuRadioItem>
      <DropdownMenuSeparator />
      {options.map((o) => (
        <DropdownMenuRadioItem key={o.value} value={o.value}>
          {o.label}
        </DropdownMenuRadioItem>
      ))}
    </DropdownMenuRadioGroup>
  );
}

/** Single-select facet dropdown with active state + count badge. */
export function FacetDropdown({
  label,
  allLabel,
  value,
  options,
  onChange,
  ariaLabel,
  icon,
}: {
  label: string;
  allLabel: string;
  value: string;
  options: { value: string; label: React.ReactNode }[];
  onChange: (v: string) => void;
  ariaLabel: string;
  /** Optional leading icon inside the trigger. */
  icon?: React.ReactNode;
}) {
  const active = value !== "all";
  const current = options.find((o) => o.value === value);
  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <FilterTrigger active={active} aria-label={ariaLabel}>
          {icon}
          {active && current ? (
            <>
              <span className="font-normal opacity-80">{label} :</span>
              <span className="font-semibold">{current.label}</span>
              <FacetCountBadge count={1} />
            </>
          ) : (
            label
          )}
        </FilterTrigger>
      </DropdownMenuTrigger>
      <DropdownMenuContent
        align="start"
        className="max-h-80 w-52 overflow-y-auto"
      >
        <FacetMenuContent
          allLabel={allLabel}
          value={value}
          options={options}
          onChange={onChange}
        />
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

/**
 * Inner items of a multi-select facet menu: « Tous » (clears) + one checkbox
 * per option. Must be rendered inside a radix `DropdownMenuContent`. Items keep
 * the menu open on select so several values toggle in one pass. Shared by
 * {@link MultiFacetDropdown} and the reservoir/research `FacetRow` panel.
 */
export function MultiFacetMenuContent({
  allLabel,
  values,
  options,
  onToggle,
  onClear,
}: {
  allLabel: string;
  values: string[];
  options: { value: string; label: React.ReactNode }[];
  onToggle: (v: string) => void;
  onClear: () => void;
}) {
  const active = values.length > 0;
  return (
    <>
      <DropdownMenuCheckboxItem
        checked={!active}
        onCheckedChange={() => onClear()}
        onSelect={(e) => e.preventDefault()}
      >
        {allLabel}
      </DropdownMenuCheckboxItem>
      <DropdownMenuSeparator />
      {options.map((o) => (
        <DropdownMenuCheckboxItem
          key={o.value}
          checked={values.includes(o.value)}
          onCheckedChange={() => onToggle(o.value)}
          onSelect={(e) => e.preventDefault()}
        >
          {o.label}
        </DropdownMenuCheckboxItem>
      ))}
    </>
  );
}

/** Multi-select facet dropdown (checkboxes): OR within the facet, count badge.
 *  The menu stays open on select so several values can be toggled in one pass. */
export function MultiFacetDropdown({
  label,
  allLabel,
  values,
  options,
  onToggle,
  onClear,
  ariaLabel,
  icon,
}: {
  label: string;
  allLabel: string;
  values: string[];
  options: { value: string; label: React.ReactNode }[];
  onToggle: (v: string) => void;
  onClear: () => void;
  ariaLabel: string;
  /** Optional leading icon inside the trigger. */
  icon?: React.ReactNode;
}) {
  const active = values.length > 0;
  const selected = options.filter((o) => values.includes(o.value));
  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <FilterTrigger active={active} aria-label={ariaLabel}>
          {icon}
          {active ? (
            <>
              <span className="font-normal opacity-80">{label} :</span>
              <span className="font-semibold">
                {selected.length === 1
                  ? selected[0].label
                  : `${values.length} sélectionnés`}
              </span>
              <FacetCountBadge count={values.length} />
            </>
          ) : (
            label
          )}
        </FilterTrigger>
      </DropdownMenuTrigger>
      <DropdownMenuContent
        align="start"
        className="max-h-80 w-52 overflow-y-auto"
      >
        <MultiFacetMenuContent
          allLabel={allLabel}
          values={values}
          options={options}
          onToggle={onToggle}
          onClear={onClear}
        />
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

/**
 * Min/max input pairs for every numeric range column. Renders a bare fragment
 * of labelled input rows (no popover shell) so it can drop into either the
 * standalone {@link ToolbarRangesPopover} or a `FacetRow` popover.
 */
export function RangesFields({
  ranges,
  filters,
  onFilters,
}: {
  ranges: RangeDef[];
  filters: ColumnFiltersState;
  onFilters: (next: ColumnFiltersState) => void;
}) {
  return (
    <>
      {ranges.map((r) => {
        const [min, max] = rangeValue(filters, r.id);
        return (
          <div key={r.id} className="space-y-1">
            <span className="font-medium text-sm">{r.label}</span>
            <div className="flex items-center gap-2">
              <Input
                type="number"
                placeholder="min"
                value={min ?? ""}
                onChange={(e) =>
                  onFilters(setRange(filters, r.id, 0, e.target.value))
                }
                aria-label={`${r.label} minimum`}
              />
              <span className="text-muted-foreground">–</span>
              <Input
                type="number"
                placeholder="max"
                value={max ?? ""}
                onChange={(e) =>
                  onFilters(setRange(filters, r.id, 1, e.target.value))
                }
                aria-label={`${r.label} maximum`}
              />
            </div>
          </div>
        );
      })}
    </>
  );
}

/** « Plages » popover: min/max inputs per numeric range column. */
export function ToolbarRangesPopover({
  ranges,
  filters,
  onFilters,
  label = "Plages",
}: {
  ranges: RangeDef[];
  filters: ColumnFiltersState;
  onFilters: (next: ColumnFiltersState) => void;
  label?: string;
}) {
  const count = activeRangeCount(filters, ranges);
  return (
    <Popover>
      <PopoverTrigger asChild>
        <FilterTrigger
          active={count > 0}
          aria-label="Filtres de plages numériques"
        >
          <SlidersHorizontalIcon
            className="size-[15px] text-muted-foreground/70"
            aria-hidden
          />
          {label}
          {count > 0 ? <FacetCountBadge count={count} /> : null}
        </FilterTrigger>
      </PopoverTrigger>
      <PopoverContent className="w-72 space-y-3">
        <RangesFields ranges={ranges} filters={filters} onFilters={onFilters} />
      </PopoverContent>
    </Popover>
  );
}

/** Columns-visibility icon button + checkbox menu. */
export function ToolbarColumnsMenu({
  columns,
  columnVisibility,
  onColumnVisibility,
}: {
  columns: { id: string; label: string }[];
  columnVisibility: VisibilityState;
  onColumnVisibility: (next: VisibilityState) => void;
}) {
  const isVisible = (id: string) => columnVisibility[id] !== false;
  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button
          variant="outline"
          size="icon"
          className="size-10 rounded-[11px] text-muted-foreground hover:text-foreground"
          aria-label="Afficher ou masquer des colonnes"
          title="Colonnes"
        >
          <Columns3Icon className="size-4" aria-hidden />
        </Button>
      </DropdownMenuTrigger>
      <DropdownMenuContent
        align="end"
        className="max-h-80 w-48 overflow-y-auto"
      >
        <DropdownMenuLabel>Colonnes</DropdownMenuLabel>
        <DropdownMenuSeparator />
        {columns.map((c) => (
          <DropdownMenuCheckboxItem
            key={c.id}
            checked={isVisible(c.id)}
            onCheckedChange={(v) =>
              onColumnVisibility({ ...columnVisibility, [c.id]: !!v })
            }
            onSelect={(e) => e.preventDefault()}
          >
            {c.label}
          </DropdownMenuCheckboxItem>
        ))}
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

/* ------------------------------------------------------------------ */
/* active filter chips row                                             */
/* ------------------------------------------------------------------ */

export interface ToolbarFilterChip {
  key: string;
  category: string;
  value: string;
  onRemove: () => void;
}

function Chip({ category, value, onRemove }: Omit<ToolbarFilterChip, "key">) {
  return (
    <span className="inline-flex h-7 items-center gap-1.5 rounded-lg border border-blue-500/30 bg-blue-500/10 py-0 pr-1 pl-2.5 font-medium text-[12px] text-blue-700 dark:text-blue-200">
      {category}{" "}
      <b className="font-semibold text-blue-900 dark:text-white">{value}</b>
      <button
        type="button"
        onClick={onRemove}
        aria-label={`Retirer le filtre ${category} ${value}`}
        className="grid size-[18px] place-items-center rounded-[5px] text-blue-500 transition-colors hover:bg-blue-500/25 hover:text-blue-900 dark:hover:text-white"
      >
        <XIcon className="size-3" aria-hidden />
      </button>
    </span>
  );
}

/** « Filtres actifs » chips row + « Tout effacer ». Hidden when no chips. */
export function ActiveFilterChips({
  chips,
  onClearAll,
}: {
  chips: ToolbarFilterChip[];
  onClearAll: () => void;
}) {
  if (chips.length === 0) return null;
  return (
    <div className="flex flex-wrap items-center gap-2 border-b bg-foreground/[0.012] px-4 py-2.5">
      <span className="inline-flex items-center gap-1.5 font-semibold text-[11px] text-muted-foreground/70 uppercase tracking-[0.06em]">
        <FilterIcon className="size-3.5" aria-hidden />
        Filtres actifs
      </span>
      {chips.map((c) => (
        <Chip
          key={c.key}
          category={c.category}
          value={c.value}
          onRemove={c.onRemove}
        />
      ))}
      <button
        type="button"
        onClick={onClearAll}
        className="rounded-md px-1.5 py-1 font-semibold text-[12px] text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
      >
        Tout effacer
      </button>
    </div>
  );
}

/* ------------------------------------------------------------------ */
/* meta + pagination row                                               */
/* ------------------------------------------------------------------ */

export interface ToolbarPagination {
  pageIndex: number;
  pageCount: number;
  pageSize: number;
  pageSizeOptions: number[];
  onPageSizeChange: (n: number) => void;
  onPrevious: () => void;
  onNext: () => void;
}

/**
 * « N résultats · M filtres » meta row with optional compact pagination
 * (« 50 / page · ‹ Précédent · Page x/y · Suivant › »). Omit `pagination`
 * for non-paginated views (e.g. a matrix).
 */
export function ToolbarMetaRow({
  resultCount,
  filterCount,
  pagination,
}: {
  resultCount: number;
  filterCount: number;
  pagination?: ToolbarPagination;
}) {
  return (
    <div className="flex flex-wrap items-center justify-between gap-3 px-4 py-3">
      <div className="text-[13px] text-muted-foreground">
        <b className="font-semibold text-foreground">
          {resultCount.toLocaleString("fr-FR")}
        </b>{" "}
        résultat{resultCount > 1 ? "s" : ""}
        {filterCount > 0 ? (
          <>
            {" "}
            <span className="text-muted-foreground/50">·</span>{" "}
            <span>
              {filterCount} filtre{filterCount > 1 ? "s" : ""}
            </span>
          </>
        ) : null}
      </div>
      {pagination ? (
        <div className="flex items-center gap-2">
          <DropdownMenu>
            <DropdownMenuTrigger asChild>
              <button
                type="button"
                className="inline-flex h-[34px] items-center gap-1.5 rounded-[9px] border bg-card px-2.5 font-medium text-[12.5px] text-muted-foreground transition-colors hover:text-foreground"
                aria-label="Résultats par page"
              >
                {pagination.pageSize} / page
                <ChevronDownIcon
                  className="size-3.5 text-muted-foreground/70"
                  aria-hidden
                />
              </button>
            </DropdownMenuTrigger>
            <DropdownMenuContent align="end" className="w-32">
              {pagination.pageSizeOptions.map((n) => (
                <DropdownMenuItem
                  key={n}
                  onSelect={() => pagination.onPageSizeChange(n)}
                  className={cn(
                    "text-[12.5px]",
                    n === pagination.pageSize
                      ? "font-semibold text-foreground"
                      : "text-muted-foreground",
                  )}
                >
                  {n} / page
                </DropdownMenuItem>
              ))}
            </DropdownMenuContent>
          </DropdownMenu>

          <div className="inline-flex items-center overflow-hidden rounded-[9px] border bg-card">
            <button
              type="button"
              onClick={pagination.onPrevious}
              disabled={pagination.pageIndex <= 0}
              className="inline-flex h-[34px] items-center gap-1 px-3 font-medium text-[12.5px] text-muted-foreground transition-colors hover:bg-accent hover:text-foreground disabled:cursor-not-allowed disabled:text-muted-foreground/40 disabled:hover:bg-transparent"
            >
              <ChevronLeftIcon className="size-3.5" aria-hidden />
              Précédent
            </button>
            <span className="flex h-[34px] items-center gap-1 border-x px-3.5 text-[12.5px] text-muted-foreground">
              Page{" "}
              <b className="font-semibold text-foreground">
                {pagination.pageIndex + 1}
              </b>{" "}
              / {Math.max(pagination.pageCount, 1)}
            </span>
            <button
              type="button"
              onClick={pagination.onNext}
              disabled={pagination.pageIndex >= pagination.pageCount - 1}
              className="inline-flex h-[34px] items-center gap-1 px-3 font-medium text-[12.5px] text-muted-foreground transition-colors hover:bg-accent hover:text-foreground disabled:cursor-not-allowed disabled:text-muted-foreground/40 disabled:hover:bg-transparent"
            >
              Suivant
              <ChevronRightIcon className="size-3.5" aria-hidden />
            </button>
          </div>
        </div>
      ) : null}
    </div>
  );
}
