// Based on https://ui.shadcn.com/docs/components/data-table
'use client'

import type { TableOptions, FilterFnOption } from '@tanstack/react-table'
import {
  type ColumnDef,
  flexRender,
  getCoreRowModel,
  type RowData,
  type SortingState,
  useReactTable,
  getSortedRowModel,
  getFilteredRowModel,
} from '@tanstack/react-table'
import React from 'react'
import { usePlaceholderRows } from '../../hooks'
import { cn } from '../../utils'
import { Typography } from '../Typography'
import { CaretSortIcon } from './CaretSortIcon'
import { SkeletonCell } from './SkeletonCell'
import {
  TableRoot,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from './Table'
import { TablePagination } from './TablePagination'
import { usePagination } from './usePagination'

// Extend table meta with reusable custom props
declare module '@tanstack/table-core' {
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  interface ColumnMeta<TData extends RowData, TValue> {
    cellClassName?: string
    skeletonWidth?: number
  }
}

export type DataTableColumn<TData, TValue = unknown> = ColumnDef<TData, TValue>

// Pagination configuration
export interface PaginationConfig {
  /** Zero-based page index for controlled pagination. */
  pageIndex?: number
  /** Called when the user moves to a different page. */
  onPageChange?: (pageIndex: number, pageSize: number) => void

  /** Needed when totalCount is unknown so the next arrow can still be enabled. */
  canNextPage?: boolean
  /** Temporarily hide the `Showing X to Y` text and render controls only. */
  showPaginationDisplay?: boolean

  /** Needed with currentPageCount to render `Showing X to Y`, and with totalCount to derive select options. */
  pageSize?: number
  /** Needed with pageSize to render `Showing X to Y` when totalCount is unknown. */
  currentPageCount?: number
  /** Enables `of Z` in the range display and page selection when paired with pageSize. */
  totalCount?: number

  serverSide?: boolean
  /** `select` shows a dropdown when possible; `display` keeps a static `Page N` label. */
  pageControlMode?: 'select' | 'display'
}

interface DataTableProps<TData, TValue = unknown> {
  columns: DataTableColumn<TData, TValue>[]
  data: TData[]
  isLoading?: boolean
  pagination?: PaginationConfig
  tableOptions?: Partial<TableOptions<TData>>
  onRowClick?: (row: TData) => void
  /** Custom placeholder count when loading. If provided, overrides all other placeholder count logic.
   * Without this prop, the priority is: data.length > pagination.pageSize > 5 (default) */
  placeholderCount?: number
}

export function DataTable<TData, TValue>({
  columns,
  data,
  isLoading,
  pagination,
  tableOptions = {},
  onRowClick,
  placeholderCount,
}: DataTableProps<TData, TValue>) {
  const [internalSorting, setInternalSorting] = React.useState<SortingState>(
    tableOptions.initialState?.sorting || [],
  )
  // Use default pagination only if pagination is not provided
  const paginationWithDefaults = pagination ?? { pageSize: 10 }
  const paginationOptions = usePagination(paginationWithDefaults)

  // Handle loading state and placeholder rows internally
  // Priority: placeholderCount > data.length > pagination.pageSize > 5 (default)
  const tableData = usePlaceholderRows({
    data,
    isLoading: isLoading ?? false,
    pageSize: pagination?.pageSize ?? paginationWithDefaults.pageSize,
    placeholderCount,
  })

  // Use external sorting state if provided, otherwise use internal
  const sorting = tableOptions.state?.sorting ?? internalSorting
  const onSortingChange = tableOptions.onSortingChange ?? setInternalSorting

  // Extract state and handlers to avoid overwriting in spreads
  const {
    state: externalState,
    onSortingChange: _onSortingChange,
    ...restTableOptions
  } = tableOptions
  const { sorting: _sorting, ...restExternalState } = externalState ?? {}
  const { state: paginationState, ...restPaginationOptions } = paginationOptions

  const tableConfig = React.useMemo(() => {
    return {
      data: tableData.data as TData[],
      columns,
      getCoreRowModel: getCoreRowModel(),
      onSortingChange,
      getSortedRowModel: getSortedRowModel(),
      getFilteredRowModel: getFilteredRowModel(),
      globalFilterFn: 'auto' as FilterFnOption<TData>,
      enableSortingRemoval: false,
      state: {
        sorting,
        ...restExternalState,
        ...paginationState,
      },
      meta: {
        ...tableOptions.meta,
        ...paginationOptions.meta,
      },
      ...restTableOptions,
      ...restPaginationOptions,
    }
  }, [
    tableData.data,
    columns,
    sorting,
    onSortingChange,
    tableOptions.meta,
    paginationOptions.meta,
    restExternalState,
    paginationState,
    restTableOptions,
    restPaginationOptions,
  ])

  const table = useReactTable(tableConfig)

  return (
    <>
      <TableRoot>
        <TableHeader>
          {table.getHeaderGroups().map((headerGroup) => (
            <TableRow key={headerGroup.id}>
              {headerGroup.headers.map((header) => (
                <TableHead key={header.id}>
                  {header.column.getCanSort() ? (
                    <button
                      className="flex items-center justify-between font-semibold text-inherit"
                      onClick={() =>
                        header.column.toggleSorting(
                          header.column.getIsSorted() === 'asc',
                        )
                      }
                    >
                      {!header.isPlaceholder &&
                        flexRender(
                          header.column.columnDef.header,
                          header.getContext(),
                        )}
                      <CaretSortIcon
                        sorted={header.column.getIsSorted()}
                        className="ml-1"
                      />
                    </button>
                  ) : (
                    !header.isPlaceholder &&
                    flexRender(
                      header.column.columnDef.header,
                      header.getContext(),
                    )
                  )}
                </TableHead>
              ))}
            </TableRow>
          ))}
        </TableHeader>

        <TableBody>
          {table.getRowModel().rows.length ? (
            table.getRowModel().rows.map((row) => (
              <TableRow key={row.id} onClick={() => onRowClick?.(row.original)}>
                {row.getVisibleCells().map((cell) =>
                  tableData.isLoading ? (
                    <TableCell key={cell.id}>
                      <SkeletonCell
                        width={cell.column.columnDef.meta?.skeletonWidth}
                      />
                    </TableCell>
                  ) : (
                    <TableCell
                      key={cell.id}
                      className={cn(
                        cell.column.columnDef.meta?.cellClassName,
                        !!onRowClick && 'cursor-pointer',
                      )}
                    >
                      {flexRender(
                        cell.column.columnDef.cell,
                        cell.getContext(),
                      )}
                    </TableCell>
                  ),
                )}
              </TableRow>
            ))
          ) : (
            <TableRow>
              <TableCell colSpan={columns.length} className="h-20 text-center">
                <Typography variant="body-s" className="max-w-none">
                  No data available.
                </Typography>
              </TableCell>
            </TableRow>
          )}
        </TableBody>
      </TableRoot>
      <TablePagination
        table={table}
        isLoading={isLoading}
        pageControlMode={pagination?.pageControlMode}
        showPaginationDisplay={pagination?.showPaginationDisplay}
      />
    </>
  )
}
