import * as React from "react";
import {
  ColumnDef,
  ColumnFiltersState,
  ColumnOrderState,
  ColumnPinningState,
  ColumnSizingState,
  flexRender,
  getCoreRowModel,
  getFilteredRowModel,
  getPaginationRowModel,
  getSortedRowModel,
  PaginationState,
  Row,
  SortingState,
  Table as ReactTable,
  useReactTable,
  VisibilityState,
} from "@tanstack/react-table";
import { useVirtualizer } from "@tanstack/react-virtual";
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
import {
  Check,
  ChevronDown,
  ChevronLeft,
  ChevronRight,
  ChevronsLeft,
  ChevronsRight,
  ChevronsUpDown,
  ChevronUp,
  Columns,
  Inbox,
  Pin,
  PinOff,
  Save,
  Trash2,
} from "lucide-react";
import { cn } from "@/lib/utils";
import { Button } from "./button";
import { EmptyState } from "./empty-state";
import { LoadingState } from "./loading-state";

/* ============================================================================
 * DataTable v2 — TanStack Table + react-virtual + Radix dropdowns
 *
 * Features:
 *   - Virtualized rows (auto-enables above `virtualizeAbove` row threshold)
 *   - Sortable columns (asc → desc → unsorted)
 *   - Column resize (drag handle)
 *   - Column visibility toggle (DropdownMenu)
 *   - Column pinning (left/right/unpin) via per-column dropdown
 *   - Pagination footer (optional)
 *   - Saved views (localStorage-backed) — keyed by `tableId`
 *
 * Re-export ColumnDef from @tanstack/react-table so consumers can type their
 * columns with full IntelliSense.
 * ========================================================================== */

export type { ColumnDef } from "@tanstack/react-table";

export type Density = "compact" | "cozy" | "comfortable";

const ROW_HEIGHT: Record<Density, number> = {
  compact: 32,
  cozy: 40,
  comfortable: 48,
};
const CELL_PAD: Record<Density, string> = {
  compact: "px-3 py-1.5",
  cozy: "px-3 py-2",
  comfortable: "px-4 py-3",
};

/* ============================================================
 * Saved views — localStorage persistence
 * ========================================================== */

interface PersistedState {
  columnSizing: ColumnSizingState;
  columnVisibility: VisibilityState;
  columnPinning: ColumnPinningState;
  columnOrder: ColumnOrderState;
  sorting: SortingState;
}

interface SavedView {
  id: string;
  name: string;
  state: PersistedState;
}

interface SavedViewsConfig {
  views: SavedView[];
  activeViewId: string | null;
}

const viewsStorageKey = (tableId: string) => `data-table:${tableId}:views`;
const stateStorageKey = (tableId: string) => `data-table:${tableId}:state`;

function readJSON<T>(key: string): T | null {
  if (typeof window === "undefined") return null;
  try {
    const v = window.localStorage.getItem(key);
    return v ? (JSON.parse(v) as T) : null;
  } catch {
    return null;
  }
}
function writeJSON(key: string, value: unknown) {
  if (typeof window === "undefined") return;
  try {
    window.localStorage.setItem(key, JSON.stringify(value));
  } catch {
    /* ignore quota */
  }
}

/* ============================================================
 * Main component
 * ========================================================== */

