import {
    flexRender,
    getCoreRowModel,
    getFilteredRowModel,
    getPaginationRowModel,
    getSortedRowModel,
    useReactTable,
    type ColumnDef,
    type OnChangeFn,
    type PaginationState,
    type Row,
    type RowSelectionState,
    type SortingState,
    type VisibilityState,
} from '@tanstack/react-table';
import { useEffect, useState, type ReactNode } from 'react';
import { DataTableColumnHeader } from '@/components/data-table/data-table-column-header';
import { DataTablePagination } from '@/components/data-table/data-table-pagination';
import { DataTableToolbar } from '@/components/data-table/data-table-toolbar';
import { useDataTablePreferences } from '@/components/data-table/use-data-table-preferences';
import {
    Table,
    TableBody,
    TableCell,
    TableHead,
    TableHeader,
    TableRow,
} from '@/components/ui/table';
import { cn } from '@/lib/utils';

export type DataTableProps<TData, TValue = unknown> = {
    id: string;
    columns: ColumnDef<TData, TValue>[];
    data: TData[];
    getRowId?: (row: TData) => string;
    searchPlaceholder?: string;
    disableSearch?: boolean;
    emptyMessage?: string;
    searchValue?: string;
    onSearchChange?: (value: string) => void;
    sorting?: SortingState;
    onSortingChange?: OnChangeFn<SortingState>;
    manualSorting?: boolean;
    pagination?: PaginationState;
    onPaginationChange?: OnChangeFn<PaginationState>;
    manualPagination?: boolean;
    pageCount?: number;
    totalRows?: number;
    isLoading?: boolean;
    bulkActions?: (context: {
        rows: TData[];
        clearSelection: () => void;
    }) => ReactNode;
    toolbarLeft?: ReactNode;
    toolbarRight?: ReactNode;
    className?: string;
};

