import {
  type ColumnDef,
  useReactTable,
  getCoreRowModel,
  flexRender,
  getSortedRowModel,
  type VisibilityState,
  type ColumnFiltersState,
  getFilteredRowModel,
  type SortingState,
  getPaginationRowModel,
  type ColumnSort,
  type RowSelectionState,
  type Table as TableType,
} from "@tanstack/react-table";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "~/components/ui/table";
import { DataTablePagination } from "~/components/custom_ui/dataTable-pagination";
import {
  DataTableToolbar,
  type SearchFiltro,
  type PinnedFiltro,
} from "~/components/custom_ui/dataTable-toolbar";
import { useVirtualizer } from "@tanstack/react-virtual";
import { cn } from "~/lib/utils";
import { useEffect, useRef, useState } from "react";

interface DataTableProps<TData, TValue> {
  columns: ColumnDef<TData, TValue>[];
  data: TData[];
  pinnedFiltri?: PinnedFiltro[];
  hasSelectRow?: boolean;
  searchColumn?: SearchFiltro;
  columns_titles: Record<string, string>;
  defaultSort?: ColumnSort[];
  rowOnClick?: (data: TData, rowId: string) => void;
  highlightedRow?: string;
  defaultColumnVisibility?: VisibilityState;
  tabClassName?: string;
  pagination?: boolean;
  onStateChange?: () => void;
  defaultFilterState?: ColumnFiltersState;
  onTableInit?: (table: TableType<TData>) => void;
}

export function DataTable<TData, TValue>({
  columns,
  data,
  pinnedFiltri,
  hasSelectRow,
  searchColumn,
  columns_titles,
  defaultSort,
  rowOnClick,
  highlightedRow,
  defaultColumnVisibility,
  tabClassName,
  pagination = true,
  onStateChange,
  defaultFilterState,
  onTableInit,
}: DataTableProps<TData, TValue>) {
  const [sorting, setSorting] = useState<SortingState>(defaultSort || []);
  const [rowSelection, setRowSelection] = useState<RowSelectionState>({});

  const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>(
    defaultFilterState || [],
  );
  const [columnVisibility, setColumnVisibility] = useState<VisibilityState>(
    defaultColumnVisibility || {},
  );

  const [globalFilter, setGlobalFilter] = useState<string>("");

  useEffect(() => {
    onStateChange?.();
  }, [rowSelection, columnFilters, columnVisibility, sorting, onStateChange]);

  const table = useReactTable({
    data,
    columns,
    onRowSelectionChange: setRowSelection,
    getCoreRowModel: getCoreRowModel(),
    getPaginationRowModel: getPaginationRowModel(),
    onSortingChange: setSorting,
    getSortedRowModel: getSortedRowModel(),
    onColumnFiltersChange: setColumnFilters,
    getFilteredRowModel: getFilteredRowModel(),
    onColumnVisibilityChange: setColumnVisibility,
    onGlobalFilterChange: setGlobalFilter,
    state: {
      globalFilter,
      sorting,
      columnFilters,
      columnVisibility,
      rowSelection,
    },
  });

  const parentRef = useRef<HTMLDivElement>(null);
  const { rows } = table.getRowModel();
  const virtualizer = useVirtualizer({
    count: rows.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 34,
    overscan: 20,
  });

  useEffect(() => {
    if (table && onTableInit) {
      onTableInit(table);
    }
  }, [table, onTableInit]);

  return (
    <div className="space-y-4">
      <DataTableToolbar
        table={table}
        pinnedColumns={pinnedFiltri}
        searchColumn={searchColumn}
        columns_titles={columns_titles}
        globalFilter={globalFilter}
      />
      <div className="rounded-md border" ref={parentRef}>
        <Table>
          <TableHeader>
            {table.getHeaderGroups().map((headerGroup) => (
              <TableRow key={headerGroup.id}>
                {headerGroup.headers.map((header) => {
                  return (
                    <TableHead key={header.id}>
                      {header.isPlaceholder
                        ? null
                        : flexRender(
                            header.column.columnDef.header,
                            header.getContext(),
                          )}
                    </TableHead>
                  );
                })}
              </TableRow>
            ))}
          </TableHeader>
          <TableBody className={tabClassName}>
            {virtualizer.getVirtualItems().length ? (
              virtualizer.getVirtualItems().map((virtualRow, index) => {
                const row = rows[virtualRow.index]!;
                return (
                  <TableRow
                    onClick={
                      rowOnClick
                        ? () => rowOnClick(row.original, row.id)
                        : undefined
                    }
                    key={row.id}
                    data-state={row.getIsSelected() && "selected"}
                    data-row-id={row.id}
                    style={{
                      height: `${virtualRow.size}px`,
                      transform: `translateY(${
                        virtualRow.start - index * virtualRow.size
                      }px)`,
                    }}
                    className={cn(
                      rowOnClick && "cursor-pointer",
                      highlightedRow === row.id && "bg-neutral-100/50",
                      "group/row",
                    )}
                  >
                    {row.getVisibleCells().map((cell) => {
                      return (
                        <TableCell key={cell.id}>
                          {flexRender(
                            cell.column.columnDef.cell,
                            cell.getContext(),
                          )}
                        </TableCell>
                      );
                    })}
                  </TableRow>
                );
              })
            ) : (
              <TableRow>
                <TableCell
                  colSpan={columns.length}
                  className="h-24 text-center"
                >
                  No results.
                </TableCell>
              </TableRow>
            )}
          </TableBody>
        </Table>
      </div>
      {pagination ? (
        <DataTablePagination table={table} hasSelectRow={hasSelectRow} />
      ) : null}
    </div>
  );
}