export interface DataTableProps<T> {
  /** Unique id used to scope localStorage (saved views, column widths). */
  tableId: string;
  data: T[] | undefined;
  columns: ColumnDef<T, any>[];
  loading?: boolean;
  loadingRows?: number;
  /** Loading variant — defaults to "table" */
  loadingVariant?: "table" | "spinner";
  /** Row stable id getter (default uses row index) */
  getRowId?: (row: T, index: number) => string;
  density?: Density;
  onDensityChange?: (density: Density) => void;
  onRowClick?: (row: T, index: number) => void;
  /** Custom empty state */
  empty?: React.ReactNode;
  /** Error state node (overrides empty when present) */
  error?: React.ReactNode;
  /** Estimated container height — needed for virtualization. */
  height?: number | string;
  /** Row count over which virtualization auto-enables. Default 50. */
  virtualizeAbove?: number;
  /** Force virtualization on/off (overrides threshold) */
  virtualize?: boolean;
  /** Enable pagination footer */
  enablePagination?: boolean;
  pageSize?: number;
  pageSizeOptions?: number[];
  /**
   * Server-side pagination — when true, the table will NOT slice rows itself.
   * Parent owns `pagination` state and supplies a fresh `data` page on change.
   * Must also pass `pageCount` (total pages from API) or `rowCount` (total rows).
   */
  manualPagination?: boolean;
  pageCount?: number;
  rowCount?: number;
  /** Controlled pagination state — required when manualPagination is true */
  paginationState?: PaginationState;
  onPaginationStateChange?: (state: PaginationState) => void;
  /** Column features (default all true) */
  enableColumnResize?: boolean;
  enableColumnVisibility?: boolean;
  enableColumnPinning?: boolean;
  /** Enable saved views UI */
  enableSavedViews?: boolean;
  /** Show density toggle in toolbar */
  enableDensityToggle?: boolean;
  /** Custom toolbar nodes rendered on the left */
  toolbarStart?: React.ReactNode;
  /** Custom toolbar nodes rendered on the right (before built-ins) */
  toolbarEnd?: React.ReactNode;
  /** Row tone — applies a soft background tint */
  rowToneAccessor?: (
    row: T,
  ) => "success" | "warning" | "danger" | "info" | undefined;
  className?: string;
}

const TONE_BG: Record<"success" | "warning" | "danger" | "info", string> = {
  success: "bg-success-subtle/40",
  warning: "bg-warning-subtle/40",
  danger: "bg-danger-subtle/40",
  info: "bg-info-subtle/40",
};

