import { Checkbox } from '@/components/ui/checkbox';
import { Field, FieldLabel } from '@/components/ui/field';
import {
	Pagination,
	PaginationContent,
	PaginationEllipsis,
	PaginationItem,
	PaginationLink,
	PaginationNext,
	PaginationPrevious,
} from '@/components/ui/pagination';
import {
	Select,
	SelectContent,
	SelectGroup,
	SelectItem,
	SelectTrigger,
	SelectValue,
} from '@/components/ui/select';
import { Skeleton } from '@/components/ui/skeleton';
import { cn } from '@/lib/utils';
import {
	flexRender,
	getCoreRowModel,
	getPaginationRowModel,
	getSortedRowModel,
	useReactTable,
	type ColumnDef,
	type PaginationState,
	type RowSelectionState,
	type SortingState,
} from '@tanstack/react-table';
import { __ } from '@wordpress/i18n';
import { ChevronDown, ChevronUp, ChevronsUpDown } from 'lucide-react';
import { useEffect, useState } from 'react';

interface ServerPaginationProps {
	total: number;
	page: number;
	perPage: number;
	onPageChange: (page: number) => void;
	onPerPageChange: (perPage: number) => void;
}

interface DataTableProps<TData> {
	data: TData[];
	columns: ColumnDef<TData, any>[];
	pagination?: boolean;
	pageSize?: number;
	pageSizeOptions?: number[];
	serverPagination?: ServerPaginationProps;
	rowsPerPageLabel?: string;
	enableRowSelection?: boolean;
	toolbar?: React.ReactNode;
	isLoading?: boolean;
	isFetching?: boolean;
	onSelectionChange?: (rows: TData[]) => void;
	resetSelectionKey?: unknown;
}

