'use client';

import * as React from 'react';
import {
  useReactTable,
  getCoreRowModel,
  getSortedRowModel,
  getPaginationRowModel,
  flexRender,
  type ColumnDef,
  type SortingState,
  type PaginationState,
  type OnChangeFn,
} from '@tanstack/react-table';
import { cn } from '@/lib/utils';

/* -------------------------------------------------------------------------- */
/*  Generic DataTable<T> wrapping TanStack Table v8                           */
/* -------------------------------------------------------------------------- */

export interface DataTableProps<T> {
  /** Column definitions created with `columnHelper` or raw ColumnDef array */
  columns: ColumnDef<T, any>[];
  /** Row data */
  data: T[];
  /** Total number of records (for server-side pagination display) */
  total?: number;

  /* --- Sorting --- */
  /** Controlled sorting state (optional; uncontrolled if omitted) */
  sorting?: SortingState;
  /** Callback when sorting changes */
  onSortingChange?: OnChangeFn<SortingState>;

  /* --- Pagination --- */
  /** Controlled pagination state */
  pagination?: PaginationState;
  /** Callback when pagination changes */
  onPaginationChange?: OnChangeFn<PaginationState>;
  /** Page size options shown in the dropdown */
  pageSizeOptions?: number[];
  /** Set to true when pagination is server-side (disables client pagination) */
  manualPagination?: boolean;

  /** Show a loading skeleton overlay */
  loading?: boolean;
  /** Message shown when data array is empty */
  emptyMessage?: string;
  /** Additional class name for the wrapper */
  className?: string;
  /** Optional row click handler */
  onRowClick?: (row: T) => void;
}

