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

type 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;
	onStateChange?: () => void;
	defaultFilterState?: ColumnFiltersState;
	onTableInit?: (table: TableType<TData>) => void;
} & (
	| {
			withContextMenu: true;

			/**
			 * @example
			 * <ContextMenuContent>
			 * 	<ContextMenuItem>{data}<ContextMenuItem>
			 * <ContextMenuContent>
			 */
			contextMenuContent: (data: TData) => React.ReactNode;
	  }
	| {
			withContextMenu?: false;
			contextMenuContent?: never;
	  }
);

export function DataTable<TData, TValue>({
	columns,
	data,
	pinnedFiltri,
	hasSelectRow,
	searchColumn,
	columns_titles,
	defaultSort,
	rowOnClick,
	highlightedRow,
	defaultColumnVisibility,
	tabClassName,
	onStateChange,
	defaultFilterState,
	onTableInit,
	withContextMenu,
	contextMenuContent,
}: DataTableProps<TData, TValue>) {
	const [sorting, setSorting] = useState<SortingState>(defaultSort || []);
	const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
	const [pagination, setPagination] = useState({
		pageIndex: 0, //initial page index
		pageSize: 20, //default page size
	});

	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({
		columns,
		data,
		getCoreRowModel: getCoreRowModel(),
		getFilteredRowModel: getFilteredRowModel(),
		getPaginationRowModel: getPaginationRowModel(),
		getSortedRowModel: getSortedRowModel(),
		onColumnFiltersChange: setColumnFilters,
		onColumnVisibilityChange: setColumnVisibility,
		onGlobalFilterChange: setGlobalFilter,
		onRowSelectionChange: setRowSelection,
		onSortingChange: setSorting,
		onPaginationChange: setPagination,
		state: {
			columnFilters,
			columnVisibility,
			globalFilter,
			rowSelection,
			sorting,
			pagination,
		},
	});

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

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

	return (
		<DataTableProvider
			columns_titles={columns_titles}
			hasSelectRow={hasSelectRow}
			pinnedFiltri={pinnedFiltri}
			searchColumn={searchColumn}
			table={table}
		>
			<div className="space-y-4">
				<DataTableToolbar />
				<div
					className="rounded-md border dark:border-muted-foreground/50"
					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) => {
									// biome-ignore lint/style/noNonNullAssertion: <always exists>
									const row = rows[virtualRow.index]!;

									const rowElem = (
										<TableRow
											className={cn(
												rowOnClick && "cursor-pointer",
												highlightedRow === row.id && "bg-neutral-100/50",
												"group/row",
											)}
											data-row-id={row.id}
											data-state={row.getIsSelected() && "selected"}
											key={row.id}
											onClick={
												rowOnClick
													? () => rowOnClick(row.original, row.id)
													: undefined
											}
											style={{
												height: `${virtualRow.size}px`,
												transform: `translateY(${
													virtualRow.start - index * virtualRow.size
												}px)`,
											}}
										>
											{row.getVisibleCells().map((cell) => {
												return (
													<TableCell key={cell.id}>
														{flexRender(
															cell.column.columnDef.cell,
															cell.getContext(),
														)}
													</TableCell>
												);
											})}
										</TableRow>
									);

									if (withContextMenu && contextMenuContent) {
										return (
											<ContextMenu key={row.id}>
												<ContextMenuTrigger asChild>
													{rowElem}
												</ContextMenuTrigger>

												{contextMenuContent(row.original)}
											</ContextMenu>
										);
									}
									return rowElem;
								})
							) : (
								<TableRow>
									<TableCell
										className="h-24 text-center"
										colSpan={columns.length}
									>
										No results.
									</TableCell>
								</TableRow>
							)}
						</TableBody>
					</Table>
				</div>

				<DataTablePagination />
			</div>
		</DataTableProvider>
	);
}