function DataTable<TData>({
	data,
	columns,
	pagination = false,
	pageSize = 10,
	pageSizeOptions = [5, 10, 25, 50, 100],
	serverPagination,
	rowsPerPageLabel,
	enableRowSelection = false,
	toolbar,
	isLoading = false,
	isFetching = false,
	onSelectionChange,
	resetSelectionKey,
}: DataTableProps<TData>) {
	const [sorting, setSorting] = useState<SortingState>([]);
	const [rowSelection, setRowSelection] = useState<RowSelectionState>({});

	useEffect(() => {
		setRowSelection({});
	}, [resetSelectionKey]);

	const [{ pageIndex, pageSize: currentPageSize }, setPagination] =
		useState<PaginationState>({ pageIndex: 0, pageSize });

	const selectionColumn: ColumnDef<TData, unknown> = {
		id: 'select',
		header: ({ table }) => (
			<Checkbox
				checked={
					table.getIsAllPageRowsSelected()
						? true
						: table.getIsSomePageRowsSelected()
							? 'indeterminate'
							: false
				}
				onCheckedChange={(checked) =>
					table.toggleAllPageRowsSelected(!!checked)
				}
			/>
		),
		cell: ({ row }) => (
			<Checkbox
				checked={row.getIsSelected()}
				disabled={!row.getCanSelect()}
				onCheckedChange={(checked) => row.toggleSelected(!!checked)}
			/>
		),
		enableSorting: false,
		size: 40,
	};

	const allColumns = enableRowSelection
		? [selectionColumn, ...columns]
		: columns;

	const table = useReactTable({
		data,
		columns: allColumns,
		state: {
			sorting,
			pagination: { pageIndex, pageSize: currentPageSize },
			rowSelection,
		},
		enableRowSelection,
		onRowSelectionChange: setRowSelection,
		onSortingChange: setSorting,
		onPaginationChange: setPagination,
		getCoreRowModel: getCoreRowModel(),
		getSortedRowModel: getSortedRowModel(),
		...(pagination && { getPaginationRowModel: getPaginationRowModel() }),
		manualPagination: !pagination,
	});

	useEffect(() => {
		onSelectionChange?.(
			table.getSelectedRowModel().rows.map((r) => r.original),
		);
	}, [rowSelection]);

	const { pageIndex: currentPage, pageSize: size } =
		table.getState().pagination;

	const isServer = !!serverPagination;
	const totalRows = isServer ? serverPagination!.total : data.length;
	const activePage = isServer ? serverPagination!.page - 1 : currentPage;
	const activeSize = isServer ? serverPagination!.perPage : size;
	const from = totalRows === 0 ? 0 : activePage * activeSize + 1;
	const to = Math.min((activePage + 1) * activeSize, totalRows);
	const pageCount = isServer
		? Math.max(1, Math.ceil(totalRows / activeSize))
		: table.getPageCount();
	const showPagination = pagination || isServer;

	const pager = isServer
		? {
				canPrev: activePage > 0,
				canNext: activePage < pageCount - 1,
				prev: () => serverPagination!.onPageChange(serverPagination!.page - 1),
				next: () => serverPagination!.onPageChange(serverPagination!.page + 1),
				goTo: (page: number) => serverPagination!.onPageChange(page),
				setSize: (s: number) => serverPagination!.onPerPageChange(s),
			}
		: {
				canPrev: table.getCanPreviousPage(),
				canNext: table.getCanNextPage(),
				prev: () => table.previousPage(),
				next: () => table.nextPage(),
				goTo: (page: number) => table.setPageIndex(page - 1),
				setSize: (s: number) => table.setPageSize(s),
			};

	return (
		<div className="w-full space-y-3">
			{toolbar && <div>{toolbar}</div>}

			<div className="relative overflow-hidden rounded-md border border-gray-200 bg-white">
				<div className="overflow-x-auto">
					<table className="w-full text-sm">
						<thead className="border-b border-gray-200 bg-gray-50">
							{table.getHeaderGroups().map((headerGroup) => (
								<tr key={headerGroup.id}>
									{headerGroup.headers.map((header) => {
										const canSort = header.column.getCanSort();
										const sorted = header.column.getIsSorted();

										return (
											<th
												key={header.id}
												style={
													header.column.columnDef.size
														? { width: header.column.columnDef.size }
														: undefined
												}
												className={cn(
													'px-4 py-3 text-start text-xs font-semibold tracking-wider text-gray-600 uppercase',
													canSort &&
														'cursor-pointer select-none hover:text-gray-800',
												)}
												onClick={
													canSort
														? header.column.getToggleSortingHandler()
														: undefined
												}
											>
												{header.isPlaceholder ? null : (
													<div className="flex items-center gap-1">
														{flexRender(
															header.column.columnDef.header,
															header.getContext(),
														)}
														{canSort && (
															<span className="text-gray-400">
																{sorted === 'asc' ? (
																	<ChevronUp className="size-3" />
																) : sorted === 'desc' ? (
																	<ChevronDown className="size-3" />
																) : (
																	<ChevronsUpDown className="size-3" />
																)}
															</span>
														)}
													</div>
												)}
											</th>
										);
									})}
								</tr>
							))}
						</thead>
						<tbody
							className={cn(
								'divide-y divide-gray-100 transition-opacity',
								isFetching && !isLoading && 'opacity-60',
							)}
						>
							{isLoading ? (
								Array.from({ length: 5 }).map((_, i) => (
									<tr key={i}>
										{allColumns.map((_, j) => (
											<td key={j} className="px-4 py-3.5">
												<Skeleton className="h-4" />
											</td>
										))}
									</tr>
								))
							) : table.getRowModel().rows.length === 0 ? (
								<tr>
									<td
										colSpan={allColumns.length}
										className="px-4 py-10 text-center text-gray-400"
									>
										No results.
									</td>
								</tr>
							) : (
								table.getRowModel().rows.map((row) => (
									<tr key={row.id} className="hover:bg-gray-50">
										{row.getVisibleCells().map((cell) => (
											<td key={cell.id} className="px-4 py-3.5">
												{flexRender(
													cell.column.columnDef.cell,
													cell.getContext(),
												)}
											</td>
										))}
									</tr>
								))
							)}
						</tbody>
					</table>
				</div>
			</div>

			{showPagination && (
				<div className="flex items-center justify-between px-1">
					<p className="text-sm text-gray-500">
						Showing {from} - {to} of {totalRows}
					</p>
					<div className="flex items-center gap-4">
						<Field orientation="horizontal" className="w-fit items-center">
							<FieldLabel
								htmlFor="select-rows-per-page"
								className="font-normal text-gray-500"
							>
								{rowsPerPageLabel ?? __('Rows per page', 'allcoach')}
							</FieldLabel>
							<Select
								value={String(activeSize)}
								onValueChange={(value) => pager.setSize(Number(value))}
							>
								<SelectTrigger className="w-20" id="select-rows-per-page">
									<SelectValue />
								</SelectTrigger>
								<SelectContent align="start">
									<SelectGroup>
										{pageSizeOptions.map((opt) => (
											<SelectItem key={opt} value={String(opt)}>
												{opt}
											</SelectItem>
										))}
									</SelectGroup>
								</SelectContent>
							</Select>
						</Field>
						<Pagination className="w-auto">
							<PaginationContent>
								<PaginationItem>
									<PaginationPrevious
										href="#"
										onClick={(e) => {
											e.preventDefault();
											pager.prev();
										}}
										className={cn(
											!pager.canPrev && 'pointer-events-none opacity-50',
										)}
									/>
								</PaginationItem>

								{Array.from({ length: pageCount }, (_, i) => {
									const showPage =
										i === 0 ||
										i === pageCount - 1 ||
										Math.abs(i - activePage) <= 1;
									const showStartEllipsis = i === 1 && activePage > 3;
									const showEndEllipsis =
										i === pageCount - 2 && activePage < pageCount - 4;

									if (showStartEllipsis || showEndEllipsis) {
										return (
											<PaginationItem key={i}>
												<PaginationEllipsis />
											</PaginationItem>
										);
									}
									if (!showPage) return null;
									return (
										<PaginationItem key={i}>
											<PaginationLink
												href="#"
												isActive={i === activePage}
												onClick={(e) => {
													e.preventDefault();
													pager.goTo(i + 1);
												}}
											>
												{i + 1}
											</PaginationLink>
										</PaginationItem>
									);
								})}

								<PaginationItem>
									<PaginationNext
										href="#"
										onClick={(e) => {
											e.preventDefault();
											pager.next();
										}}
										className={cn(
											!pager.canNext && 'pointer-events-none opacity-50',
										)}
									/>
								</PaginationItem>
							</PaginationContent>
						</Pagination>
					</div>
				</div>
			)}
		</div>
	);
}

export { DataTable };
