import {
  constructTable,
  createSortedRowModel,
  rowSortingFeature,
  sortFn_alphanumeric,
  sortFn_text,
  tableFeatures,
  type ColumnDef,
  type SortingState,
} from "@tanstack/table-core";
import { storeReactivityBindings } from "@tanstack/table-core/store-reactivity-bindings";
import Table from "../table/table.marko";
import TableHeader from "../table/header.marko";
import TableBody from "../table/body.marko";
import TableRow from "../table/row.marko";
import TableHead from "../table/head.marko";
import TableCell from "../table/cell.marko";
import Button from "../button/button.marko";
import { cn } from "#lib/utils.ts";
import { dataTable as styles } from "./classes.ts";

export interface DataTableColumn<TData> {
  accessorKey: keyof TData & string;
  header: string;
  cell?: (row: TData) => Marko.Body | string | number;
}

export interface Input<TData extends Record<string, unknown> = Record<string, unknown>> extends Marko.Input<"div"> {
  columns: DataTableColumn<TData>[];
  data: TData[];
  class?: string;
}

static const features = tableFeatures({
  rowSortingFeature,
  sortedRowModel: createSortedRowModel(),
  sortFns: { alphanumeric: sortFn_alphanumeric, text: sortFn_text },
  coreReactivityFeature: storeReactivityBindings(),
});

<let/sorting=[] as SortingState/>

// `ColumnDef`'s SECOND type parameter is the row type. Hardcoding
// `Record<string, unknown>` there pinned it to the default while
// `constructTable` infers the real row type from `data: input.data`
// (`TData[]`), so `ctx.row.original` came back as the wrong type and
// `col.cell` — which takes `TData` — rejected it. Thread `TData` through.
<const/columnDefs=() =>
  input.columns.map((col): ColumnDef<typeof features, TData> => ({
    id: col.accessorKey,
    accessorKey: col.accessorKey,
    header: col.header,
    cell: col.cell ? (ctx) => col.cell!(ctx.row.original) : undefined,
  }))
/>

<const/buildTable=() => {
  const table = constructTable({
    features,
    data: input.data,
    columns: columnDefs(),
    initialState: { sorting },
    renderFallbackValue: null,
  });
  return table;
}/>

<const/toggleSort=(columnId: string) => {
  const table = buildTable();
  const column = table.getColumn(columnId);
  column?.toggleSorting(column.getIsSorted() === "asc");
  sorting = table.atoms.sorting.get();
}/>

<const/computeUi=() => {
  const table = buildTable();
  return {
    headers: table.getHeaderGroups().flatMap((group) =>
      group.headers.map((header) => ({
        id: header.id,
        label: header.isPlaceholder ? "" : String(header.column.columnDef.header ?? ""),
        canSort: header.column.getCanSort(),
        sortDir: header.column.getIsSorted(),
      })),
    ),
    rows: table.getRowModel().rows.map((row) => ({
      id: row.id,
      cells: row.getAllCells().map((cell) => {
        const col = input.columns.find((c) => c.accessorKey === cell.column.id);
        return {
          id: cell.id,
          value: col?.cell ? col.cell(row.original) : String(cell.getValue() ?? ""),
        };
      }),
    })),
  };
}/>
<const/ui=computeUi()/>

<const/{ class: className, columns: _columns, data: _data, ...rest }=input/>

<div ...rest data-slot="data-table-container" class=cn(className)>
  <Table>
    <TableHeader>
      <TableRow>
        <for|header| of=ui.headers>
          <TableHead>
            <if=header.canSort>
              <Button type="button" variant="ghost" size="sm" class=styles.sortButton onClick() { toggleSort(header.id); }>
                ${header.label}
                <svg
                  xmlns="http://www.w3.org/2000/svg"
                  width="24"
                  height="24"
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="currentColor"
                  stroke-width="2"
                  stroke-linecap="round"
                  stroke-linejoin="round"
                  class=styles.sortIcon
                >
                  <if=header.sortDir === "asc">
                    <path d="m18 15-6-6-6 6"/>
                  </if>
                  <else if=header.sortDir === "desc">
                    <path d="m6 9 6 6 6-6"/>
                  </else>
                  <else>
                    <path d="m21 16-4 4-4-4"/>
                    <path d="M17 20V4"/>
                    <path d="m3 8 4-4 4 4"/>
                    <path d="M7 4v16"/>
                  </else>
                </svg>
              </Button>
            </if>
            <else>${header.label}</else>
          </TableHead>
        </for>
      </TableRow>
    </TableHeader>
    <TableBody>
      <if=ui.rows.length === 0>
        <TableRow>
          <TableCell colspan=ui.headers.length class=styles.emptyCell>No results.</TableCell>
        </TableRow>
      </if>
      <else>
        <for|row| of=ui.rows>
          <TableRow>
            <for|cell| of=row.cells>
              <TableCell>${cell.value}</TableCell>
            </for>
          </TableRow>
        </for>
      </else>
    </TableBody>
  </Table>
</div>
