"use client";
import { useState, type ReactNode } from "react";
import { useSearchParams } from "next/navigation";
import {
  Search,
  RefreshCw,
  SlidersHorizontal,
  ChevronLeft,
  ChevronRight,
  ArrowUpDown,
  Plus,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogDescription,
  DialogFooter,
} from "@/components/ui/dialog";
import {
  readListQuery,
  filterRecords,
  sortRecords,
  paginate,
} from "@/lib/list-query";
import { tableExportData } from "@/lib/export";
import { TableExport } from "./table-export";
import { useQueryUpdate } from "./tabs";
export type Column<T> = {
  key: keyof T & string;
  label: string;
  sortKey?: string | null;
  render?: (row: T) => ReactNode;
  exportValue?: (row: T) => unknown;
};
export type RemoteTableState = {
  page: number;
  limit: number;
  total: number;
  pages: number;
  loading: boolean;
  error: string;
  onRefresh: () => void;
};
export type DataTableLoadState = Pick<
  RemoteTableState,
  "loading" | "error" | "onRefresh"
>;
export function DataTable<T extends { id: string }>({
  title,
  description,
  rows,
  columns,
  filters = [],
  onCreate,
  onRefresh,
  createLabel = "Create",
  actions,
  searchPlaceholder = "Search...",
  exportName,
  emptyMessage = "No records found.",
  remote,
  dataState,
  hideSearch = false,
  pageSizes = [25, 50, 100],
}: {
  title: string;
  description?: string;
  rows: readonly T[];
  columns: readonly Column<T>[];
  filters?: readonly {
    key: keyof T & string;
    label: string;
    options: readonly string[];
  }[];
  onCreate?: () => void;
  onRefresh?: () => void;
  createLabel?: string;
  actions?: (row: T) => ReactNode;
  searchPlaceholder?: string;
  exportName?: string;
  emptyMessage?: string;
  remote?: RemoteTableState;
  dataState?: DataTableLoadState;
  hideSearch?: boolean;
  pageSizes?: readonly number[];
}) {
  const params = useSearchParams(),
    update = useQueryUpdate();
  const query = readListQuery(Object.fromEntries(params));
  const [filterOpen, setFilterOpen] = useState(false),
    [notice, setNotice] = useState("");
  const [draft, setDraft] = useState<Record<string, string>>({});
  const filtersValue = Object.fromEntries(
    filters.map((f) => [f.key, params.get(f.key) ?? ""]),
  );
  const filtered = remote
    ? [...rows]
    : filterRecords(rows, query.search, filtersValue, (row) =>
        Object.values(row).join(" "),
      );
  const sorted = remote
    ? filtered
    : sortRecords(
        filtered,
        query.sort,
        query.order,
        columns.map((c) => c.key),
      );
  const page = remote
    ? {
        items: sorted,
        page: remote.page,
        pages: Math.max(1, remote.pages),
        limit: remote.limit,
        total: remote.total,
        start: remote.total ? (remote.page - 1) * remote.limit + 1 : 0,
        end: Math.min(
          (remote.page - 1) * remote.limit + sorted.length,
          remote.total,
        ),
      }
    : paginate(
        params.get("state") === "empty" ? [] : sorted,
        query.page,
        query.limit,
      );
  return (
    <section className="panel overflow-hidden">
      <div className="flex flex-wrap items-center justify-between gap-4 border-b p-5">
        <div>
          <h2 className="font-semibold">{title}</h2>
          {description && (
            <p className="mt-1 text-sm text-muted-foreground">{description}</p>
          )}
        </div>
        <div className="flex max-w-full flex-wrap items-center gap-2">
          {!hideSearch && (
            <form
              className="relative"
              onSubmit={(e) => {
                e.preventDefault();
                const data = new FormData(e.currentTarget);
                update({
                  query: String(data.get("query") ?? ""),
                  paginate_page: null,
                });
              }}
            >
              <Search className="pointer-events-none absolute left-3 top-3.5 size-4 text-muted-foreground" />
              <input
                key={query.search}
                name="query"
                aria-label={`Search ${title}`}
                defaultValue={query.search}
                placeholder={searchPlaceholder}
                className="form-input w-52 max-w-full pl-9 pr-12"
              />
              <button
                className="absolute right-2 top-3 text-xs text-muted-foreground"
                type="submit"
              >
                Find
              </button>
            </form>
          )}
          <Button
            variant="outline"
            size="icon"
            disabled={remote?.loading || dataState?.loading}
            aria-label={`Refresh ${title}`}
             onClick={() => {
              if (remote) remote.onRefresh();
              else if (dataState) dataState.onRefresh();
              else if (onRefresh) onRefresh();
              else setNotice("Fixture records refreshed. Local edits are preserved.");
            }}
          >
            <RefreshCw className="size-4" />
          </Button>
          {filters.length > 0 && (
            <Button
              variant="outline"
              size="icon"
              aria-label={`Filter ${title}`}
              onClick={() => {
                setDraft(filtersValue);
                setFilterOpen(true);
              }}
            >
              <SlidersHorizontal className="size-4" />
            </Button>
          )}
          <TableExport
            title={title}
            filename={exportName}
            records={(params.get("state") === "empty" ||
            params.get("state") === "error"
              ? []
              : sorted
            ).map((row) => ({
              id: row.id,
              label: String(row[columns[0]?.key] ?? row.id),
              data: tableExportData(row, columns),
            }))}
            pageIds={page.items.map((row) => row.id)}
          />
          {onCreate && (
            <Button onClick={onCreate} aria-label={createLabel}>
              <Plus className="size-4" />
              <span className="hidden sm:inline">{createLabel}</span>
            </Button>
          )}
        </div>
      </div>
      {notice && (
        <p
          role="status"
          className="border-b px-5 py-2 text-xs text-muted-foreground"
        >
          {notice}
        </p>
      )}
      {Object.values(filtersValue).some(Boolean) && (
        <div className="flex flex-wrap gap-2 border-b px-5 py-3">
          {Object.entries(filtersValue)
            .filter(([, v]) => v)
            .map(([k, v]) => (
              <button
                className="rounded-full bg-muted px-3 py-1 text-xs"
                key={k}
                onClick={() => update({ [k]: null, paginate_page: null })}
              >
                {v} ×
              </button>
            ))}
          <button
            className="text-xs underline"
            onClick={() =>
              update(Object.fromEntries(filters.map((f) => [f.key, null])))
            }
          >
            Clear filters
          </button>
        </div>
      )}
      {remote?.error || dataState?.error || params.get("state") === "error" ? (
        <div role="alert" className="p-10 text-center">
          <p className="text-destructive">
            {remote?.error || dataState?.error || `Unable to load ${title.toLowerCase()}.`}
          </p>
          <Button
            variant="outline"
            className="mt-4"
            onClick={() => {
              if (remote) remote.onRefresh();
              else if (dataState) dataState.onRefresh();
              else update({ state: null });
            }}
          >
            Try again
          </Button>
        </div>
      ) : (
        <div className="overflow-x-auto">
          <table className="data-table">
            <thead>
              <tr>
                {columns.map((c) => (
                  <th key={c.key}>
                    {remote && c.sortKey === null ? (
                      c.label
                    ) : (
                      <button
                        aria-sort={
                          query.sort === (remote ? c.sortKey ?? c.key : c.key)
                            ? query.order === "asc"
                              ? "ascending"
                              : "descending"
                            : undefined
                        }
                        onClick={() => {
                          const sortKey = remote ? c.sortKey ?? c.key : c.key;
                          update({
                            sort_by: sortKey,
                            sort_order:
                              query.sort === sortKey && query.order === "asc"
                                ? "desc"
                                : "asc",
                            paginate_page: null,
                          });
                        }}
                        className="inline-flex items-center gap-2 whitespace-nowrap"
                      >
                        {c.label}
                        <ArrowUpDown className="size-3" />
                      </button>
                    )}
                  </th>
                ))}
                {actions && <th>Actions</th>}
              </tr>
            </thead>
            <tbody>
              {page.items.map((row) => (
                <tr key={row.id}>
                  {columns.map((c) => (
                    <td key={c.key}>
                      {c.render ? c.render(row) : String(row[c.key] ?? "—")}
                    </td>
                  ))}
                  {actions && (
                    <td>
                      <div className="flex items-center justify-center gap-2">
                        {actions(row)}
                      </div>
                    </td>
                  )}
                </tr>
              ))}
              {!page.items.length && (
                <tr>
                  <td
                    colSpan={columns.length + (actions ? 1 : 0)}
                    className="py-12 text-center text-muted-foreground"
                  >
                    {remote?.loading || dataState?.loading
                      ? `Loading ${title.toLowerCase()}…`
                      : emptyMessage}
                  </td>
                </tr>
              )}
            </tbody>
          </table>
        </div>
      )}
      <div className="flex flex-wrap items-center justify-between gap-3 px-5 py-4 text-sm">
        <p className="text-muted-foreground">
          Showing {page.start} to {page.end} of {page.total} entries
        </p>
        <div className="flex flex-wrap items-center gap-2">
          <Button
            size="sm"
            variant="outline"
            disabled={page.page <= 1 || remote?.loading || dataState?.loading}
            onClick={() => update({ paginate_page: String(page.page - 1) })}
          >
            <ChevronLeft className="size-4" />
            Previous
          </Button>
          <span aria-live="polite" className="px-2">
            {page.page} / {page.pages}
          </span>
          <Button
            size="sm"
            variant="outline"
            disabled={page.page >= page.pages || remote?.loading || dataState?.loading}
            onClick={() => update({ paginate_page: String(page.page + 1) })}
          >
            Next
            <ChevronRight className="size-4" />
          </Button>
          <label className="ml-2 flex items-center gap-2 text-muted-foreground">
            Rows per page
            <select
              aria-label="Rows per page"
              className="form-input w-20"
              value={remote?.limit ?? query.limit}
              onChange={(e) =>
                update({ paginate_limit: e.target.value, paginate_page: null })
              }
            >
              {pageSizes.map((n) => (
                <option key={n}>{n}</option>
              ))}
            </select>
          </label>
        </div>
      </div>
      <Dialog open={filterOpen} onOpenChange={setFilterOpen}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Filter {title}</DialogTitle>
            <DialogDescription>
              Narrow these records.
            </DialogDescription>
          </DialogHeader>
          <form
            onSubmit={(e) => {
              e.preventDefault();
              update({ ...draft, paginate_page: null });
              setFilterOpen(false);
            }}
          >
            <div className="mb-6 grid gap-4 sm:grid-cols-2">
              {filters.map((f) => (
                <label className="field-label" key={f.key}>
                  {f.label}
                  <select
                    className="form-input mt-2"
                    value={draft[f.key] ?? ""}
                    onChange={(e) =>
                      setDraft({ ...draft, [f.key]: e.target.value })
                    }
                  >
                    <option value="">All</option>
                    {f.options.map((o) => (
                      <option key={o}>{o}</option>
                    ))}
                  </select>
                </label>
              ))}
            </div>
            <DialogFooter>
              <Button
                type="button"
                variant="outline"
                onClick={() => setFilterOpen(false)}
              >
                Cancel
              </Button>
              <Button>Apply Filters</Button>
            </DialogFooter>
          </form>
        </DialogContent>
      </Dialog>
    </section>
  );
}
