"use client"

import * as React from "react"
import {
  type ColumnDef,
  type ColumnFiltersState,
  type SortingState,
  type VisibilityState,
  flexRender,
  getCoreRowModel,
  getFilteredRowModel,
  getPaginationRowModel,
  getSortedRowModel,
  useReactTable,
} from "@tanstack/react-table"

import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import { useT } from "@/components/locale-provider"
import type { Translate } from "@/lib/i18n"
import {
  DropdownMenu,
  DropdownMenuCheckboxItem,
  DropdownMenuContent,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table"
import {
  IconArrowsSort,
  IconChevronDown,
  IconSortAscending,
  IconSortDescending,
} from "@tabler/icons-react"
import type { Column, Table as TanTable } from "@tanstack/react-table"

/**
 * The table every list in this application uses. Sorting, pagination, column
 * visibility and row selection come from TanStack Table; the markup is ours.
 *
 * Search and the filters that people share live in the url, above this
 * component. What lives here is what only concerns the person looking: how the
 * rows are sorted, which page they are on, which columns they hid.
 */

/**
 * The selection column, for a table that has something to do with a selection.
 * Not a default: a checkbox in every row of every list costs a column and
 * suggests a bulk action that may not exist.
 *
 * The header box is indeterminate rather than checked when only part of the
 * page is selected. Base UI takes that as its own prop; the shadcn guide passes
 * the string "indeterminate" in `checked`, which this checkbox does not accept.
 *
 * `t` is a parameter and not `useT()` because this builds a column definition
 * rather than rendering one, which is the same reason `peopleColumns(t)` and
 * `taskColumns(t, locale)` take theirs.
 */
export function selectionColumn<TData>(
  t: Translate,
  label: (row: TData) => string,
): ColumnDef<TData> {
  return {
    id: "select",
    enableSorting: false,
    enableHiding: false,
    header: ({ table }) => (
      <Checkbox
        checked={table.getIsAllPageRowsSelected()}
        indeterminate={table.getIsSomePageRowsSelected()}
        onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
        aria-label={t("table.selectPage")}
      />
    ),
    cell: ({ row }) => (
      <Checkbox
        checked={row.getIsSelected()}
        onCheckedChange={(value) => row.toggleSelected(!!value)}
        aria-label={t("table.selectRow", { name: label(row.original) })}
      />
    ),
  }
}

export function DataTableColumnHeader<TData, TValue>({
  column,
  title,
  className,
}: {
  column: Column<TData, TValue>
  title: string
  className?: string
}) {
  const t = useT()
  if (!column.getCanSort()) {
    return <span className={className}>{title}</span>
  }
  const sorted = column.getIsSorted()
  return (
    <Button
      variant="ghost"
      className={`-ml-2 h-8 ${className ?? ""}`}
      onClick={() => column.toggleSorting(sorted === "asc")}
      aria-label={t("table.sortBy", { column: title })}
    >
      {title}
      {sorted === "asc" ? (
        <IconSortAscending />
      ) : sorted === "desc" ? (
        <IconSortDescending />
      ) : (
        <IconArrowsSort className="opacity-50" />
      )}
    </Button>
  )
}

export function DataTableViewOptions<TData>({
  table,
}: {
  table: TanTable<TData>
}) {
  const t = useT()
  return (
    <DropdownMenu>
      <DropdownMenuTrigger render={<Button variant="outline" />}>
        {t("table.columns")}
        <IconChevronDown />
      </DropdownMenuTrigger>
      <DropdownMenuContent align="end">
        {table
          .getAllColumns()
          .filter((column) => column.getCanHide())
          .map((column) => (
            <DropdownMenuCheckboxItem
              key={column.id}
              checked={column.getIsVisible()}
              onCheckedChange={(value) => column.toggleVisibility(!!value)}
            >
              {column.columnDef.meta?.label ?? column.id}
            </DropdownMenuCheckboxItem>
          ))}
      </DropdownMenuContent>
    </DropdownMenu>
  )
}

export function DataTablePagination<TData>({
  table,
}: {
  table: TanTable<TData>
}) {
  const t = useT()
  const selected = table.getFilteredSelectedRowModel().rows.length
  const shown = table.getFilteredRowModel().rows.length
  const page = table.getState().pagination.pageIndex + 1
  const pages = table.getPageCount() || 1

  /**
   * One page and nothing selected means there is nothing here worth a row of
   * chrome: "Page 1 of 1" beside two buttons that cannot be pressed tells
   * somebody only that the table has a pager. A selection still needs its
   * count, so that case keeps the bar.
   */
  if (pages <= 1 && selected === 0) return null

  return (
    <div className="flex flex-wrap items-center justify-between gap-3">
      <p className="text-foreground/80">
        {selected
          ? t("table.selected", { selected, shown })
          : t("table.page", { page, pages })}
      </p>
      <div className="flex items-center gap-2">
        <Button
          variant="outline"
          onClick={() => table.previousPage()}
          disabled={!table.getCanPreviousPage()}
        >
          {t("table.previous")}
        </Button>
        <Button
          variant="outline"
          onClick={() => table.nextPage()}
          disabled={!table.getCanNextPage()}
        >
          {t("table.next")}
        </Button>
      </div>
    </div>
  )
}

export function DataTable<TData, TValue>({
  columns,
  data,
  toolbar,
  empty,
  pageSize = 25,
  paginate = true,
}: {
  columns: ColumnDef<TData, TValue>[]
  data: TData[]
  toolbar?: (table: TanTable<TData>) => React.ReactNode
  /**
   * Already in the reader's language, and required. There was a default here,
   * an English sentence about clearing a filter, which meant a screen that
   * forgot the prop shipped words nobody chose to somebody who cannot read
   * them. An empty table is the one moment a list has something to explain, so
   * the explanation belongs to the screen that knows what is missing.
   */
  empty: string
  pageSize?: number
  /**
   * Off for a list that has already been made short by other means.
   *
   * The team overview groups a run of missed days into one line, and pagination
   * on top of that would hide the eleventh duty behind a Next button on a table
   * of fifteen. A pager exists to carry repetition; where the repetition is
   * gone, it is a control that only tells somebody the table has one.
   */
  paginate?: boolean
}) {
  const [sorting, setSorting] = React.useState<SortingState>([])
  const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
    [],
  )
  const [columnVisibility, setColumnVisibility] =
    React.useState<VisibilityState>({})
  const [rowSelection, setRowSelection] = React.useState({})

  const table = useReactTable({
    data,
    columns,
    initialState: { pagination: { pageSize } },
    state: { sorting, columnFilters, columnVisibility, rowSelection },
    onSortingChange: setSorting,
    onColumnFiltersChange: setColumnFilters,
    onColumnVisibilityChange: setColumnVisibility,
    onRowSelectionChange: setRowSelection,
    getCoreRowModel: getCoreRowModel(),
    getFilteredRowModel: getFilteredRowModel(),
    getSortedRowModel: getSortedRowModel(),
    // Left off entirely rather than given a page size big enough to hold
    // everything: a table with no pagination row model has one page by
    // construction, and cannot be put on page two by a stale state.
    ...(paginate ? { getPaginationRowModel: getPaginationRowModel() } : {}),
  })

  return (
    <div className="flex flex-col gap-4">
      {toolbar ? toolbar(table) : null}
      <div className="overflow-hidden rounded-xl ring-1 ring-foreground/10">
        <Table>
          <TableHeader>
            {table.getHeaderGroups().map((group) => (
              <TableRow key={group.id}>
                {group.headers.map((header) => (
                  <TableHead key={header.id}>
                    {header.isPlaceholder
                      ? null
                      : flexRender(
                          header.column.columnDef.header,
                          header.getContext(),
                        )}
                  </TableHead>
                ))}
              </TableRow>
            ))}
          </TableHeader>
          <TableBody>
            {table.getRowModel().rows.length ? (
              table.getRowModel().rows.map((row) => (
                <TableRow
                  key={row.id}
                  data-state={row.getIsSelected() ? "selected" : undefined}
                >
                  {row.getVisibleCells().map((cell) => (
                    <TableCell key={cell.id}>
                      {flexRender(
                        cell.column.columnDef.cell,
                        cell.getContext(),
                      )}
                    </TableCell>
                  ))}
                </TableRow>
              ))
            ) : (
              <TableRow>
                <TableCell
                  colSpan={columns.length}
                  className="py-10 text-center"
                >
                  {empty}
                </TableCell>
              </TableRow>
            )}
          </TableBody>
        </Table>
      </div>
      {paginate ? <DataTablePagination table={table} /> : null}
    </div>
  )
}