export function DataTable<T>({
  tableId,
  data,
  columns,
  loading,
  loadingRows = 8,
  loadingVariant = "table",
  getRowId,
  density: densityProp,
  onDensityChange,
  onRowClick,
  empty,
  error,
  height = 520,
  virtualizeAbove = 50,
  virtualize,
  enablePagination = false,
  pageSize: pageSizeProp = 50,
  pageSizeOptions = [25, 50, 100, 200],
  manualPagination = false,
  pageCount,
  rowCount,
  paginationState,
  onPaginationStateChange,
  enableColumnResize = true,
  enableColumnVisibility = true,
  enableColumnPinning = true,
  enableSavedViews = true,
  enableDensityToggle = true,
  toolbarStart,
  toolbarEnd,
  rowToneAccessor,
  className,
}: DataTableProps<T>) {
  /* ----------------------- Persisted state ----------------------- */

  const persisted = React.useMemo<Partial<PersistedState>>(
    () => readJSON<PersistedState>(stateStorageKey(tableId)) ?? {},
    [tableId],
  );

  const [sorting, setSorting] = React.useState<SortingState>(
    persisted.sorting ?? [],
  );
  const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>(
    persisted.columnVisibility ?? {},
  );
  const [columnPinning, setColumnPinning] = React.useState<ColumnPinningState>(
    persisted.columnPinning ?? { left: [], right: [] },
  );
  const [columnSizing, setColumnSizing] = React.useState<ColumnSizingState>(
    persisted.columnSizing ?? {},
  );
  const [columnOrder, setColumnOrder] = React.useState<ColumnOrderState>(
    persisted.columnOrder ?? [],
  );
  const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
    [],
  );
  const [internalPagination, setInternalPagination] = React.useState<PaginationState>({
    pageIndex: 0,
    pageSize: pageSizeProp,
  });
  // Controlled when caller passes paginationState (typical for server-side)
  const pagination = paginationState ?? internalPagination;
  const setPagination = (
    updater: PaginationState | ((prev: PaginationState) => PaginationState),
  ) => {
    const next =
      typeof updater === "function" ? updater(pagination) : updater;
    if (onPaginationStateChange) onPaginationStateChange(next);
    else setInternalPagination(next);
  };

  // Uncontrolled internal density (when no parent control)
  const [internalDensity, setInternalDensity] = React.useState<Density>(
    densityProp ?? "cozy",
  );
  const density = densityProp ?? internalDensity;
  const setDensity = (d: Density) => {
    if (onDensityChange) onDensityChange(d);
    else setInternalDensity(d);
  };

  /* ----------------------- Persist on change ----------------------- */
  React.useEffect(() => {
    writeJSON(stateStorageKey(tableId), {
      sorting,
      columnVisibility,
      columnPinning,
      columnSizing,
      columnOrder,
    });
  }, [
    tableId,
    sorting,
    columnVisibility,
    columnPinning,
    columnSizing,
    columnOrder,
  ]);

  /* ----------------------- Table instance ----------------------- */

  const table = useReactTable({
    data: data ?? [],
    columns,
    state: {
      sorting,
      columnVisibility,
      columnPinning,
      columnSizing,
      columnOrder,
      columnFilters,
      pagination: enablePagination ? pagination : undefined,
    },
    getRowId,
    onSortingChange: setSorting,
    onColumnVisibilityChange: setColumnVisibility,
    onColumnPinningChange: setColumnPinning,
    onColumnSizingChange: setColumnSizing,
    onColumnOrderChange: setColumnOrder,
    onColumnFiltersChange: setColumnFilters,
    onPaginationChange: setPagination,
    columnResizeMode: "onChange",
    enableColumnResizing: enableColumnResize,
    enableHiding: enableColumnVisibility,
    enablePinning: enableColumnPinning,
    getCoreRowModel: getCoreRowModel(),
    getSortedRowModel: getSortedRowModel(),
    getFilteredRowModel: getFilteredRowModel(),
    getPaginationRowModel:
      enablePagination && !manualPagination
        ? getPaginationRowModel()
        : undefined,
    manualPagination,
    pageCount:
      manualPagination && typeof pageCount === "number" ? pageCount : undefined,
    rowCount:
      manualPagination && typeof rowCount === "number" ? rowCount : undefined,
  });

  const allRows = table.getRowModel().rows;
  const usingPaginationView = enablePagination && !manualPagination;
  const shouldVirtualize =
    virtualize ?? (!usingPaginationView && allRows.length > virtualizeAbove);

  /* ----------------------- Layout helpers ----------------------- */

  const cellPadClass = CELL_PAD[density];
  const rowH = ROW_HEIGHT[density];

  /* ----------------------- Render ----------------------- */

  return (
    <div className={cn("flex flex-col gap-2", className)}>
      <Toolbar
        table={table}
        tableId={tableId}
        density={density}
        setDensity={setDensity}
        enableSavedViews={enableSavedViews}
        enableColumnVisibility={enableColumnVisibility}
        enableDensityToggle={enableDensityToggle}
        toolbarStart={toolbarStart}
        toolbarEnd={toolbarEnd}
        sorting={sorting}
        setSorting={setSorting}
        columnVisibility={columnVisibility}
        setColumnVisibility={setColumnVisibility}
        columnPinning={columnPinning}
        setColumnPinning={setColumnPinning}
        columnSizing={columnSizing}
        setColumnSizing={setColumnSizing}
        columnOrder={columnOrder}
        setColumnOrder={setColumnOrder}
      />

      <div
        className={cn(
          "relative overflow-hidden rounded-lg border border-border bg-surface",
        )}
      >
        {error ? (
          <div className="px-4">{error}</div>
        ) : loading ? (
          <div className="p-3">
            <LoadingState
              variant={loadingVariant}
              rows={loadingRows}
            />
          </div>
        ) : allRows.length === 0 ? (
          empty ?? (
            <EmptyState
              compact
              icon={Inbox}
              title="No records"
              description="Nothing matches the current filters."
            />
          )
        ) : (
          <TableShell
            table={table}
            allRows={allRows}
            cellPadClass={cellPadClass}
            rowHeight={rowH}
            height={height}
            shouldVirtualize={shouldVirtualize}
            onRowClick={onRowClick}
            rowToneAccessor={rowToneAccessor}
            enableColumnResize={enableColumnResize}
          />
        )}
      </div>

      {enablePagination && !loading && allRows.length > 0 && (
        <Pagination
          table={table}
          pageSizeOptions={pageSizeOptions}
        />
      )}
    </div>
  );
}

