"use client";

/**
 * <AdminTable> — the ONE table every admin list page uses.
 * ---------------------------------------------------------------------------
 * THE CELL CONTRACT (read this before using it)
 *
 * The table is a client component, so it can never receive `render(row)`
 * functions or raw Prisma objects. Instead the SERVER page maps each DB row to
 * an `AdminRow` whose `cells` map holds ready-made ReactNodes:
 *
 *   const rows: AdminRow[] = orders.map((o) => ({
 *     id: String(o.id),
 *     cells: {
 *       id: <Stacked main={<Mono>{o.id}</Mono>} sub={o.providerOrderId} />,
 *       charge: <Stacked main={money(o.charge)} sub={money(o.cost)} align="right" />,
 *       status: <StatusBadge status={o.status} />,
 *     },
 *     menu: [
 *       { kind: "link", label: "Details", href: `/admin/orders/${o.id}` },
 *       { kind: "action", label: "Resend", action: resendOrderAction,
 *         confirm: "Send this order to the provider again?" },
 *     ],
 *   }));
 *
 * Never put a Decimal or a Date in a cell — format it with
 * `lib/serialize` (money/dec/iso) or `lib/format` first. Helper cell
 * components live in `components/admin/table/cells.tsx`.
 *
 * URL STATE: the table never reads `useSearchParams`. The page passes
 * `basePath` + `query` (from `parseListParams(...).query`) and every control
 * builds a link/`router.push` from them, so the server refetches the data.
 *
 * BULK ACTIONS: submitted FormData carries one `ids` field with the
 * comma-separated selected ids — decode with `bulkIds()` / `bulkIntIds()`.
 * Row-menu actions carry both `id` and `ids` (same value) so one server action
 * can serve a single row and a selection.
 */

import * as React from "react";
import Link from "next/link";
import { ArrowDown, ArrowUp, ChevronsUpDown } from "lucide-react";
import { Checkbox } from "@/components/ui/checkbox";
import { sortHref, type SortDir } from "@/lib/admin/list";
import { adminTexts } from "@/lib/i18n/admin";
import { cn } from "@/lib/utils";
import { AdminActionDialog, type PendingAction } from "./table/action-dialog";
import { AdminBulkBar } from "./table/bulk-bar";
import { AdminRowMenu } from "./table/row-menu";
import { AdminTablePagination } from "./table/pagination";
import { AdminTableToolbar } from "./table/toolbar";
import type {
  AdminBulkAction,
  AdminColumn,
  AdminDateRange,
  AdminFacet,
  AdminRow,
  AdminSearchField,
  AdminTab,
  AdminTotals,
} from "./table/types";

export type {
  AdminBulkAction,
  AdminColumn,
  AdminDateRange,
  AdminFacet,
  AdminMenuField,
  AdminMenuItem,
  AdminRow,
  AdminSearchField,
  AdminTab,
  AdminTotals,
} from "./table/types";

const t = adminTexts.table;

const ALIGN: Record<string, string> = {
  left: "text-left",
  right: "text-right",
  center: "text-center",
};

const HIDE_BELOW: Record<string, string> = {
  sm: "hidden sm:table-cell",
  md: "hidden md:table-cell",
  lg: "hidden lg:table-cell",
  xl: "hidden xl:table-cell",
};

const HIGHLIGHT: Record<string, string> = {
  warning: "bg-amber-50/60 dark:bg-amber-500/5",
  danger: "bg-rose-50/60 dark:bg-rose-500/5",
  success: "bg-emerald-50/60 dark:bg-emerald-500/5",
};

export type AdminTableProps = {
  /** Pathname of the page, e.g. "/admin/orders". */
  basePath: string;
  /** `parseListParams(...).query` — the sanitised current URL params. */
  query: Record<string, string>;
  columns: AdminColumn[];
  rows: AdminRow[];
  total: number;
  page: number;
  perPage: number;
  sort?: string;
  dir?: SortDir;
  perPageOptions?: number[];

  tabs?: AdminTab[];
  activeTab?: string;
  defaultTab?: string;
  facetFilters?: AdminFacet[];
  searchFields?: AdminSearchField[];
  q?: string;
  qField?: string;
  searchPlaceholder?: string;
  dateRange?: AdminDateRange;

  bulkActions?: AdminBulkAction[];
  /** Header label / visibility of the trailing row-menu column. */
  rowMenu?: { label?: string; hidden?: boolean };
  /** Pass only when `?totals=1`; the toggle button shows whenever `totalsAvailable`. */
  totalsRow?: AdminTotals;
  totalsAvailable?: boolean;
  totalsEnabled?: boolean;
  exportHref?: string;
  /** Rendered on the right of the toolbar (e.g. an "Add service" button). */
  toolbarExtra?: React.ReactNode;
  emptyState?: React.ReactNode;
  /** Scroll-region height that makes the sticky header actually stick. */
  maxHeightClass?: string;
  className?: string;
};

