"use client";

import { useEffect, useMemo, useState, useCallback, useRef } from "react";
import {
  useReactTable,
  getCoreRowModel,
  getSortedRowModel,
  getFilteredRowModel,
  getPaginationRowModel,
  flexRender,
  type SortingState,
  type ColumnDef,
  type ColumnFiltersState,
  type RowSelectionState,
  type ColumnOrderState,
  type ColumnPinningState,
} from "@tanstack/react-table";
import {
  ColumnMetadata,
  TableMetadata,
  RowValues,
  fetchRows,
  fetchTableRowCount,
  insertRow,
  deleteRow,
  ApiError,
} from "@/app/lib/api";
import { useSettings } from "@/app/lib/settings";
import { useAppStore } from "@/app/lib/store";
import { cn } from "@/app/lib/utils";
import { toast } from "sonner";
import { Button } from "./ui/button";
import { Input } from "./ui/input";
import { Badge } from "./ui/badge";
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogDescription,
  DialogFooter,
} from "./ui/dialog";
import {
  ContextMenu,
  ContextMenuTrigger,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuSeparator,
} from "./ui/context-menu";
import {
  ArrowUp,
  ArrowDown,
  Plus,
  Trash2,
  RefreshCw,
  Loader2,
  Search,
  AlertCircle,
  ChevronLeft,
  ChevronRight,
  Rows3,
  Filter,
  X,
  Download,
  Eye,
  Pencil,
  Copy,
  CheckSquare,
  Pin,
  GripVertical,
  FileText,
  AlignJustify,
  SquareMousePointer,
} from "lucide-react";

type Props = {
  table: TableMetadata | null;
  columns: ColumnMetadata[];
  clientName: string | null;
  refreshKey: number;
  onMutation: () => void;
  onCellView?: (column: string, value: unknown, rowId?: string) => void;
  onCellEdit?: (
    table: string,
    column: string,
    value: string,
    rowId: string,
  ) => void;
  onRowView?: (row: Record<string, unknown>) => void;
};

const PAGE_SIZES = [10, 25, 50, 100] as const;
const DEFAULT_PAGE_SIZE = 50;

const rowCache = new Map<string, RowValues[]>();

const formatCellValue = (value: unknown): string => {
  if (value === null) return "NULL";
  if (value === undefined) return "";
  if (typeof value === "boolean") return value ? "true" : "false";
  if (typeof value === "object") {
    try {
      return JSON.stringify(value);
    } catch {
      return "[object]";
    }
  }
  return String(value);
};

const getCellClass = (value: unknown): string => {
  if (value === null) return "text-muted-foreground-subtle italic";
  if (typeof value === "number") return "text-primary tabular-nums";
  if (typeof value === "boolean") return "text-foreground-subtle";
  return "";
};

const looksLikeDocument = (value: unknown): boolean => {
  if (typeof value !== "string") return false;
  return (
    /\.[a-zA-Z]{2,5}$/.test(value) || value.includes("_") || value.length > 20
  );
};

function measureTextWidth(text: string, fontSize = 11): number {
  if (typeof document === "undefined") return text.length * 7;
  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d");
  if (!ctx) return text.length * 7;
  ctx.font = `${fontSize}px ui-monospace, monospace`;
  return ctx.measureText(text).width;
}