/* ============================================================
 * Toolbar — saved views + column visibility + density
 * ========================================================== */

function Toolbar<T>({
  table,
  tableId,
  density,
  setDensity,
  enableSavedViews,
  enableColumnVisibility,
  enableDensityToggle,
  toolbarStart,
  toolbarEnd,
  sorting,
  setSorting,
  columnVisibility,
  setColumnVisibility,
  columnPinning,
  setColumnPinning,
  columnSizing,
  setColumnSizing,
  columnOrder,
  setColumnOrder,
}: {
  table: ReactTable<T>;
  tableId: string;
  density: Density;
  setDensity: (d: Density) => void;
  enableSavedViews: boolean;
  enableColumnVisibility: boolean;
  enableDensityToggle: boolean;
  toolbarStart?: React.ReactNode;
  toolbarEnd?: React.ReactNode;
  sorting: SortingState;
  setSorting: React.Dispatch<React.SetStateAction<SortingState>>;
  columnVisibility: VisibilityState;
  setColumnVisibility: React.Dispatch<React.SetStateAction<VisibilityState>>;
  columnPinning: ColumnPinningState;
  setColumnPinning: React.Dispatch<React.SetStateAction<ColumnPinningState>>;
  columnSizing: ColumnSizingState;
  setColumnSizing: React.Dispatch<React.SetStateAction<ColumnSizingState>>;
  columnOrder: ColumnOrderState;
  setColumnOrder: React.Dispatch<React.SetStateAction<ColumnOrderState>>;
}) {
  const currentState: PersistedState = {
    sorting,
    columnVisibility,
    columnPinning,
    columnSizing,
    columnOrder,
  };

  return (
    <div className="flex flex-wrap items-center gap-2">
      <div className="flex flex-1 items-center gap-2">{toolbarStart}</div>
      <div className="flex items-center gap-1.5">
        {toolbarEnd}
        {enableSavedViews && (
          <ViewsMenu
            tableId={tableId}
            currentState={currentState}
            applyState={(s) => {
              setSorting(s.sorting);
              setColumnVisibility(s.columnVisibility);
              setColumnPinning(s.columnPinning);
              setColumnSizing(s.columnSizing);
              setColumnOrder(s.columnOrder);
            }}
          />
        )}
        {enableColumnVisibility && <ColumnsMenu table={table} />}
        {enableDensityToggle && (
          <DensityMenu density={density} setDensity={setDensity} />
        )}
      </div>
    </div>
  );
}

/* ============================================================
 * ColumnsMenu — toggle visibility + pin
 * ========================================================== */

