"use client";

import * as React from "react";

import {
  getCoreRowModel,
  useReactTable,
  type ColumnDef,
  type Header,
  type PaginationState,
} from "@tanstack/react-table";
import { useRouter } from "next/navigation";
import { toast } from "sonner";

import {
  DataTablePagination,
  DataTableShell,
  useDebouncedValue,
} from "@/app/dashboard/_components/data-table";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { TableCell } from "@/components/ui/table";

import {
  MISSING_MASTER_DEFAULT_PAGE_SIZE,
  type MissingMasterFilters,
  type MissingMasterRouteState,
} from "./missing-master-route";
import type { MissingMasterRow } from "./missing-master-schema";

type Props<TRow extends MissingMasterRow> = {
  data: TRow[];
  columns: ColumnDef<TRow>[];
  filters: MissingMasterFilters;
  totalCount: number;
  page: number;
  pageSize: number;
  pageCount: number;
  /** Builds the list URL for a route state (from `createMissingMasterRoute`). */
  buildHref: (state: Partial<MissingMasterRouteState>) => string;
  /** Prefix for the pagination control ids, unique per page. */
  idPrefix: string;
};

type FilterKey = keyof MissingMasterFilters;

type NavigateFilters = (
  next: Partial<MissingMasterFilters> & { page?: number; pageSize?: number },
  options?: { replace?: boolean },
) => void;

/** Filter input rendered under the column header whose id is `columnId`. */
const FILTER_FIELDS: ReadonlyArray<{
  columnId: string;
  key: FilterKey;
  placeholder: string;
  className: string;
}> = [
  { columnId: "ticker", key: "ticker", placeholder: "Ticker", className: "h-8 bg-background font-mono text-xs" },
  { columnId: "xmlId", key: "xmlId", placeholder: "Upload / Bank API", className: "h-8 bg-background text-xs" },
  { columnId: "bankName", key: "bankName", placeholder: "Bank API", className: "h-8 bg-background text-xs" },
  { columnId: "id", key: "tempId", placeholder: "Temp ID", className: "h-8 bg-background text-xs" },
];

/**
 * A text filter whose draft follows the URL value and writes back (debounced, trimmed,
 * back to page 1) when the user types.
 */
function useFilterDraft(
  key: FilterKey,
  committed: string,
  navigate: NavigateFilters,
): readonly [string, (value: string) => void] {
  const [draft, setDraft] = React.useState(committed);
  const [seenCommitted, setSeenCommitted] = React.useState(committed);
  const debounced = useDebouncedValue(draft);

  // Follow the URL value (clear filters, back/forward) without an effect round-trip.
  if (seenCommitted !== committed) {
    setSeenCommitted(committed);
    setDraft(committed);
  }

  React.useEffect(() => {
    const normalized = debounced.trim();
    if (normalized === committed) return;
    navigate({ [key]: normalized, page: 1 }, { replace: true });
  }, [debounced, committed, key, navigate]);

  return [draft, setDraft] as const;
}

export function MissingMasterTable<TRow extends MissingMasterRow>({
  data,
  columns,
  filters,
  totalCount,
  page,
  pageSize,
  pageCount,
  buildHref,
  idPrefix,
}: Props<TRow>) {
  const router = useRouter();

  const navigate = React.useCallback<NavigateFilters>(
    (next, options) => {
      const href = buildHref({
        ticker: next.ticker ?? filters.ticker,
        xmlId: next.xmlId ?? filters.xmlId,
        bankName: next.bankName ?? filters.bankName,
        tempId: next.tempId ?? filters.tempId,
        page: next.page ?? page,
        pageSize: next.pageSize ?? pageSize,
      });

      if (options?.replace) {
        router.replace(href, { scroll: false });
        return;
      }

      router.push(href, { scroll: false });
    },
    [
      buildHref,
      filters.bankName,
      filters.tempId,
      filters.ticker,
      filters.xmlId,
      page,
      pageSize,
      router,
    ],
  );

  const [tickerDraft, setTickerDraft] = useFilterDraft("ticker", filters.ticker, navigate);
  const [xmlIdDraft, setXmlIdDraft] = useFilterDraft("xmlId", filters.xmlId, navigate);
  const [bankNameDraft, setBankNameDraft] = useFilterDraft("bankName", filters.bankName, navigate);
  const [tempIdDraft, setTempIdDraft] = useFilterDraft("tempId", filters.tempId, navigate);
  const drafts: Record<FilterKey, readonly [string, (value: string) => void]> = {
    ticker: [tickerDraft, setTickerDraft],
    xmlId: [xmlIdDraft, setXmlIdDraft],
    bankName: [bankNameDraft, setBankNameDraft],
    tempId: [tempIdDraft, setTempIdDraft],
  };

  const pagination = React.useMemo<PaginationState>(
    () => ({
      pageIndex: Math.max(page - 1, 0),
      pageSize,
    }),
    [page, pageSize],
  );

  const table = useReactTable({
    data,
    columns,
    state: { pagination },
    pageCount,
    manualPagination: true,
    onPaginationChange: (updater) => {
      const next = typeof updater === "function" ? updater(pagination) : updater;
      const nextPageSize = next.pageSize > 0 ? next.pageSize : pageSize;
      const nextPage = nextPageSize !== pageSize ? 1 : Math.max(next.pageIndex + 1, 1);

      navigate({
        page: nextPage,
        pageSize: nextPageSize,
      });
    },
    getCoreRowModel: getCoreRowModel(),
    getRowId: (row) => String(row.id),
  });

  return (
    <div className="space-y-4">
      <DataTableShell
        table={table}
        columnCount={columns.length}
        emptyMessage="No records found."
        renderFilterCell={(header) => renderFilterCell(header, drafts)}
      />

      <DataTablePagination
        table={table}
        totalRows={totalCount}
        itemNoun="record"
        idPrefix={idPrefix}
        pageSizes={[25, MISSING_MASTER_DEFAULT_PAGE_SIZE, 100]}
      />

      <div className="flex justify-end border-t pt-4">
        <Button
          type="button"
          onClick={() => toast.success("Priority updated (mock — connect API when ready)")}
        >
          Update priority
        </Button>
      </div>
    </div>
  );
}

function renderFilterCell<TRow>(
  header: Header<TRow, unknown>,
  drafts: Record<FilterKey, readonly [string, (value: string) => void]>,
) {
  const field = FILTER_FIELDS.find((candidate) => candidate.columnId === header.column.id);
  if (!field) return null;

  const [value, setValue] = drafts[field.key];
  return (
    <TableCell key={header.id} className="p-2">
      <Input
        value={value}
        onChange={(event) => setValue(event.target.value)}
        placeholder={field.placeholder}
        className={field.className}
      />
    </TableCell>
  );
}