export function DataTable<TData, TValue = unknown>({
    id,
    columns,
    data,
    getRowId,
    searchPlaceholder = 'Rechercher…',
    disableSearch = false,
    emptyMessage = 'Aucun résultat.',
    searchValue,
    onSearchChange,
    sorting: controlledSorting,
    onSortingChange,
    manualSorting = false,
    pagination: controlledPagination,
    onPaginationChange,
    manualPagination = false,
    pageCount,
    totalRows,
    isLoading = false,
    bulkActions,
    toolbarLeft,
    toolbarRight,
    className,
}: DataTableProps<TData, TValue>) {
    const { initialPreferences, write, defaultPageSize } =
        useDataTablePreferences(id);

    const [localSorting, setLocalSorting] = useState<SortingState>([]);
    const [localPagination, setLocalPagination] = useState<PaginationState>({
        pageIndex: 0,
        pageSize: initialPreferences.pageSize || defaultPageSize,
    });
    const [localSearch, setLocalSearch] = useState('');
    const [globalFilter, setGlobalFilter] = useState('');
    const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
    const [showOverlay, setShowOverlay] = useState(false);
    const [columnVisibility, setColumnVisibility] = useState<VisibilityState>(
        initialPreferences.columnVisibility,
    );

    const isSearchControlled = searchValue !== undefined;
    const search = isSearchControlled ? searchValue : localSearch;

    const sorting = controlledSorting ?? localSorting;
    const setSorting = onSortingChange ?? setLocalSorting;

    const pagination = controlledPagination ?? localPagination;
    const setPagination = onPaginationChange ?? setLocalPagination;

    useEffect(() => {
        if (isSearchControlled || onSearchChange) {
            return;
        }

        const timeout = window.setTimeout(() => {
            setGlobalFilter(localSearch);
        }, 300);

        return () => window.clearTimeout(timeout);
    }, [localSearch, isSearchControlled, onSearchChange]);

    useEffect(() => {
        write({
            pageSize: pagination.pageSize,
            columnVisibility,
        });
    }, [pagination.pageSize, columnVisibility, write]);

    useEffect(() => {
        if (!isLoading) {
            setShowOverlay(false);
            return;
        }

        const timeout = window.setTimeout(() => {
            setShowOverlay(true);
        }, 200);

        return () => window.clearTimeout(timeout);
    }, [isLoading]);

    const table = useReactTable({
        data,
        columns,
        state: {
            sorting,
            pagination,
            rowSelection,
            columnVisibility,
            globalFilter: manualPagination ? undefined : globalFilter,
        },
        getRowId,
        enableRowSelection: true,
        onRowSelectionChange: setRowSelection,
        onColumnVisibilityChange: setColumnVisibility,
        onSortingChange: setSorting,
        onPaginationChange: setPagination,
        onGlobalFilterChange: setGlobalFilter,
        getCoreRowModel: getCoreRowModel(),
        getSortedRowModel: manualSorting ? undefined : getSortedRowModel(),
        getFilteredRowModel: manualPagination
            ? undefined
            : getFilteredRowModel(),
        getPaginationRowModel: manualPagination
            ? undefined
            : getPaginationRowModel(),
        manualSorting,
        manualPagination,
        pageCount: manualPagination ? pageCount : undefined,
    });

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

    function handleSearchChange(value: string) {
        if (onSearchChange) {
            onSearchChange(value);
            return;
        }

        setLocalSearch(value);
        setPagination((current) => ({ ...current, pageIndex: 0 }));
    }

    const selectedRows = table
        .getSelectedRowModel()
        .rows.map((row: Row<TData>) => row.original);

    function clearSelection(): void {
        table.resetRowSelection();
    }

    return (
        <div className={cn('space-y-4', className)}>
            <DataTableToolbar
                table={table}
                search={search}
                onSearchChange={handleSearchChange}
                searchPlaceholder={searchPlaceholder}
                disableSearch={disableSearch}
                toolbarLeft={toolbarLeft}
                toolbarRight={toolbarRight}
            />

            {selectedRows.length > 0 && bulkActions && (
                <div className="flex flex-wrap items-center gap-3 rounded-xl border border-destructive/20 bg-destructive/5 px-4 py-3">
                    <p className="text-sm font-medium">
                        {selectedRows.length} sélectionné
                        {selectedRows.length > 1 ? 's' : ''}
                    </p>
                    <div className="flex flex-wrap items-center gap-2">
                        {bulkActions({
                            rows: selectedRows,
                            clearSelection,
                        })}
                    </div>
                    <button
                        type="button"
                        className="ml-auto text-sm text-muted-foreground underline-offset-4 hover:underline"
                        onClick={clearSelection}
                    >
                        Tout désélectionner
                    </button>
                </div>
            )}

            <div className="relative overflow-hidden rounded-xl border">
                {showOverlay && (
                    <div className="absolute inset-0 z-10 flex items-center justify-center bg-background/60 backdrop-blur-[1px]">
                        <div className="size-6 animate-spin rounded-full border-2 border-muted-foreground border-t-transparent" />
                    </div>
                )}

                <Table>
                    <TableHeader className="bg-muted/40">
                        {table.getHeaderGroups().map((headerGroup) => (
                            <TableRow key={headerGroup.id}>
                                {headerGroup.headers.map((header) => (
                                    <TableHead key={header.id}>
                                        {header.isPlaceholder
                                            ? null
                                            : flexRender(
                                                  header.column.columnDef
                                                      .header,
                                                  header.getContext(),
                                              )}
                                    </TableHead>
                                ))}
                            </TableRow>
                        ))}
                    </TableHeader>
                    <TableBody>
                        {table.getRowModel().rows.length ? (
                            table.getRowModel().rows.map((row) => (
                                <TableRow
                                    key={row.id}
                                    data-state={
                                        row.getIsSelected()
                                            ? 'selected'
                                            : undefined
                                    }
                                >
                                    {row.getVisibleCells().map((cell) => (
                                        <TableCell key={cell.id}>
                                            {flexRender(
                                                cell.column.columnDef.cell,
                                                cell.getContext(),
                                            )}
                                        </TableCell>
                                    ))}
                                </TableRow>
                            ))
                        ) : (
                            <TableRow>
                                <TableCell
                                    colSpan={table.getAllColumns().length}
                                    className="h-24 text-center text-muted-foreground"
                                >
                                    {emptyMessage}
                                </TableCell>
                            </TableRow>
                        )}
                    </TableBody>
                </Table>
            </div>

            <DataTablePagination
                table={table}
                totalRows={totalRows}
                manualPagination={manualPagination}
            />
        </div>
    );
}

export { DataTableColumnHeader };
export { DataTableBulkAction } from '@/components/data-table/data-table-bulk-action';
export { DataTableBulkDelete } from '@/components/data-table/data-table-bulk-delete';
export { withSelectionColumn, getSelectionColumn } from '@/components/data-table/data-table-row-selection';