function ColumnsMenu<T>({ table }: { table: ReactTable<T> }) {
  const cols = table
    .getAllLeafColumns()
    .filter((c) => c.getCanHide() || c.getCanPin());

  return (
    <DropdownMenu.Root>
      <DropdownMenu.Trigger asChild>
        <Button variant="outline" size="sm" className="h-9">
          <Columns className="h-4 w-4" />
          Columns
        </Button>
      </DropdownMenu.Trigger>
      <DropdownMenu.Portal>
        <DropdownMenu.Content
          align="end"
          sideOffset={4}
          className="z-40 w-64 rounded-md border border-border bg-popover p-1 shadow-md animate-fade-in-up"
        >
          <div className="px-2 py-1.5 text-2xs font-semibold uppercase tracking-wider text-muted-foreground">
            Toggle columns
          </div>
          {cols.map((col) => {
            const isPinned = col.getIsPinned();
            const headerLabel =
              typeof col.columnDef.header === "string"
                ? col.columnDef.header
                : col.id;
            return (
              <div
                key={col.id}
                className="flex items-center gap-2 rounded-sm px-2 py-1.5 text-sm hover:bg-accent/60"
              >
                <button
                  type="button"
                  onClick={() => col.toggleVisibility()}
                  className="flex flex-1 items-center gap-2 text-left"
                >
                  <span
                    className={cn(
                      "flex h-4 w-4 items-center justify-center rounded border",
                      col.getIsVisible()
                        ? "border-brand-500 bg-brand-500 text-white"
                        : "border-border",
                    )}
                  >
                    {col.getIsVisible() && <Check className="h-3 w-3" />}
                  </span>
                  <span className="truncate">{headerLabel}</span>
                </button>
                {col.getCanPin() && (
                  <button
                    type="button"
                    onClick={() => {
                      col.pin(isPinned === "left" ? false : "left");
                    }}
                    className={cn(
                      "rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground",
                      isPinned === "left" && "text-brand-600 dark:text-brand-300",
                    )}
                    title={isPinned === "left" ? "Unpin" : "Pin to left"}
                  >
                    {isPinned === "left" ? (
                      <PinOff className="h-3.5 w-3.5" />
                    ) : (
                      <Pin className="h-3.5 w-3.5" />
                    )}
                  </button>
                )}
              </div>
            );
          })}
        </DropdownMenu.Content>
      </DropdownMenu.Portal>
    </DropdownMenu.Root>
  );
}

/* ============================================================
 * DensityMenu
 * ========================================================== */

function DensityMenu({
  density,
  setDensity,
}: {
  density: Density;
  setDensity: (d: Density) => void;
}) {
  return (
    <DropdownMenu.Root>
      <DropdownMenu.Trigger asChild>
        <Button variant="outline" size="sm" className="h-9">
          Density: <span className="font-medium capitalize">{density}</span>
          <ChevronDown className="h-3.5 w-3.5" />
        </Button>
      </DropdownMenu.Trigger>
      <DropdownMenu.Portal>
        <DropdownMenu.Content
          align="end"
          sideOffset={4}
          className="z-40 w-44 rounded-md border border-border bg-popover p-1 shadow-md animate-fade-in-up"
        >
          {(["compact", "cozy", "comfortable"] as const).map((d) => (
            <DropdownMenu.Item
              key={d}
              onSelect={() => setDensity(d)}
              className="flex cursor-pointer items-center justify-between rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent/60 data-[highlighted]:bg-accent/60"
            >
              <span className="capitalize">{d}</span>
              {density === d && <Check className="h-3.5 w-3.5" />}
            </DropdownMenu.Item>
          ))}
        </DropdownMenu.Content>
      </DropdownMenu.Portal>
    </DropdownMenu.Root>
  );
}

/* ============================================================
 * ViewsMenu — saved views
 * ========================================================== */