export default function DataGrid({
  table,
  columns: columnMeta,
  clientName,
  refreshKey,
  onMutation,
  onCellView,
  onCellEdit,
  onRowView,
}: Props) {
  const { baseUrl, apiKey } = useSettings();
  const { flags, preferences } = useAppStore();
  const ctx = useMemo(() => ({ baseUrl, apiKey }), [baseUrl, apiKey]);
  const [rows, setRows] = useState<RowValues[]>([]);
  const [totalRowCount, setTotalRowCount] = useState<number | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [sorting, setSorting] = useState<SortingState>([]);
  const [globalFilter, setGlobalFilter] = useState("");
  const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
  const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
  const [showFilters, setShowFilters] = useState(false);
  const [insertOpen, setInsertOpen] = useState(false);
  const [deleteOpen, setDeleteOpen] = useState(false);
  const [insertValues, setInsertValues] = useState<Record<string, string>>({});
  const [deleteId, setDeleteId] = useState("");
  const [actionLoading, setActionLoading] = useState(false);
  const [actionError, setActionError] = useState<string | null>(null);
  const [page, setPage] = useState(0);
  const [pageSize, setPageSize] = useState(DEFAULT_PAGE_SIZE);
  const [filterCol, setFilterCol] = useState<string | null>(null);
  const [filterVal, setFilterVal] = useState("");
  const [columnOrder, setColumnOrder] = useState<ColumnOrderState>([]);
  const [columnPinning, setColumnPinning] = useState<ColumnPinningState>({
    left: ["_select", "_row_num"],
  });
  const [editingCell, setEditingCell] = useState<{
    rowIdx: number;
    colId: string;
  } | null>(null);
  const [editingValue, setEditingValue] = useState("");
  const editInputRef = useRef<HTMLInputElement>(null);
  const [draggedCol, setDraggedCol] = useState<string | null>(null);
  const [columnWidths, setColumnWidths] = useState<Record<string, number>>({});
  const [resizingCol, setResizingCol] = useState<string | null>(null);
  const resizeStartX = useRef(0);
  const resizeStartW = useRef(0);
  const prevTableNameRef = useRef<string | null>(null);

  const columnNames = useMemo(
    () =>
      columnMeta.length > 0 ? columnMeta.map((c) => c.column_name) : ["*"],
    [columnMeta],
  );

  useEffect(() => {
    const tableName = table?.table_name ?? null;
    if (tableName !== prevTableNameRef.current) {
      setSorting([]);
      setPage(0);
      setColumnFilters([]);
      setRowSelection({});
      prevTableNameRef.current = tableName;
    }
    if (!table || !clientName) {
      setRows([]);
      return;
    }
    const cacheKey = `${clientName}:${table.table_name}`;
    const cached = rowCache.get(cacheKey);
    if (cached && refreshKey === 0) {
      setRows(cached);
      setLoading(false);
      return;
    }
    const controller = new AbortController();
    setLoading(true);
    setError(null);
    fetchTableRowCount(ctx, table.table_name, table.table_schema, clientName)
      .then((count) => setTotalRowCount(count))
      .catch(() => setTotalRowCount(null));
    fetchRows(
      ctx,
      table.table_name,
      columnNames,
      clientName,
      controller.signal,
      refreshKey > 0,
    )
      .then((data) => {
        setRows(data);
        rowCache.set(cacheKey, data);
      })
      .catch((err) => {
        if (err instanceof ApiError && err.code === "ABORTED") return;
        const msg =
          err instanceof ApiError ? err.message : "Failed to load rows";
        setError(msg);
        if (err instanceof ApiError && err.status === 401) {
          toast.error("Authentication required", {
            description: "Set your API key in Settings",
          });
        }
      })
      .finally(() => setLoading(false));
    return () => controller.abort();
  }, [table, refreshKey, columnNames, clientName, ctx]);

  const rawDisplayColumns = useMemo(() => {
    if (columnMeta.length > 0) return columnMeta.map((c) => c.column_name);
    if (rows.length > 0) return Object.keys(rows[0]);
    return [];
  }, [columnMeta, rows]);

  const allNullCols = useMemo(() => {
    if (!flags.hideNullColumns || rows.length === 0) return new Set<string>();
    const nullCols = new Set<string>();
    for (const col of rawDisplayColumns) {
      if (rows.every((row) => row[col] === null || row[col] === undefined)) {
        nullCols.add(col);
      }
    }
    return nullCols;
  }, [flags.hideNullColumns, rows, rawDisplayColumns]);

  const displayColumns = useMemo(
    () => rawDisplayColumns.filter((col) => !allNullCols.has(col)),
    [rawDisplayColumns, allNullCols],
  );

  // Default sort: newest first on created_at when available and no sort set.
  // Only set when created_at is actually a visible column to avoid "Column with id 'created_at' does not exist".
  useEffect(() => {
    if (sorting.length > 0) return;
    const hasCreatedAt =
      columnMeta.some((c) => c.column_name === "created_at") ||
      displayColumns.includes("created_at");
    if (hasCreatedAt && displayColumns.includes("created_at")) {
      setSorting([{ id: "created_at", desc: true }]);
    }
  }, [sorting.length, columnMeta, displayColumns]);

  // Keep sorting in sync with visible columns so we never reference a non-existent column id.
  const validSorting = useMemo(
    () =>
      sorting.filter(
        (s) =>
          s.id === "_select" ||
          s.id === "_row_num" ||
          displayColumns.includes(s.id),
      ),
    [sorting, displayColumns],
  );

  // Auto-fit column widths based on content
  useEffect(() => {
    if (!preferences.autoFitColumnWidth || displayColumns.length === 0) return;

    const newWidths: Record<string, number> = {};
    for (const col of displayColumns) {
      const headerWidth = measureTextWidth(col) + 60;
      let maxContentWidth = headerWidth;

      const sampleRows = rows.slice(0, 100);
      for (const row of sampleRows) {
        const val = formatCellValue(row[col]);
        const w = measureTextWidth(val) + 32;
        if (w > maxContentWidth) maxContentWidth = w;
      }

      newWidths[col] = Math.min(
        maxContentWidth,
        preferences.maxAutoColumnWidth,
      );
    }

    setColumnWidths((prev) => {
      const merged = { ...prev };
      for (const [k, v] of Object.entries(newWidths)) {
        if (!(k in merged)) merged[k] = v;
      }
      return merged;
    });
  }, [
    displayColumns,
    rows,
    preferences.autoFitColumnWidth,
    preferences.maxAutoColumnWidth,
  ]);

  useEffect(() => {
    if (flags.sortColumnsByDdl) {
      setColumnOrder([]);
    }
  }, [flags.sortColumnsByDdl]);

  const firstDataColIndex = 0;

  const handleStartEdit = useCallback(
    (rowIdx: number, colId: string, value: unknown) => {
      setEditingCell({ rowIdx, colId });
      setEditingValue(value === null ? "" : formatCellValue(value));
      setTimeout(() => editInputRef.current?.focus(), 0);
    },
    [],
  );

  const handleCommitEdit = useCallback(() => {
    if (!editingCell || !table || !onCellEdit) return;
    const row = rows[editingCell.rowIdx];
    if (!row) return;
    const rowId = String(row.id ?? row.uuid ?? editingCell.rowIdx);
    onCellEdit(table.table_name, editingCell.colId, editingValue, rowId);
    setEditingCell(null);
    setEditingValue("");
  }, [editingCell, editingValue, table, onCellEdit, rows]);

  const handleCancelEdit = useCallback(() => {
    setEditingCell(null);
    setEditingValue("");
  }, []);

  const startResize = useCallback(
    (col: string, e: React.MouseEvent) => {
      e.preventDefault();
      e.stopPropagation();
      setResizingCol(col);
      resizeStartX.current = e.clientX;
      resizeStartW.current = columnWidths[col] ?? 200;

      const onMove = (ev: MouseEvent) => {
        const diff = ev.clientX - resizeStartX.current;
        const newWidth = Math.max(60, resizeStartW.current + diff);
        setColumnWidths((prev) => ({ ...prev, [col]: newWidth }));
      };
      const onUp = () => {
        setResizingCol(null);
        document.removeEventListener("mousemove", onMove);
        document.removeEventListener("mouseup", onUp);
      };
      document.addEventListener("mousemove", onMove);
      document.addEventListener("mouseup", onUp);
    },
    [columnWidths],
  );

  const tableColumns = useMemo<ColumnDef<RowValues, unknown>[]>(
    () => [
      {
        id: "_select",
        header: ({ table: t }) => (
          <input
            type="checkbox"
            checked={t.getIsAllPageRowsSelected()}
            onChange={t.getToggleAllPageRowsSelectedHandler()}
            className="h-[13px] w-[13px] rounded-sm accent-primary cursor-pointer"
          />
        ),
        cell: ({ row }) => (
          <input
            type="checkbox"
            checked={row.getIsSelected()}
            onChange={row.getToggleSelectedHandler()}
            className="h-[13px] w-[13px] rounded-sm accent-primary cursor-pointer"
          />
        ),
        size: 40,
        enableSorting: false,
        enableColumnFilter: false,
        enablePinning: true,
      },
      ...(flags.showRowNumbers
        ? [
            {
              id: "_row_num",
              header: () => null,
              size: 44,
              enableSorting: false,
              enableColumnFilter: false,
              enablePinning: true,
              cell: ({ row }: { row: { index: number } }) => (
                <span className="text-body-sm text-muted-foreground-faint tabular-nums select-none">
                  {page * pageSize + row.index + 1}
                </span>
              ),
            } as ColumnDef<RowValues, unknown>,
          ]
        : []),
      ...displayColumns.map((col, colIdx) => ({
        accessorKey: col,
        header: ({
          column,
        }: {
          column: {
            getIsSorted: () => false | "asc" | "desc";
            toggleSorting: () => void;
            getIsPinned: () => false | "left" | "right";
            pin: (pos: false | "left" | "right") => void;
          };
        }) => {
          const sorted = column.getIsSorted();
          const pinned = column.getIsPinned();
          return (
            <div
              className="flex items-center gap-1.5 text-left group select-none relative"
              draggable={!flags.sortColumnsByDdl}
              onDragStart={() => {
                if (!flags.sortColumnsByDdl) setDraggedCol(col);
              }}
              onDragOver={(e) => e.preventDefault()}
              onDrop={() => {
                if (
                  !flags.sortColumnsByDdl &&
                  draggedCol &&
                  draggedCol !== col
                ) {
                  setColumnOrder((prev) => {
                    const order =
                      prev.length > 0
                        ? [...prev]
                        : ["_select", "_row_num", ...displayColumns];
                    const fromIdx = order.indexOf(draggedCol);
                    const toIdx = order.indexOf(col);
                    if (fromIdx >= 0 && toIdx >= 0) {
                      order.splice(fromIdx, 1);
                      order.splice(toIdx, 0, draggedCol);
                    }
                    return order;
                  });
                }
                setDraggedCol(null);
              }}
            >
              {!flags.sortColumnsByDdl && (
                <GripVertical className="h-3 w-3 opacity-0 group-hover:opacity-30 cursor-grab shrink-0" />
              )}
              {flags.showColumnIcons &&
                (colIdx === firstDataColIndex ? (
                  <FileText className="h-3.5 w-3.5 text-muted-foreground-subtle shrink-0" />
                ) : (
                  <AlignJustify className="h-3.5 w-3.5 text-muted-foreground-faint shrink-0" />
                ))}
              <button
                onClick={() => column.toggleSorting()}
                className="flex items-center gap-1 text-foreground-muted hover:text-foreground transition-colors truncate font-medium text-body-sm"
              >
                <span className="truncate">{col}</span>
                {sorted === "asc" && (
                  <ArrowUp className="h-3 w-3 text-primary shrink-0" />
                )}
                {sorted === "desc" && (
                  <ArrowDown className="h-3 w-3 text-primary shrink-0" />
                )}
              </button>
              <button
                onClick={() => column.pin(pinned ? false : "left")}
                className={cn(
                  "ml-auto h-3 w-3 shrink-0 transition-opacity",
                  pinned
                    ? "text-primary opacity-100"
                    : "opacity-0 group-hover:opacity-30",
                )}
                title={pinned ? "Unpin column" : "Pin column"}
              >
                <Pin className="h-3 w-3" />
              </button>
              <div
                className={cn(
                  "col-resize-handle",
                  resizingCol === col && "resizing",
                )}
                onMouseDown={(e) => startResize(col, e)}
              />
            </div>
          );
        },
        cell: ({
          getValue,
          row,
        }: {
          getValue: () => unknown;
          row: { index: number };
        }) => {
          const value = getValue();
          const isEditing =
            editingCell?.rowIdx === page * pageSize + row.index &&
            editingCell?.colId === col;

          if (isEditing) {
            return (
              <input
                ref={editInputRef}
                value={editingValue}
                onChange={(e) => setEditingValue(e.target.value)}
                onKeyDown={(e) => {
                  if (e.key === "Enter") handleCommitEdit();
                  if (e.key === "Escape") handleCancelEdit();
                }}
                onBlur={handleCommitEdit}
                className="w-full bg-background border border-primary rounded px-1.5 py-0.5 text-body-sm font-mono outline-none ring-1 ring-ring-subtle"
                autoFocus
              />
            );
          }

          const formatted = formatCellValue(value);
          const isFirstCol = colIdx === firstDataColIndex;
          const showDocIcon =
            flags.showDocumentIcons && isFirstCol && looksLikeDocument(value);

          return (
            <div
              className={cn("flex items-center gap-1.5", getCellClass(value))}
            >
              {showDocIcon && (
                <FileText className="h-3.5 w-3.5 text-muted-foreground-faint shrink-0" />
              )}
              <span className="truncate text-body-sm">{formatted}</span>
            </div>
          );
        },
        filterFn: (
          row: { getValue: (id: string) => unknown },
          columnId: string,
          filterValue: string,
        ) => {
          if (!filterValue) return true;
          const val = row.getValue(columnId);
          const str = formatCellValue(val).toLowerCase();
          const filter = filterValue.toLowerCase();
          if (filter === "null") return val === null;
          if (filter === "!null" || filter === "notnull") return val !== null;
          if (filter.startsWith(">") && typeof val === "number") {
            const num = parseFloat(filter.slice(1));
            return !isNaN(num) && val > num;
          }
          if (filter.startsWith("<") && typeof val === "number") {
            const num = parseFloat(filter.slice(1));
            return !isNaN(num) && val < num;
          }
          if (filter.startsWith("="))
            return str === filter.slice(1).toLowerCase();
          return str.includes(filter);
        },
        size: columnWidths[col] ?? 200,
        enablePinning: true,
      })),
    ],
    [
      displayColumns,
      page,
      pageSize,
      editingCell,
      editingValue,
      handleCommitEdit,
      handleCancelEdit,
      draggedCol,
      flags,
      firstDataColIndex,
      columnWidths,
      resizingCol,
      startResize,
    ],
  );

  const reactTable = useReactTable({
    data: rows,
    columns: tableColumns as ColumnDef<RowValues, unknown>[],
    state: {
      sorting: validSorting,
      globalFilter,
      columnFilters,
      rowSelection,
      columnOrder,
      columnPinning,
      pagination: { pageIndex: page, pageSize },
    },
    onSortingChange: setSorting,
    onGlobalFilterChange: setGlobalFilter,
    onColumnFiltersChange: setColumnFilters,
    onRowSelectionChange: setRowSelection,
    onColumnOrderChange: setColumnOrder,
    onColumnPinningChange: setColumnPinning,
    onPaginationChange: (updater) => {
      const next =
        typeof updater === "function"
          ? updater({ pageIndex: page, pageSize })
          : updater;
      setPage(next.pageIndex);
      setPageSize(next.pageSize);
    },
    getCoreRowModel: getCoreRowModel(),
    getSortedRowModel: getSortedRowModel(),
    getFilteredRowModel: getFilteredRowModel(),
    getPaginationRowModel: getPaginationRowModel(),
    enableRowSelection: true,
    enableColumnPinning: true,
  });

  const totalFiltered = reactTable.getFilteredRowModel().rows.length;
  const totalPages = Math.max(1, Math.ceil(totalFiltered / pageSize));
  const activeFilterCount = columnFilters.filter((f) => f.value).length;
  const selectedCount = Object.keys(rowSelection).length;

  const handleCopyCell = useCallback((value: string) => {
    navigator.clipboard.writeText(value);
    toast.success("Copied to clipboard", { duration: 1500 });
  }, []);

  const handleExportCsv = useCallback(() => {
    if (!table) return;
    const selectedRows = reactTable.getSelectedRowModel().rows;
    const exportRows =
      selectedRows.length > 0 ? selectedRows.map((r) => r.original) : rows;
    if (exportRows.length === 0) {
      toast.error("No rows to export");
      return;
    }
    const cols = displayColumns;
    const header = cols.join(",");
    const csvRows = exportRows.map((row) =>
      cols
        .map((col) => {
          const val = row[col];
          const str = formatCellValue(val);
          return str.includes(",") || str.includes('"') || str.includes("\n")
            ? `"${str.replace(/"/g, '""')}"`
            : str;
        })
        .join(","),
    );
    const csv = [header, ...csvRows].join("\n");
    const blob = new Blob([csv], { type: "text/csv" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = `${table.table_name}.csv`;
    a.click();
    URL.revokeObjectURL(url);
    toast.success(`Exported ${exportRows.length} rows`);
  }, [table, rows, displayColumns, reactTable]);

  const handleAddFilter = useCallback(() => {
    if (!filterCol || !filterVal.trim()) return;
    setColumnFilters((prev) => {
      const next = prev.filter((f) => f.id !== filterCol);
      next.push({ id: filterCol, value: filterVal.trim() });
      return next;
    });
    setFilterCol(null);
    setFilterVal("");
  }, [filterCol, filterVal]);

  const handleInsert = async () => {
    if (!table || !clientName) return;

    const requiredCols = columnMeta.filter(
      (c) => c.is_nullable === "NO" && !c.column_default,
    );
    for (const col of requiredCols) {
      const val = insertValues[col.column_name]?.trim();
      if (!val) {
        setActionError(
          `"${col.column_name}" is required (NOT NULL, no default)`,
        );
        return;
      }
    }

    setActionLoading(true);
    setActionError(null);
    try {
      const payload: Record<string, unknown> = {};
      for (const [k, v] of Object.entries(insertValues)) {
        if (v.trim() === "") continue;
        if (v === "null" || v === "NULL") {
          payload[k] = null;
          continue;
        }
        if (v === "true") {
          payload[k] = true;
          continue;
        }
        if (v === "false") {
          payload[k] = false;
          continue;
        }
        const num = Number(v);
        if (!isNaN(num) && v.trim() !== "") {
          payload[k] = num;
          continue;
        }
        try {
          payload[k] = JSON.parse(v);
          continue;
        } catch {
          /* not JSON */
        }
        payload[k] = v;
      }
      if (Object.keys(payload).length === 0)
        throw new Error("At least one field is required");
      await insertRow(ctx, table.table_name, payload, clientName);
      setInsertValues({});
      setInsertOpen(false);
      rowCache.delete(`${clientName}:${table.table_name}`);
      onMutation();
      toast.success("Row inserted");
    } catch (err) {
      const msg =
        err instanceof ApiError
          ? err.message
          : err instanceof Error
            ? err.message
            : "Insert failed";
      setActionError(msg);
      toast.error("Insert failed", { description: msg });
    } finally {
      setActionLoading(false);
    }
  };

  const handleDelete = async () => {
    if (!table || !clientName || !deleteId.trim()) return;
    setActionLoading(true);
    setActionError(null);
    try {
      await deleteRow(ctx, table.table_name, deleteId.trim(), clientName);
      setDeleteId("");
      setDeleteOpen(false);
      rowCache.delete(`${clientName}:${table.table_name}`);
      onMutation();
      toast.success("Row deleted");
    } catch (err) {
      const msg = err instanceof ApiError ? err.message : "Delete failed";
      setActionError(msg);
      toast.error("Delete failed", { description: msg });
    } finally {
      setActionLoading(false);
    }
  };

  if (!table) {
    return (
      <div className="flex flex-1 flex-col items-center justify-center gap-4 text-muted-foreground animate-fade-in">
        <div className="flex h-14 w-14 items-center justify-center rounded-xl border border-border bg-card shadow-sm">
          <Rows3 className="h-6 w-6 text-muted-foreground-dim" />
        </div>
        <div className="text-center space-y-1">
          {!clientName ? (
            <>
              <p className="text-sm font-medium text-foreground">
                No client connected
              </p>
              <p className="text-xs text-muted-foreground-subtle max-w-[240px]">
                Select a database client from the sidebar to get started
              </p>
            </>
          ) : (
            <>
              <p className="text-sm font-medium text-foreground">
                No table selected
              </p>
              <p className="text-xs text-muted-foreground-subtle">
                Choose a table from the sidebar to view its data
              </p>
            </>
          )}
        </div>
      </div>
    );
  }

  return (
    <div className="flex flex-1 flex-col min-h-0 min-w-0">
      {/* Toolbar */}
      <div className="flex flex-wrap items-center gap-1.5 sm:gap-2 border-b border-border px-2 sm:px-4 py-2 bg-card-subtle backdrop-blur-sm">
        <div className="flex items-center gap-2 shrink-0">
          <h2 className="text-xs sm:text-sm font-semibold text-foreground truncate max-w-[100px] sm:max-w-[160px]">
            {table.table_name}
          </h2>
          <Badge
            variant="secondary"
            className="text-caption px-1.5 py-0 h-4 tabular-nums hidden sm:inline-flex"
          >
            {totalRowCount != null
              ? totalRowCount.toLocaleString()
              : rows.length}{" "}
            rows
          </Badge>
        </div>
        <div className="relative shrink-0">
          <Search className="absolute left-2.5 top-1/2 h-3 w-3 -translate-y-1/2 text-muted-foreground-subtle" />
          <Input
            placeholder="Search…"
            value={globalFilter}
            onChange={(e) => setGlobalFilter(e.target.value)}
            className="h-7 w-[100px] sm:w-[160px] pl-7 text-body-sm bg-surface-subtle"
          />
        </div>
        <Button
          variant={showFilters ? "secondary" : "ghost"}
          size="sm"
          onClick={() => setShowFilters(!showFilters)}
          className="shrink-0 h-7 text-body-sm gap-1.5"
        >
          <Filter className="h-3 w-3" />
          <span className="hidden sm:inline">Filters</span>
          {activeFilterCount > 0 && (
            <span className="ml-0.5 flex h-4 w-4 items-center justify-center rounded-full bg-primary text-tiny text-primary-foreground font-bold">
              {activeFilterCount}
            </span>
          )}
        </Button>
        {activeFilterCount > 0 && (
          <Button
            variant="ghost"
            size="sm"
            onClick={() => setColumnFilters([])}
            className="shrink-0 h-7 text-body-sm"
          >
            <X className="h-3 w-3" />{" "}
            <span className="hidden sm:inline">Clear</span>
          </Button>
        )}
        {selectedCount > 0 && (
          <Badge
            variant="secondary"
            className="shrink-0 text-caption gap-1 hidden sm:inline-flex"
          >
            <CheckSquare className="h-3 w-3" />
            {selectedCount} selected
          </Badge>
        )}
        <div className="flex-1" />
        <div className="flex items-center gap-1 shrink-0">
          <Button
            variant="ghost"
            size="sm"
            onClick={handleExportCsv}
            className="h-7 text-body-sm hidden sm:inline-flex"
            disabled={rows.length === 0}
            title={rows.length === 0 ? "No rows to export" : "Export to CSV"}
          >
            <Download className="h-3 w-3" />{" "}
            <span className="hidden md:inline">Export</span>
          </Button>
          <Button
            variant="ghost"
            size="sm"
            className="h-7 w-7 p-0"
            onClick={() => {
              rowCache.delete(`${clientName}:${table.table_name}`);
              onMutation();
            }}
          >
            <RefreshCw className="h-3 w-3" />
          </Button>
          <Button
            size="sm"
            onClick={() => {
              setActionError(null);
              setInsertValues({});
              setInsertOpen(true);
            }}
            className="h-7 text-body-sm"
          >
            <Plus className="h-3 w-3" />{" "}
            <span className="hidden sm:inline">Insert row</span>
          </Button>
          <Button
            variant="ghost"
            size="sm"
            className="h-7 w-7 p-0 text-muted-foreground hover:text-destructive"
            onClick={() => {
              setActionError(null);
              setDeleteOpen(true);
            }}
          >
            <Trash2 className="h-3 w-3" />
          </Button>
        </div>
      </div>

      {/* Filter bar */}
      {showFilters && (
        <div className="flex flex-wrap items-center gap-2 border-b border-border bg-muted-subtle px-4 py-2">
          <select
            value={filterCol ?? ""}
            onChange={(e) => setFilterCol(e.target.value || null)}
            className="h-7 rounded-md border border-border bg-background px-2 text-body-sm text-foreground"
          >
            <option value="">Column…</option>
            {displayColumns.map((col) => (
              <option key={col} value={col}>
                {col}
              </option>
            ))}
          </select>
          <Input
            placeholder="Filter value…"
            value={filterVal}
            onChange={(e) => setFilterVal(e.target.value)}
            className="h-7 w-[160px] text-body-sm"
            onKeyDown={(e) => e.key === "Enter" && handleAddFilter()}
          />
          <Button
            size="sm"
            variant="outline"
            onClick={handleAddFilter}
            disabled={!filterCol || !filterVal.trim()}
            className="h-7 text-body-sm"
          >
            Add filter
          </Button>
          <div className="flex flex-wrap gap-1 ml-1">
            {columnFilters.map((f) => (
              <Badge
                key={f.id}
                variant="secondary"
                className="text-caption gap-1"
              >
                <span className="font-medium">{f.id}</span>: {String(f.value)}
                <button
                  onClick={() =>
                    setColumnFilters((prev) =>
                      prev.filter((p) => p.id !== f.id),
                    )
                  }
                  className="ml-0.5 hover:text-destructive"
                >
                  <X className="h-2.5 w-2.5" />
                </button>
              </Badge>
            ))}
          </div>
          <div className="flex-1" />
          <div className="flex items-center gap-1.5 text-tiny text-muted-foreground-faint">
            {["null", "!null", ">100", "<50", "=exact"].map((hint) => (
              <code key={hint} className="bg-accent-subtle px-1 py-0.5 rounded">
                {hint}
              </code>
            ))}
          </div>
        </div>
      )}

      {error && (
        <div className="flex items-center gap-2 border-b border-destructive-border bg-destructive-subtle px-3 py-1.5 text-caption text-destructive">
          <AlertCircle className="h-3 w-3 shrink-0" />
          <span className="truncate">{error}</span>
          <button
            onClick={() => setError(null)}
            className="ml-auto shrink-0 opacity-60 hover:opacity-100"
          >
            <X className="h-2.5 w-2.5" />
          </button>
        </div>
      )}

      {/* Table */}
      <div className="flex-1 overflow-auto relative">
        {loading && (
          <div className="absolute inset-0 z-10 flex items-center justify-center bg-surface-overlay backdrop-blur-[1px]">
            <div className="flex flex-col items-center gap-2">
              <Loader2 className="h-5 w-5 animate-spin text-primary" />
              <span className="text-caption text-muted-foreground">
                Loading…
              </span>
            </div>
          </div>
        )}
        <table className="w-max min-w-full border-collapse">
          <thead className="sticky top-0 z-5">
            {reactTable.getHeaderGroups().map((headerGroup) => (
              <tr
                key={headerGroup.id}
                className="border-b border-border bg-grid-header"
              >
                {headerGroup.headers.map((header) => (
                  <th
                    key={header.id}
                    className={cn(
                      "border-r border-border-muted px-3 py-2 text-left text-body-sm font-medium text-muted-foreground whitespace-nowrap relative",
                      "bg-grid-header",
                      header.column.getIsPinned() && "sticky left-0 z-6",
                    )}
                    style={{
                      width:
                        header.column.id !== "_select" &&
                        header.column.id !== "_row_num"
                          ? (columnWidths[header.column.id] ?? header.getSize())
                          : header.getSize(),
                    }}
                  >
                    {header.isPlaceholder
                      ? null
                      : flexRender(
                          header.column.columnDef.header,
                          header.getContext(),
                        )}
                  </th>
                ))}
              </tr>
            ))}
          </thead>
          <tbody>
            {reactTable.getRowModel().rows.length === 0 && !loading && (
              <tr>
                <td
                  colSpan={tableColumns.length}
                  className="px-4 py-16 text-center"
                >
                  <div className="flex flex-col items-center gap-2 text-muted-foreground">
                    <Rows3 className="h-8 w-8 opacity-20" />
                    <p className="text-sm font-medium">
                      {activeFilterCount > 0
                        ? "No rows match your filters"
                        : "No rows to display"}
                    </p>
                    {activeFilterCount > 0 && (
                      <button
                        onClick={() => setColumnFilters([])}
                        className="text-xs text-primary hover:underline"
                      >
                        Clear filters
                      </button>
                    )}
                    {rows.length === 0 && activeFilterCount === 0 && (
                      <Button
                        size="sm"
                        variant="outline"
                        onClick={() => {
                          setActionError(null);
                          setInsertValues({});
                          setInsertOpen(true);
                        }}
                        className="mt-2 text-xs"
                      >
                        <Plus className="h-3 w-3" /> Insert first row
                      </Button>
                    )}
                  </div>
                </td>
              </tr>
            )}
            {reactTable.getRowModel().rows.map((row, rowIdx) => (
              <ContextMenu key={row.id}>
                <ContextMenuTrigger asChild>
                  <tr
                    className={cn(
                      "border-b border-border-faint transition-colors",
                      row.getIsSelected()
                        ? "bg-primary/5 hover:bg-primary/8"
                        : rowIdx % 2 === 0
                          ? "hover:bg-accent-subtle"
                          : "bg-muted-faint hover:bg-accent-subtle",
                    )}
                  >
                    {row.getVisibleCells().map((cell) => {
                      const colId = cell.column.id;
                      const cellValue = cell.getValue();
                      const isDataCol =
                        colId !== "_select" && colId !== "_row_num";
                      return (
                        <td
                          key={cell.id}
                          className={cn(
                            "border-r border-border-faint px-3 py-1.5 whitespace-nowrap",
                            cell.column.getIsPinned() &&
                              "sticky left-0 z-1 bg-background",
                            row.getIsSelected() &&
                              cell.column.getIsPinned() &&
                              "bg-primary/5",
                          )}
                          style={{
                            width: isDataCol
                              ? (columnWidths[colId] ?? undefined)
                              : undefined,
                            maxWidth: isDataCol
                              ? (columnWidths[colId] ?? undefined)
                              : undefined,
                          }}
                        >
                          {isDataCol ? (
                            <div
                              className="cursor-default"
                              onDoubleClick={() => {
                                if (onCellEdit && table) {
                                  handleStartEdit(
                                    page * pageSize + row.index,
                                    colId,
                                    cellValue,
                                  );
                                } else {
                                  handleCopyCell(formatCellValue(cellValue));
                                }
                              }}
                            >
                              {flexRender(
                                cell.column.columnDef.cell,
                                cell.getContext(),
                              )}
                            </div>
                          ) : (
                            flexRender(
                              cell.column.columnDef.cell,
                              cell.getContext(),
                            )
                          )}
                        </td>
                      );
                    })}
                  </tr>
                </ContextMenuTrigger>
                <ContextMenuContent className="text-xs">
                  {onRowView && (
                    <ContextMenuItem onClick={() => onRowView(row.original)}>
                      <SquareMousePointer className="h-3 w-3" /> View full row
                    </ContextMenuItem>
                  )}
                  <ContextMenuItem
                    onClick={() => {
                      navigator.clipboard.writeText(
                        JSON.stringify(row.original, null, 2),
                      );
                      toast.success("Copied row JSON");
                    }}
                  >
                    <Copy className="h-3 w-3" /> Copy row as JSON
                  </ContextMenuItem>
                  {onCellView && (
                    <>
                      <ContextMenuSeparator />
                      <ContextMenuItem
                        onClick={() => {
                          const firstCol = displayColumns[0];
                          if (firstCol)
                            onCellView(
                              firstCol,
                              row.original[firstCol],
                              row.original.id as string | undefined,
                            );
                        }}
                      >
                        <Eye className="h-3 w-3" /> View cell
                      </ContextMenuItem>
                    </>
                  )}
                  {onCellEdit && table && (
                    <ContextMenuItem
                      onClick={() => {
                        const rowId = String(
                          row.original.id ?? row.original.uuid ?? row.index,
                        );
                        const firstCol = displayColumns[0];
                        if (firstCol)
                          onCellEdit(
                            table.table_name,
                            firstCol,
                            formatCellValue(row.original[firstCol]),
                            rowId,
                          );
                      }}
                    >
                      <Pencil className="h-3 w-3" /> Edit row
                    </ContextMenuItem>
                  )}
                </ContextMenuContent>
              </ContextMenu>
            ))}
          </tbody>
        </table>
      </div>

      {/* Status bar */}
      <div className="flex items-center gap-3 border-t border-border bg-grid-header px-4 py-1.5 text-caption text-muted-foreground">
        <span className="tabular-nums">
          Total:{" "}
          <strong className="text-foreground">
            {totalRowCount != null
              ? totalRowCount.toLocaleString()
              : rows.length}
          </strong>{" "}
          rows
          {totalRowCount != null && rows.length < totalRowCount
            ? ` (showing ${rows.length})`
            : ""}
        </span>
        <span className="text-border-subtle">·</span>
        <span className="tabular-nums">Filtered: {totalFiltered}</span>
        <span className="text-border-subtle">·</span>
        <span>{displayColumns.length} cols</span>
        <div className="flex items-center gap-2 sm:gap-3 border-t border-border bg-grid-header px-2 sm:px-4 py-1.5 text-caption text-muted-foreground">
          <span className="tabular-nums">
            <strong className="text-foreground">
              {totalRowCount != null
                ? totalRowCount.toLocaleString()
                : rows.length}
            </strong>{" "}
            rows
          </span>
          <span className="text-border-subtle hidden sm:inline">·</span>
          <span className="tabular-nums hidden sm:inline">
            Filtered: {totalFiltered}
          </span>
          <span className="text-border-subtle hidden sm:inline">·</span>
          <span className="hidden sm:inline">{displayColumns.length} cols</span>
          <div className="flex items-center gap-1">
            <span>Per page:</span>
            <select
              value={pageSize}
              onChange={(e) => {
                setPageSize(Number(e.target.value));
                setPage(0);
              }}
              className="bg-transparent text-caption text-foreground outline-none cursor-pointer tabular-nums"
            >
              {PAGE_SIZES.map((s) => (
                <option key={s} value={s}>
                  {s}
                </option>
              ))}
            </select>
          </div>
          <div className="flex-1" />
          <div className="flex items-center gap-1">
            <span className="tabular-nums">
              {totalFiltered === 0
                ? "0"
                : `${page * pageSize + 1}–${Math.min((page + 1) * pageSize, totalFiltered)}`}{" "}
              of {totalFiltered}
            </span>
            <Button
              variant="ghost"
              size="icon"
              className="h-5 w-5"
              onClick={() => setPage((p) => Math.max(0, p - 1))}
              disabled={page === 0}
            >
              <ChevronLeft className="h-3 w-3" />
            </Button>
            <Button
              variant="ghost"
              size="icon"
              className="h-5 w-5"
              onClick={() => setPage((p) => Math.min(totalPages - 1, p + 1))}
              disabled={page >= totalPages - 1}
            >
              <ChevronRight className="h-3 w-3" />
            </Button>
          </div>
        </div>

        {/* Insert Dialog */}
        <Dialog open={insertOpen} onOpenChange={setInsertOpen}>
          <DialogContent className="max-w-lg max-h-[80vh] overflow-hidden flex flex-col">
            <DialogHeader>
              <DialogTitle>
                Insert row into{" "}
                <span className="font-medium">{table.table_name}</span>
              </DialogTitle>
              <DialogDescription>
                Fill in values. Leave blank to skip optional fields.
              </DialogDescription>
            </DialogHeader>
            {actionError && (
              <div className="flex items-start gap-1.5 rounded-md border border-destructive-border bg-destructive-muted px-2.5 py-1.5 text-caption text-destructive">
                <AlertCircle className="h-3 w-3 shrink-0 mt-0.5" />
                <span className="wrap-break-word">{actionError}</span>
              </div>
            )}
            <div className="flex-1 overflow-auto space-y-2 pr-1">
              {(columnMeta.length > 0
                ? columnMeta.map((c) => ({
                    name: c.column_name,
                    type: c.data_type ?? "",
                    required: c.is_nullable === "NO" && !c.column_default,
                    defaultValue: c.column_default ?? null,
                  }))
                : rawDisplayColumns.length > 0
                  ? rawDisplayColumns.map((c) => ({
                      name: c,
                      type: "",
                      required: false,
                      defaultValue: null,
                    }))
                  : displayColumns.map((c) => ({
                      name: c,
                      type: "",
                      required: false,
                      defaultValue: null,
                    }))
              ).map((col) => (
                <div key={col.name} className="flex items-start gap-3">
                  <div className="w-[140px] shrink-0 pt-1.5">
                    <label
                      className="text-body-sm text-muted-foreground truncate block"
                      title={`${col.name}${col.type ? ` (${col.type})` : ""}${col.defaultValue ? ` [default: ${col.defaultValue}]` : ""}`}
                    >
                      {col.name}
                      {col.required && (
                        <span className="text-destructive ml-0.5">*</span>
                      )}
                    </label>
                    {col.type && (
                      <span className="text-tiny text-muted-foreground-faint truncate block">
                        {col.type}
                        {col.defaultValue ? ` · default` : ""}
                      </span>
                    )}
                  </div>
                  <Input
                    value={insertValues[col.name] ?? ""}
                    onChange={(e) =>
                      setInsertValues((prev) => ({
                        ...prev,
                        [col.name]: e.target.value,
                      }))
                    }
                    placeholder={
                      col.defaultValue
                        ? col.defaultValue.length > 24
                          ? col.defaultValue.slice(0, 24) + "…"
                          : col.defaultValue
                        : col.type || "value"
                    }
                    className="flex-1 h-7 text-body-sm"
                  />
                </div>
              ))}
              {columnMeta.length === 0 &&
                rawDisplayColumns.length === 0 &&
                displayColumns.length === 0 && (
                  <p className="text-body-sm text-muted-foreground py-4 text-center">
                    No column metadata available. Enter columns manually in the
                    fields above or check your client connection.
                  </p>
                )}
            </div>
            <DialogFooter>
              <Button
                variant="outline"
                onClick={() => setInsertOpen(false)}
                className="h-8 text-xs"
              >
                Cancel
              </Button>
              <Button
                onClick={handleInsert}
                disabled={actionLoading}
                className="h-8 text-xs"
              >
                {actionLoading && <Loader2 className="h-3 w-3 animate-spin" />}{" "}
                Insert
              </Button>
            </DialogFooter>
          </DialogContent>
        </Dialog>

        {/* Delete Dialog */}
        <Dialog open={deleteOpen} onOpenChange={setDeleteOpen}>
          <DialogContent>
            <DialogHeader>
              <DialogTitle>
                Delete row from{" "}
                <span className="font-medium">{table.table_name}</span>
              </DialogTitle>
              <DialogDescription>
                Enter the resource_id of the row to delete. This cannot be
                undone.
              </DialogDescription>
            </DialogHeader>
            {actionError && (
              <div className="flex items-start gap-1.5 rounded-md border border-destructive-border bg-destructive-muted px-2.5 py-1.5 text-caption text-destructive">
                <AlertCircle className="h-3 w-3 shrink-0 mt-0.5" />
                <span className="wrap-break-word">{actionError}</span>
              </div>
            )}
            <Input
              value={deleteId}
              onChange={(e) => setDeleteId(e.target.value)}
              placeholder="resource_id"
              className="text-body-sm"
            />
            <DialogFooter>
              <Button variant="outline" onClick={() => setDeleteOpen(false)}>
                Cancel
              </Button>
              <Button
                variant="destructive"
                onClick={handleDelete}
                disabled={actionLoading || !deleteId.trim()}
              >
                {actionLoading && <Loader2 className="h-3 w-3 animate-spin" />}{" "}
                Delete
              </Button>
            </DialogFooter>
          </DialogContent>
        </Dialog>
      </div>
    </div>
  );
}
