"use client"

import { Checkbox } from "@/components/ui/checkbox"
import { resolveDataTablePagination } from "@/lib/data-table-pagination"
import type {
  ColumnDef,
  ColumnFiltersState,
  PaginationState,
  RowData,
  RowSelectionState,
  SortingState,
  TableOptions,
} from "@tanstack/react-table"
import {
  getCoreRowModel,
  getFilteredRowModel,
  getPaginationRowModel,
  getSortedRowModel,
  useReactTable,
} from "@tanstack/react-table"
import { useEffect, useMemo, useState } from "react"

export interface UseDataTableOptions<TData extends RowData> {
  data: TData[]
  columns: ColumnDef<TData, unknown>[]
  pageSize?: number
  disablePagination?: boolean
  manualPagination?: boolean
  manualSorting?: boolean
  getRowId?: TableOptions<TData>["getRowId"]
  enableRowSelection?: boolean
  initialSorting?: SortingState
}

export interface UseDataTableReturn<TData extends RowData> {
  table: ReturnType<typeof useReactTable<TData>>
  selectedRows: TData[]
  selectedRowIds: string[]
}

function createSelectColumn<TData extends RowData>(): ColumnDef<TData> {
  return {
    id: "select",
    enableSorting: false,
    enableHiding: false,
    header: ({ table }) => (
      <Checkbox
        checked={table.getIsAllPageRowsSelected()}
        onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
        aria-label="Select all"
        className="translate-y-0.5"
      />
    ),
    cell: ({ row }) => (
      <Checkbox
        checked={row.getIsSelected()}
        onCheckedChange={(value) => row.toggleSelected(!!value)}
        aria-label="Select row"
        className="translate-y-0.5"
      />
    ),
    size: 48,
  }
}

export function useDataTable<TData extends RowData>(options: UseDataTableOptions<TData>): UseDataTableReturn<TData> {
  const paginationConfig = useMemo(
    () =>
      resolveDataTablePagination({
        disablePagination: options.disablePagination,
        manualPagination: options.manualPagination,
        pageSize: options.pageSize,
        dataLength: options.data.length,
      }),
    [options.data.length, options.disablePagination, options.manualPagination, options.pageSize],
  )

  const [sorting, setSorting] = useState<SortingState>(options.initialSorting ?? [])
  const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])
  const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
  const [pagination, setPagination] = useState<PaginationState>({
    pageIndex: 0,
    pageSize: paginationConfig.pageSize,
  })

  const columns = useMemo(() => {
    const base = options.columns
    if (options.enableRowSelection) {
      return [createSelectColumn<TData>(), ...base]
    }
    return base
  }, [options.columns, options.enableRowSelection])

  // eslint-disable-next-line react-hooks/incompatible-library -- tanstack table hook is required for table state
  const table = useReactTable({
    data: options.data,
    columns,
    state: {
      sorting,
      columnFilters,
      rowSelection,
      pagination,
    },
    enableSorting: !options.manualSorting,
    enableRowSelection: options.enableRowSelection ?? false,
    manualSorting: options.manualSorting ?? false,
    manualPagination: paginationConfig.manualPagination,
    getRowId: options.getRowId,
    onSortingChange: setSorting,
    onColumnFiltersChange: setColumnFilters,
    onRowSelectionChange: setRowSelection,
    onPaginationChange: setPagination,
    getCoreRowModel: getCoreRowModel(),
    getSortedRowModel: getSortedRowModel(),
    getFilteredRowModel: getFilteredRowModel(),
    getPaginationRowModel: paginationConfig.manualPagination ? undefined : getPaginationRowModel(),
    // Library resets pageIndex during render when data changes, which can trigger setState
    // before commit (e.g. on route change). We do the reset in useEffect instead.
    autoResetPageIndex: false,
  })

  // Reset to first page when data changes (after commit). Replaces autoResetPageIndex
  // so we don't update state during render.
  useEffect(() => {
    setPagination((prev) => (prev.pageIndex === 0 ? prev : { ...prev, pageIndex: 0 }))
  }, [options.data])

  useEffect(() => {
    setPagination((prev) =>
      prev.pageSize === paginationConfig.pageSize ? prev : { ...prev, pageSize: paginationConfig.pageSize },
    )
  }, [paginationConfig.pageSize])

  const selectedRows = table.getSelectedRowModel().rows.map((row) => row.original)
  const selectedRowIds = table.getSelectedRowModel().rows.map((row) => row.id)

  return {
    table,
    selectedRows,
    selectedRowIds,
  }
}