function ViewsMenu({
  tableId,
  currentState,
  applyState,
}: {
  tableId: string;
  currentState: PersistedState;
  applyState: (s: PersistedState) => void;
}) {
  const [config, setConfig] = React.useState<SavedViewsConfig>(() =>
    readJSON<SavedViewsConfig>(viewsStorageKey(tableId)) ?? {
      views: [],
      activeViewId: null,
    },
  );

  const persist = (next: SavedViewsConfig) => {
    setConfig(next);
    writeJSON(viewsStorageKey(tableId), next);
  };

  const activeView =
    config.views.find((v) => v.id === config.activeViewId) ?? null;

  const saveCurrent = () => {
    const name = window.prompt("Name for this view");
    if (!name?.trim()) return;
    const id = `${Date.now().toString(36)}`;
    const next: SavedViewsConfig = {
      views: [...config.views, { id, name: name.trim(), state: currentState }],
      activeViewId: id,
    };
    persist(next);
  };

  const apply = (view: SavedView) => {
    applyState(view.state);
    persist({ ...config, activeViewId: view.id });
  };

  const remove = (id: string) => {
    if (!confirm("Delete this view?")) return;
    persist({
      views: config.views.filter((v) => v.id !== id),
      activeViewId: config.activeViewId === id ? null : config.activeViewId,
    });
  };

  return (
    <DropdownMenu.Root>
      <DropdownMenu.Trigger asChild>
        <Button variant="outline" size="sm" className="h-9 max-w-[180px]">
          <Save className="h-3.5 w-3.5" />
          <span className="truncate">{activeView?.name ?? "Default view"}</span>
          <ChevronDown className="h-3.5 w-3.5" />
        </Button>
      </DropdownMenu.Trigger>
      <DropdownMenu.Portal>
        <DropdownMenu.Content
          align="end"
          sideOffset={4}
          className="z-40 w-64 rounded-md border border-border bg-popover p-1 shadow-md animate-fade-in-up"
        >
          <div className="flex items-center justify-between px-2 py-1.5 text-2xs font-semibold uppercase tracking-wider text-muted-foreground">
            Saved views
          </div>
          {config.views.length === 0 && (
            <div className="px-2 py-3 text-center text-xs text-muted-foreground">
              No saved views yet
            </div>
          )}
          {config.views.map((view) => (
            <div
              key={view.id}
              className="flex items-center gap-1 rounded-sm px-1 py-0.5 hover:bg-accent/60"
            >
              <button
                type="button"
                onClick={() => apply(view)}
                className="flex flex-1 items-center gap-2 px-2 py-1 text-left text-sm"
              >
                {config.activeViewId === view.id ? (
                  <Check className="h-3.5 w-3.5 text-brand-600 dark:text-brand-300" />
                ) : (
                  <span className="h-3.5 w-3.5" />
                )}
                <span className="truncate">{view.name}</span>
              </button>
              <button
                type="button"
                onClick={() => remove(view.id)}
                className="rounded p-1 text-muted-foreground hover:bg-muted hover:text-danger"
                aria-label={`Delete view ${view.name}`}
              >
                <Trash2 className="h-3 w-3" />
              </button>
            </div>
          ))}
          <DropdownMenu.Separator className="my-1 h-px bg-border" />
          <DropdownMenu.Item
            onSelect={saveCurrent}
            className="flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent/60 data-[highlighted]:bg-accent/60"
          >
            <Save className="h-3.5 w-3.5" />
            Save current as new view…
          </DropdownMenu.Item>
        </DropdownMenu.Content>
      </DropdownMenu.Portal>
    </DropdownMenu.Root>
  );
}

/* ============================================================
 * TableShell — header + body with optional virtualization
 * ========================================================== */