export function AdminTable({
  basePath,
  query,
  columns,
  rows,
  total,
  page,
  perPage,
  sort,
  dir,
  perPageOptions,
  tabs,
  activeTab,
  defaultTab,
  facetFilters,
  searchFields,
  q,
  qField,
  searchPlaceholder,
  dateRange,
  bulkActions,
  rowMenu,
  totalsRow,
  totalsAvailable,
  totalsEnabled,
  exportHref,
  toolbarExtra,
  emptyState,
  maxHeightClass = "max-h-[calc(100vh-16rem)]",
  className,
}: AdminTableProps) {
  const [selected, setSelected] = React.useState<string[]>([]);
  const [pending, setPending] = React.useState<PendingAction | null>(null);

  const rowsKey = rows.map((r) => r.id).join(",");
  React.useEffect(() => {
    setSelected([]);
  }, [rowsKey]);

  const selectable = Boolean(bulkActions?.length);
  const selectableIds = React.useMemo(
    () => rows.filter((r) => r.selectable !== false).map((r) => r.id),
    [rowsKey], // eslint-disable-line react-hooks/exhaustive-deps
  );
  const allSelected =
    selectableIds.length > 0 && selectableIds.every((id) => selected.includes(id));
  const someSelected = selected.length > 0 && !allSelected;

  const showMenuColumn =
    !rowMenu?.hidden && (Boolean(rowMenu) || rows.some((r) => r.menu?.length));

  const colSpan = columns.length + (selectable ? 1 : 0) + (showMenuColumn ? 1 : 0);
  const hasTotalsCells = Boolean(totalsRow?.cells && Object.keys(totalsRow.cells).length);

  return (
    <div className={cn("overflow-hidden rounded-2xl border bg-card shadow-sm", className)}>
      <AdminTableToolbar
        basePath={basePath}
        query={query}
        tabs={tabs}
        activeTab={activeTab}
        defaultTab={defaultTab}
        searchFields={searchFields}
        q={q}
        qField={qField}
        searchPlaceholder={searchPlaceholder}
        facetFilters={facetFilters}
        dateRange={dateRange}
        totalsEnabled={totalsEnabled}
        hasTotals={totalsAvailable}
        exportHref={exportHref}
        extra={toolbarExtra}
      />

      {selectable ? (
        <AdminBulkBar
          selected={selected}
          actions={bulkActions ?? []}
          onClear={() => setSelected([])}
          onRequest={setPending}
        />
      ) : null}

      {totalsEnabled && totalsRow?.items?.length ? (
        <div className="flex flex-wrap items-center gap-x-5 gap-y-1.5 border-b bg-muted/40 px-3 py-2">
          {totalsRow.items.map((item) => (
            <div key={item.label} className="flex items-baseline gap-1.5">
              <span className="text-[11px] tracking-wider text-muted-foreground uppercase">
                {item.label}
              </span>
              <span className="font-mono text-sm font-semibold tabular-nums">
                {item.value}
              </span>
            </div>
          ))}
        </div>
      ) : null}

      <div className={cn("relative w-full overflow-auto", maxHeightClass)}>
        <table className="w-full caption-bottom border-separate border-spacing-0 text-sm">
          <thead>
            <tr>
              {selectable ? (
                <th className="sticky top-0 z-20 w-9 border-b bg-card px-3 py-2 text-left">
                  <Checkbox
                    aria-label={t.selectAll}
                    checked={allSelected ? true : someSelected ? "indeterminate" : false}
                    onCheckedChange={(value) =>
                      setSelected(value === true ? selectableIds : [])
                    }
                  />
                </th>
              ) : null}

              {columns.map((col) => {
                const isActive = sort === col.key;
                const head = (
                  <span className="inline-flex items-center gap-1">
                    {col.label}
                    {col.sortable ? (
                      isActive ? (
                        dir === "asc" ? (
                          <ArrowUp className="size-3 text-blue-600" />
                        ) : (
                          <ArrowDown className="size-3 text-blue-600" />
                        )
                      ) : (
                        <ChevronsUpDown className="size-3 opacity-40" />
                      )
                    ) : null}
                  </span>
                );
                return (
                  <th
                    key={col.key}
                    scope="col"
                    aria-sort={
                      isActive ? (dir === "asc" ? "ascending" : "descending") : undefined
                    }
                    className={cn(
                      "sticky top-0 z-20 border-b bg-card px-3 py-2 text-[11px] font-semibold tracking-wider whitespace-nowrap text-muted-foreground uppercase",
                      ALIGN[col.align ?? "left"],
                      col.hideBelow ? HIDE_BELOW[col.hideBelow] : null,
                      col.headClassName,
                    )}
                  >
                    {col.sortable ? (
                      <Link
                        href={sortHref(
                          basePath,
                          query,
                          col.key,
                          sort,
                          dir,
                          col.defaultDir ?? "desc",
                        )}
                        scroll={false}
                        className="inline-flex items-center gap-1 transition-colors hover:text-foreground"
                      >
                        {head}
                      </Link>
                    ) : (
                      head
                    )}
                  </th>
                );
              })}

              {showMenuColumn ? (
                <th className="sticky top-0 z-20 w-10 border-b bg-card px-2 py-2 text-right text-[11px] font-semibold tracking-wider text-muted-foreground uppercase">
                  <span className="sr-only">{rowMenu?.label ?? t.actions}</span>
                </th>
              ) : null}
            </tr>
          </thead>

          <tbody>
            {rows.length === 0 ? (
              <tr>
                <td colSpan={colSpan} className="px-3 py-10">
                  {emptyState ?? (
                    <div className="flex flex-col items-center gap-1 text-center">
                      <p className="text-sm font-medium">{t.noRows}</p>
                      <p className="text-xs text-muted-foreground">{t.noRowsHint}</p>
                    </div>
                  )}
                </td>
              </tr>
            ) : (
              rows.map((row) => {
                const isSelected = selected.includes(row.id);
                return (
                  <tr
                    key={row.id}
                    data-state={isSelected ? "selected" : undefined}
                    className={cn(
                      "transition-colors hover:bg-muted/40 [&:last-child>td]:border-0",
                      row.highlight ? HIGHLIGHT[row.highlight] : null,
                      isSelected && "bg-blue-600/5 dark:bg-blue-500/10",
                      row.className,
                    )}
                  >
                    {selectable ? (
                      <td className="border-b px-3 py-2 align-middle">
                        {row.selectable === false ? null : (
                          <Checkbox
                            aria-label={t.selectRow}
                            checked={isSelected}
                            onCheckedChange={(value) =>
                              setSelected((prev) =>
                                value === true
                                  ? [...new Set([...prev, row.id])]
                                  : prev.filter((id) => id !== row.id),
                              )
                            }
                          />
                        )}
                      </td>
                    ) : null}

                    {columns.map((col) => (
                      <td
                        key={col.key}
                        className={cn(
                          "border-b px-3 py-2 align-middle text-sm",
                          col.wrap ? "whitespace-normal" : "whitespace-nowrap",
                          ALIGN[col.align ?? "left"],
                          col.hideBelow ? HIDE_BELOW[col.hideBelow] : null,
                          col.className,
                        )}
                      >
                        {row.cells[col.key] ?? (
                          <span className="text-muted-foreground">—</span>
                        )}
                      </td>
                    ))}

                    {showMenuColumn ? (
                      <td className="border-b px-2 py-2 text-right align-middle">
                        {row.menu?.length ? (
                          <AdminRowMenu
                            items={row.menu}
                            rowId={row.id}
                            label={rowMenu?.label}
                            onRequest={setPending}
                          />
                        ) : null}
                      </td>
                    ) : null}
                  </tr>
                );
              })
            )}
          </tbody>

          {totalsEnabled && hasTotalsCells ? (
            <tfoot>
              <tr className="bg-muted/60 font-medium">
                {selectable ? <td className="px-3 py-2" /> : null}
                {columns.map((col, index) => (
                  <td
                    key={col.key}
                    className={cn(
                      "border-t px-3 py-2 text-sm whitespace-nowrap",
                      ALIGN[col.align ?? "left"],
                      col.hideBelow ? HIDE_BELOW[col.hideBelow] : null,
                    )}
                  >
                    {totalsRow?.cells?.[col.key] ??
                      (index === 0 ? (
                        <span className="text-[11px] tracking-wider text-muted-foreground uppercase">
                          {totalsRow?.label ?? t.totals}
                        </span>
                      ) : null)}
                  </td>
                ))}
                {showMenuColumn ? <td className="border-t px-2 py-2" /> : null}
              </tr>
            </tfoot>
          ) : null}
        </table>
      </div>

      <AdminTablePagination
        basePath={basePath}
        query={query}
        total={total}
        page={page}
        perPage={perPage}
        perPageOptions={perPageOptions}
      />

      <AdminActionDialog pending={pending} onClose={() => setPending(null)} />
    </div>
  );
}

export default AdminTable;