export function DataTable<T>({
  columns,
  data,
  total,
  sorting: controlledSorting,
  onSortingChange,
  pagination: controlledPagination,
  onPaginationChange,
  pageSizeOptions = [10, 20, 50, 100],
  manualPagination = false,
  loading = false,
  emptyMessage = 'No results.',
  className,
  onRowClick,
}: DataTableProps<T>) {
  /* -- Internal (uncontrolled) state fallbacks -- */
  const [internalSorting, setInternalSorting] = React.useState<SortingState>([]);
  const [internalPagination, setInternalPagination] =
    React.useState<PaginationState>({ pageIndex: 0, pageSize: 10 });

  const sorting = controlledSorting ?? internalSorting;
  const pagination = controlledPagination ?? internalPagination;

  const table = useReactTable({
    data,
    columns,
    state: { sorting, pagination },
    onSortingChange: onSortingChange ?? setInternalSorting,
    onPaginationChange: onPaginationChange ?? setInternalPagination,
    getCoreRowModel: getCoreRowModel(),
    getSortedRowModel: getSortedRowModel(),
    getPaginationRowModel: manualPagination ? undefined : getPaginationRowModel(),
    manualPagination,
    ...(manualPagination && total != null ? { pageCount: Math.ceil(total / pagination.pageSize) } : {}),
  });

  const pageCount = table.getPageCount();
  const currentPage = pagination.pageIndex + 1;

  return (
    <div className={cn('w-full space-y-4', className)}>
      {/* Table */}
      <div className="rounded-md border">
        <div className="relative overflow-x-auto">
          {loading && (
            <div className="absolute inset-0 z-10 flex items-center justify-center bg-background/60">
              <svg
                className="h-6 w-6 animate-spin text-muted-foreground"
                xmlns="http://www.w3.org/2000/svg"
                fill="none"
                viewBox="0 0 24 24"
              >
                <circle
                  className="opacity-25"
                  cx="12"
                  cy="12"
                  r="10"
                  stroke="currentColor"
                  strokeWidth="4"
                />
                <path
                  className="opacity-75"
                  fill="currentColor"
                  d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
                />
              </svg>
            </div>
          )}
          <table className="w-full caption-bottom text-sm">
            {/* Head */}
            <thead className="[&_tr]:border-b">
              {table.getHeaderGroups().map((headerGroup) => (
                <tr key={headerGroup.id} className="border-b transition-colors hover:bg-muted/50">
                  {headerGroup.headers.map((header) => {
                    const canSort = header.column.getCanSort();
                    const sorted = header.column.getIsSorted();
                    return (
                      <th
                        key={header.id}
                        className={cn(
                          'h-10 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0',
                          canSort && 'cursor-pointer select-none',
                        )}
                        onClick={canSort ? header.column.getToggleSortingHandler() : undefined}
                      >
                        <div className="flex items-center gap-1">
                          {header.isPlaceholder
                            ? null
                            : flexRender(header.column.columnDef.header, header.getContext())}
                          {canSort && (
                            <span className="ml-1 inline-flex flex-col text-xs leading-none">
                              <span className={sorted === 'asc' ? 'text-foreground' : 'text-muted-foreground/40'}>
                                &#9650;
                              </span>
                              <span className={sorted === 'desc' ? 'text-foreground' : 'text-muted-foreground/40'}>
                                &#9660;
                              </span>
                            </span>
                          )}
                        </div>
                      </th>
                    );
                  })}
                </tr>
              ))}
            </thead>

            {/* Body */}
            <tbody className="[&_tr:last-child]:border-0">
              {table.getRowModel().rows.length === 0 ? (
                <tr>
                  <td
                    colSpan={columns.length}
                    className="h-24 text-center text-muted-foreground"
                  >
                    {emptyMessage}
                  </td>
                </tr>
              ) : (
                table.getRowModel().rows.map((row) => (
                  <tr
                    key={row.id}
                    className={cn(
                      'border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted',
                      onRowClick && 'cursor-pointer',
                    )}
                    onClick={() => onRowClick?.(row.original)}
                  >
                    {row.getVisibleCells().map((cell) => (
                      <td key={cell.id} className="px-4 py-2 align-middle [&:has([role=checkbox])]:pr-0">
                        {flexRender(cell.column.columnDef.cell, cell.getContext())}
                      </td>
                    ))}
                  </tr>
                ))
              )}
            </tbody>
          </table>
        </div>
      </div>

      {/* Pagination Controls */}
      <div className="flex items-center justify-between px-2">
        {/* Left side: row count */}
        <div className="text-sm text-muted-foreground">
          {total != null ? (
            <>
              {pagination.pageIndex * pagination.pageSize + 1}&ndash;
              {Math.min((pagination.pageIndex + 1) * pagination.pageSize, total)} of{' '}
              {total}
            </>
          ) : (
            <>{table.getRowModel().rows.length} row(s)</>
          )}
        </div>

        {/* Right side: controls */}
        <div className="flex items-center gap-4">
          {/* Page size selector */}
          <div className="flex items-center gap-2">
            <span className="text-sm text-muted-foreground">Rows</span>
            <select
              className="h-8 rounded-md border border-input bg-background px-2 text-sm"
              value={pagination.pageSize}
              onChange={(e) => {
                const setter = onPaginationChange ?? setInternalPagination;
                setter((prev: PaginationState) => ({
                  ...prev,
                  pageSize: Number(e.target.value),
                  pageIndex: 0,
                }));
              }}
            >
              {pageSizeOptions.map((size) => (
                <option key={size} value={size}>
                  {size}
                </option>
              ))}
            </select>
          </div>

          {/* Page navigation */}
          <div className="flex items-center gap-1">
            <button
              className="inline-flex h-8 w-8 items-center justify-center rounded-md border border-input bg-background text-sm disabled:opacity-50"
              onClick={() => table.setPageIndex(0)}
              disabled={!table.getCanPreviousPage()}
              aria-label="First page"
            >
              &laquo;
            </button>
            <button
              className="inline-flex h-8 w-8 items-center justify-center rounded-md border border-input bg-background text-sm disabled:opacity-50"
              onClick={() => table.previousPage()}
              disabled={!table.getCanPreviousPage()}
              aria-label="Previous page"
            >
              &lsaquo;
            </button>
            <span className="px-2 text-sm text-muted-foreground">
              {currentPage} / {pageCount || 1}
            </span>
            <button
              className="inline-flex h-8 w-8 items-center justify-center rounded-md border border-input bg-background text-sm disabled:opacity-50"
              onClick={() => table.nextPage()}
              disabled={!table.getCanNextPage()}
              aria-label="Next page"
            >
              &rsaquo;
            </button>
            <button
              className="inline-flex h-8 w-8 items-center justify-center rounded-md border border-input bg-background text-sm disabled:opacity-50"
              onClick={() => table.setPageIndex(pageCount - 1)}
              disabled={!table.getCanNextPage()}
              aria-label="Last page"
            >
              &raquo;
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}