function TableShell<T>({
  table,
  allRows,
  cellPadClass,
  rowHeight,
  height,
  shouldVirtualize,
  onRowClick,
  rowToneAccessor,
  enableColumnResize,
}: {
  table: ReactTable<T>;
  allRows: Row<T>[];
  cellPadClass: string;
  rowHeight: number;
  height: number | string;
  shouldVirtualize: boolean;
  onRowClick?: (row: T, index: number) => void;
  rowToneAccessor?: (row: T) => "success" | "warning" | "danger" | "info" | undefined;
  enableColumnResize: boolean;
}) {
  const parentRef = React.useRef<HTMLDivElement>(null);

  const virtualizer = useVirtualizer({
    count: allRows.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => rowHeight,
    overscan: 12,
    enabled: shouldVirtualize,
  });

  const totalSize = shouldVirtualize ? virtualizer.getTotalSize() : "auto";
  const items = shouldVirtualize ? virtualizer.getVirtualItems() : null;

  return (
    <div
      ref={parentRef}
      className="relative overflow-auto"
      style={{ height }}
    >
      <table
        className="w-full border-collapse text-sm tabular-nums"
        style={{
          tableLayout: "fixed",
          width: table.getCenterTotalSize() + getPinnedWidth(table, "left") + getPinnedWidth(table, "right"),
        }}
      >
        <colgroup>
          {table.getVisibleLeafColumns().map((col) => (
            <col key={col.id} style={{ width: col.getSize() }} />
          ))}
        </colgroup>
        <thead className="sticky top-0 z-[2] bg-background-subtle text-xs uppercase tracking-wide text-muted-foreground">
          {table.getHeaderGroups().map((headerGroup) => (
            <tr key={headerGroup.id}>
              {headerGroup.headers.map((header) => {
                const col = header.column;
                const isSorted = col.getIsSorted();
                const canSort = col.getCanSort();
                const align =
                  (col.columnDef.meta as { align?: "left" | "right" | "center" } | undefined)
                    ?.align ?? "left";
                const alignClass =
                  align === "right"
                    ? "text-right"
                    : align === "center"
                      ? "text-center"
                      : "text-left";
                const pinned = col.getIsPinned();
                return (
                  <th
                    key={header.id}
                    scope="col"
                    style={{
                      width: header.getSize(),
                      ...getPinStyle(col, "header"),
                    }}
                    aria-sort={
                      isSorted === "asc"
                        ? "ascending"
                        : isSorted === "desc"
                          ? "descending"
                          : canSort
                            ? "none"
                            : undefined
                    }
                    className={cn(
                      "relative border-b border-border font-medium",
                      cellPadClass,
                      alignClass,
                      canSort && "cursor-pointer select-none hover:text-foreground",
                      pinned && "bg-background-subtle",
                    )}
                    onClick={canSort ? col.getToggleSortingHandler() : undefined}
                  >
                    {header.isPlaceholder ? null : (
                      <span
                        className={cn(
                          "inline-flex items-center gap-1",
                          align === "right" && "flex-row-reverse",
                        )}
                      >
                        {flexRender(col.columnDef.header, header.getContext())}
                        {canSort && (
                          isSorted === "asc" ? (
                            <ChevronUp className="h-3.5 w-3.5" />
                          ) : isSorted === "desc" ? (
                            <ChevronDown className="h-3.5 w-3.5" />
                          ) : (
                            <ChevronsUpDown className="h-3.5 w-3.5 opacity-40" />
                          )
                        )}
                      </span>
                    )}
                    {enableColumnResize && col.getCanResize() && (
                      <span
                        onMouseDown={header.getResizeHandler()}
                        onTouchStart={header.getResizeHandler()}
                        onClick={(e) => e.stopPropagation()}
                        className={cn(
                          "absolute right-0 top-0 h-full w-1 cursor-col-resize select-none touch-none",
                          "bg-transparent hover:bg-brand-500/40",
                          header.column.getIsResizing() && "bg-brand-500",
                        )}
                        role="separator"
                        aria-orientation="vertical"
                      />
                    )}
                  </th>
                );
              })}
            </tr>
          ))}
        </thead>
        <tbody
          style={
            shouldVirtualize
              ? { height: totalSize, position: "relative", display: "block" }
              : undefined
          }
        >
          {(shouldVirtualize ? items! : allRows.map((_, i) => ({ index: i, start: 0, size: 0, key: i }))).map(
            (virtualRow) => {
              const row = allRows[virtualRow.index];
              if (!row) return null;
              const tone = rowToneAccessor?.(row.original);
              return (
                <tr
                  key={row.id}
                  data-index={virtualRow.index}
                  style={
                    shouldVirtualize
                      ? {
                          position: "absolute",
                          top: 0,
                          left: 0,
                          width: "100%",
                          height: rowHeight,
                          transform: `translateY(${virtualRow.start}px)`,
                          display: "table",
                          tableLayout: "fixed",
                        }
                      : { height: rowHeight }
                  }
                  className={cn(
                    "border-b border-border transition-colors last:border-b-0",
                    tone ? TONE_BG[tone] : "hover:bg-background-subtle",
                    onRowClick && "cursor-pointer",
                  )}
                  onClick={onRowClick ? () => onRowClick(row.original, virtualRow.index) : undefined}
                >
                  {row.getVisibleCells().map((cell) => {
                    const col = cell.column;
                    const align =
                      (col.columnDef.meta as { align?: "left" | "right" | "center" } | undefined)
                        ?.align ?? "left";
                    const alignClass =
                      align === "right"
                        ? "text-right"
                        : align === "center"
                          ? "text-center"
                          : "text-left";
                    const pinned = col.getIsPinned();
                    return (
                      <td
                        key={cell.id}
                        style={{
                          width: col.getSize(),
                          ...getPinStyle(col, "cell"),
                        }}
                        className={cn(
                          cellPadClass,
                          alignClass,
                          "text-foreground",
                          pinned && "bg-surface",
                        )}
                      >
                        {flexRender(col.columnDef.cell, cell.getContext())}
                      </td>
                    );
                  })}
                </tr>
              );
            },
          )}
        </tbody>
      </table>
    </div>
  );
}

/* ============================================================
 * Pinning helpers — sticky positioning for pinned columns
 * ========================================================== */

function getPinStyle(
  column: ReactTable<any>["getColumn"] extends (...a: any) => infer R ? R : never,
  kind: "header" | "cell",
): React.CSSProperties {
  if (!column) return {};
  const pinned = column.getIsPinned();
  if (!pinned) return {};
  const offset = pinned === "left" ? column.getStart("left") : column.getAfter("right");
  return {
    position: "sticky",
    [pinned === "left" ? "left" : "right"]: `${offset}px`,
    zIndex: kind === "header" ? 3 : 1,
  };
}

function getPinnedWidth<T>(table: ReactTable<T>, side: "left" | "right"): number {
  return table
    .getAllLeafColumns()
    .filter((c) => c.getIsPinned() === side && c.getIsVisible())
    .reduce((sum, c) => sum + c.getSize(), 0);
}

/* ============================================================
 * Pagination
 * ========================================================== */

function Pagination<T>({
  table,
  pageSizeOptions,
}: {
  table: ReactTable<T>;
  pageSizeOptions: number[];
}) {
  const { pageIndex, pageSize } = table.getState().pagination;
  const pageCount = table.getPageCount();
  const totalRows = table.getFilteredRowModel().rows.length;
  const start = pageIndex * pageSize + 1;
  const end = Math.min(start + pageSize - 1, totalRows);

  return (
    <div className="flex flex-wrap items-center justify-between gap-3 rounded-md border border-border bg-surface px-3 py-2 text-sm">
      <div className="flex items-center gap-3 text-muted-foreground">
        <span className="tabular-nums">
          {totalRows === 0 ? 0 : `${start}–${end}`} of {totalRows}
        </span>
        <label className="flex items-center gap-2">
          <span>Rows per page</span>
          <select
            className="h-8 rounded-md border border-border bg-surface px-2 text-sm"
            value={pageSize}
            onChange={(e) => table.setPageSize(Number(e.target.value))}
          >
            {pageSizeOptions.map((n) => (
              <option key={n} value={n}>
                {n}
              </option>
            ))}
          </select>
        </label>
      </div>
      <div className="flex items-center gap-1">
        <Button
          variant="ghost"
          size="icon"
          onClick={() => table.setPageIndex(0)}
          disabled={!table.getCanPreviousPage()}
          aria-label="First page"
        >
          <ChevronsLeft className="h-4 w-4" />
        </Button>
        <Button
          variant="ghost"
          size="icon"
          onClick={() => table.previousPage()}
          disabled={!table.getCanPreviousPage()}
          aria-label="Previous page"
        >
          <ChevronLeft className="h-4 w-4" />
        </Button>
        <span className="px-2 text-sm tabular-nums text-muted-foreground">
          {pageIndex + 1} / {Math.max(pageCount, 1)}
        </span>
        <Button
          variant="ghost"
          size="icon"
          onClick={() => table.nextPage()}
          disabled={!table.getCanNextPage()}
          aria-label="Next page"
        >
          <ChevronRight className="h-4 w-4" />
        </Button>
        <Button
          variant="ghost"
          size="icon"
          onClick={() => table.setPageIndex(pageCount - 1)}
          disabled={!table.getCanNextPage()}
          aria-label="Last page"
        >
          <ChevronsRight className="h-4 w-4" />
        </Button>
      </div>
    </div>
  );
}
