import { Checkbox } from '@/components/ui/checkbox';
import {
    DropdownMenu,
    DropdownMenuCheckboxItem,
    DropdownMenuContent,
    DropdownMenuLabel,
    DropdownMenuSeparator,
    DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import {
    Tooltip,
    TooltipContent,
    TooltipTrigger,
} from '@/components/ui/tooltip';
import type { TableParams } from '@/hooks/useDataTable';
import { cn } from '@/lib/utils';
import {
    type ColumnDef,
    type RowData,
    flexRender,
    getCoreRowModel,
    useReactTable,
} from '@tanstack/react-table';
import { AnimatePresence, motion } from 'framer-motion';
import {
    ArrowDown,
    ArrowUp,
    ArrowUpDown,
    ChevronDown,
    Columns3,
    Download,
    RefreshCw,
    Search,
    X,
} from 'lucide-react';
import { useEffect, useState } from 'react';

// ── Module augmentation so columns can declare sortable + label ───────────────
declare module '@tanstack/react-table' {
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    interface ColumnMeta<TData extends RowData, TValue> {
        sortable?: boolean;
        sortKey?: string;
        label?: string;
    }
}

// ── Types ─────────────────────────────────────────────────────────────────────

export interface TableMeta {
    current_page: number;
    last_page: number;
    total: number;
    per_page: number;
    from: number;
    to: number;
}

export interface QuickFilter {
    label: string;
    value: string;
    count?: number;
}

export interface BulkAction<TData> {
    label: string;
    icon: React.ComponentType<{ className?: string }>;
    onClick: (rows: TData[]) => void;
    variant?: 'default' | 'destructive';
}

interface DataTableProps<TData extends object> {
    columns: ColumnDef<TData, unknown>[];
    data: TData[];
    meta: TableMeta;
    params: TableParams;
    isLoading: boolean;
    isFetching: boolean;
    onParamsChange: (patch: Partial<TableParams>) => void;
    quickFilters?: QuickFilter[];
    bulkActions?: BulkAction<TData>[];
    searchPlaceholder?: string;
    noun?: string;
    getRowId?: (row: TData) => string;
    exportUrl?: string;
    toolbarSlot?: React.ReactNode;
    onRefresh?: () => void;
}

// ── Skeleton ──────────────────────────────────────────────────────────────────

function SkeletonCell({ col }: { col: number }) {
    // First data col: double-line (name + subtitle), others: single bar
    if (col === 0) {
        return (
            <div className="space-y-1.5">
                <div className="bg-muted h-3.5 w-32 animate-pulse rounded-full" />
                <div className="bg-muted/60 h-2.5 w-48 animate-pulse rounded-full" />
            </div>
        );
    }
    const widths = ['w-12', 'w-10', 'w-20', 'w-16', 'w-14', 'w-18'];
    return (
        <div
            className={cn(
                'bg-muted h-3 animate-pulse rounded-full',
                widths[col % widths.length],
            )}
        />
    );
}

// ── Sort header button ────────────────────────────────────────────────────────

function SortButton({
    columnId,
    sortKey,
    params,
    onParamsChange,
    children,
}: {
    columnId: string;
    sortKey?: string;
    params: TableParams;
    onParamsChange: (patch: Partial<TableParams>) => void;
    children: React.ReactNode;
}) {
    const key = sortKey ?? columnId;
    const active = params.sort === key;
    return (
        <button
            className={cn(
                'group flex items-center gap-1.5 whitespace-nowrap transition-colors',
                active
                    ? 'text-foreground'
                    : 'text-foreground dark:text-muted-foreground dark:hover:text-foreground',
            )}
            onClick={() =>
                onParamsChange({
                    sort: key,
                    dir: active && params.dir === 'asc' ? 'desc' : 'asc',
                })
            }
        >
            {children}
            {active ? (
                params.dir === 'asc' ? (
                    <ArrowUp className="h-3 w-3 shrink-0" />
                ) : (
                    <ArrowDown className="h-3 w-3 shrink-0" />
                )
            ) : (
                <ArrowUpDown className="h-3 w-3 shrink-0 opacity-0 transition-opacity group-hover:opacity-40" />
            )}
        </button>
    );
}

// ── Page jump dropdown ────────────────────────────────────────────────────────

function PagePicker({
    current,
    total,
    onChange,
}: {
    current: number;
    total: number;
    onChange: (page: number) => void;
}) {
    const [open, setOpen] = useState(false);
    return (
        <DropdownMenu open={open} onOpenChange={setOpen}>
            <DropdownMenuTrigger asChild>
                <button
                    className={cn(
                        'border-border flex items-center gap-1 rounded-lg border px-3 py-1.5',
                        'text-foreground text-xs font-medium transition-colors',
                        'hover:bg-muted/60 focus:outline-none',
                    )}
                >
                    Page {current} of {total}
                    <ChevronDown className="text-foreground dark:text-muted-foreground h-3 w-3" />
                </button>
            </DropdownMenuTrigger>
            <DropdownMenuContent
                align="center"
                className="max-h-56 overflow-y-auto"
            >
                <DropdownMenuLabel className="text-foreground dark:text-muted-foreground text-xs">
                    Jump to page
                </DropdownMenuLabel>
                <DropdownMenuSeparator />
                {Array.from({ length: total }, (_, i) => i + 1).map((p) => (
                    <button
                        key={p}
                        onClick={() => {
                            onChange(p);
                            setOpen(false);
                        }}
                        className={cn(
                            'flex w-full items-center rounded-sm px-3 py-1.5 text-xs transition-colors',
                            p === current
                                ? 'bg-primary/10 text-primary font-semibold'
                                : 'text-foreground hover:bg-muted/60',
                        )}
                    >
                        Page {p}
                    </button>
                ))}
            </DropdownMenuContent>
        </DropdownMenu>
    );
}

// ── Main component ────────────────────────────────────────────────────────────

export function DataTable<TData extends object>({
    columns,
    data,
    meta,
    params,
    isLoading,
    isFetching,
    onParamsChange,
    quickFilters,
    bulkActions,
    searchPlaceholder = 'Search…',
    noun = 'rows',
    getRowId,
    exportUrl,
    toolbarSlot,
    onRefresh,
}: DataTableProps<TData>) {
    const [rowSelection, setRowSelection] = useState<Record<string, boolean>>(
        {},
    );
    // True when the user explicitly chose "select all N rows across every page"
    const [allTableSelected, setAllTableSelected] = useState(false);

    // Clear "all table" flag whenever filter/search/page changes (new query context)
    useEffect(() => {
        setAllTableSelected(false);
    }, [params.filter, params.search, params.sort, params.dir]);

    const hasBulkActions = !!bulkActions?.length;

    const selectionColumn: ColumnDef<TData, unknown> = {
        id: '_select',
        header: ({ table }) => (
            <Checkbox
                checked={
                    allTableSelected || table.getIsAllPageRowsSelected()
                        ? true
                        : table.getIsSomePageRowsSelected()
                          ? 'indeterminate'
                          : false
                }
                onCheckedChange={(v) => {
                    if (!v) {
                        // Deselect everything
                        table.resetRowSelection();
                        setAllTableSelected(false);
                    } else {
                        table.toggleAllPageRowsSelected(true);
                    }
                }}
                aria-label="Select all on page"
            />
        ),
        cell: ({ row }) => (
            <Checkbox
                checked={allTableSelected || row.getIsSelected()}
                onCheckedChange={(v) => {
                    if (allTableSelected) {
                        // Deselect all first, then invert this row
                        table.resetRowSelection();
                        setAllTableSelected(false);
                        row.toggleSelected(true);
                    } else {
                        row.toggleSelected(v === true);
                    }
                }}
                aria-label={`Select row ${row.index + 1}`}
            />
        ),
        enableSorting: false,
        size: 40,
    };

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

    const table = useReactTable({
        data,
        columns: allColumns,
        getCoreRowModel: getCoreRowModel(),
        enableRowSelection: true,
        onRowSelectionChange: setRowSelection,
        manualSorting: true,
        manualPagination: true,
        manualFiltering: true,
        state: { rowSelection },
        pageCount: meta.last_page,
        // ID-based selection so it survives page navigation
        ...(getRowId ? { getRowId } : {}),
    });

    const selectedRows = table
        .getSelectedRowModel()
        .rows.map((r) => r.original);
    const selectedCount = allTableSelected ? meta.total : selectedRows.length;
    const hasSelection = allTableSelected || selectedRows.length > 0;

    // Show "Select all N in table" banner when entire current page is checked
    // but not yet all pages, and there are more rows beyond this page
    const allPageSelected =
        table.getIsAllPageRowsSelected() || allTableSelected;
    const showSelectAllBanner =
        hasBulkActions &&
        allPageSelected &&
        !allTableSelected &&
        meta.total > meta.per_page;

    // Debounced search
    const [localSearch, setLocalSearch] = useState(params.search);
    useEffect(() => {
        setLocalSearch(params.search);
    }, [params.search]);
    useEffect(() => {
        const timer = setTimeout(() => {
            if (localSearch !== params.search) {
                onParamsChange({ search: localSearch });
            }
        }, 300);
        return () => clearTimeout(timer);
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [localSearch]);

    const colCount = allColumns.length;

    return (
        <div className="pf-glass flex flex-col overflow-hidden rounded-xl">
            {/* ── Toolbar ───────────────────────────────────────────────── */}
            <div className="border-border/60 flex flex-col gap-3 border-b px-4 py-3">
                {/* Bulk action bar */}
                <AnimatePresence>
                    {hasSelection && bulkActions?.length ? (
                        <motion.div
                            key="bulk"
                            initial={{ opacity: 0, y: -6 }}
                            animate={{ opacity: 1, y: 0 }}
                            exit={{ opacity: 0, y: -6 }}
                            transition={{ duration: 0.15 }}
                            className="flex flex-wrap items-center gap-3"
                        >
                            <span className="text-foreground text-sm font-medium">
                                {selectedCount.toLocaleString()} {noun} selected
                            </span>
                            <div className="bg-border h-4 w-px" />
                            {bulkActions.map((action) => (
                                <button
                                    key={action.label}
                                    onClick={() => action.onClick(selectedRows)}
                                    className={cn(
                                        'flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-xs font-medium transition-colors',
                                        action.variant === 'destructive'
                                            ? 'bg-red-500/10 text-red-600 hover:bg-red-500/20 dark:text-red-400'
                                            : 'bg-muted/60 text-foreground hover:bg-muted',
                                    )}
                                >
                                    <action.icon className="h-3.5 w-3.5" />
                                    {action.label}
                                </button>
                            ))}
                            <button
                                onClick={() => {
                                    table.resetRowSelection();
                                    setAllTableSelected(false);
                                }}
                                className="text-foreground dark:text-muted-foreground dark:hover:text-foreground ml-auto flex items-center gap-1 text-xs transition-colors"
                            >
                                <X className="h-3.5 w-3.5" /> Clear selection
                            </button>
                        </motion.div>
                    ) : null}
                </AnimatePresence>

                {/* Select-all-in-table banner */}
                <AnimatePresence>
                    {showSelectAllBanner && (
                        <motion.div
                            key="select-all-banner"
                            initial={{ opacity: 0, height: 0 }}
                            animate={{ opacity: 1, height: 'auto' }}
                            exit={{ opacity: 0, height: 0 }}
                            transition={{ duration: 0.15 }}
                            className="overflow-hidden"
                        >
                            <div className="border-primary/20 bg-primary/5 text-foreground flex items-center gap-2 rounded-lg border px-3 py-2 text-xs">
                                <span>
                                    All {meta.per_page} {noun} on this page are
                                    selected.
                                </span>
                                <button
                                    onClick={() => setAllTableSelected(true)}
                                    className="text-primary font-semibold underline-offset-2 hover:underline"
                                >
                                    Select all {meta.total.toLocaleString()}{' '}
                                    {noun} in the table
                                </button>
                                <span className="text-foreground dark:text-muted-foreground">
                                    ·
                                </span>
                                <button
                                    onClick={() => {
                                        table.resetRowSelection();
                                        setAllTableSelected(false);
                                    }}
                                    className="text-foreground dark:text-muted-foreground dark:hover:text-foreground"
                                >
                                    Clear selection
                                </button>
                            </div>
                        </motion.div>
                    )}
                </AnimatePresence>

                {/* Quick filters + search */}
                <div className="flex flex-wrap items-center justify-between gap-2">
                    {/* Quick filter chips */}
                    {quickFilters?.length ? (
                        <div className="flex flex-wrap items-center gap-1.5">
                            {quickFilters.map((f) => (
                                <button
                                    key={f.value}
                                    onClick={() =>
                                        onParamsChange({ filter: f.value })
                                    }
                                    className={cn(
                                        'flex items-center gap-1.5 rounded-full border px-3 py-1 text-xs font-medium transition-all',
                                        params.filter === f.value
                                            ? 'border-primary bg-primary/10 text-primary'
                                            : 'border-border text-foreground dark:text-muted-foreground hover:border-border/80 hover:bg-muted/40 bg-transparent',
                                    )}
                                >
                                    {f.label}
                                    {f.count != null && (
                                        <span
                                            className={cn(
                                                'rounded-full px-1.5 py-0.5 text-[10px] font-semibold tabular-nums',
                                                params.filter === f.value
                                                    ? 'bg-primary/15 text-primary'
                                                    : 'bg-muted text-foreground dark:text-muted-foreground',
                                            )}
                                        >
                                            {f.count}
                                        </span>
                                    )}
                                </button>
                            ))}
                        </div>
                    ) : (
                        <div />
                    )}

                    {/* Right side: search + export + column visibility + slot */}
                    <div className="flex items-center gap-2">
                        {/* Search */}
                        <div className="relative">
                            <Search className="text-foreground dark:text-muted-foreground/60 absolute top-1/2 left-2.5 h-3.5 w-3.5 -translate-y-1/2" />
                            <input
                                type="search"
                                value={localSearch}
                                onChange={(e) => setLocalSearch(e.target.value)}
                                placeholder={searchPlaceholder}
                                className={cn(
                                    'border-border bg-card dark:bg-muted/40 h-8 w-56 rounded-lg border pr-3 pl-8 text-xs',
                                    'text-foreground placeholder:text-muted-foreground dark:placeholder:text-muted-foreground/60',
                                    'focus:border-primary/50 focus:ring-primary/20 focus:ring-1 focus:outline-none',
                                    'transition-colors',
                                )}
                            />
                            {localSearch && (
                                <button
                                    onClick={() => {
                                        setLocalSearch('');
                                        onParamsChange({ search: '' });
                                    }}
                                    className="text-foreground dark:text-muted-foreground/60 absolute top-1/2 right-2.5 -translate-y-1/2 transition-colors"
                                >
                                    <X className="h-3 w-3" />
                                </button>
                            )}
                        </div>

                        {/* Refresh */}
                        {onRefresh && (
                            <Tooltip>
                                <TooltipTrigger asChild>
                                    <button
                                        type="button"
                                        onClick={onRefresh}
                                        disabled={isFetching}
                                        className={cn(
                                            'border-border flex h-8 items-center justify-center rounded-lg border px-2.5',
                                            'text-foreground dark:text-muted-foreground hover:bg-muted/40 text-xs transition-colors',
                                            'disabled:opacity-50',
                                        )}
                                    >
                                        <RefreshCw
                                            className={cn(
                                                'h-3.5 w-3.5',
                                                isFetching && 'animate-spin',
                                            )}
                                        />
                                    </button>
                                </TooltipTrigger>
                                <TooltipContent side="bottom">
                                    Refresh
                                </TooltipContent>
                            </Tooltip>
                        )}

                        {/* Export */}
                        {exportUrl && (
                            <Tooltip>
                                <TooltipTrigger asChild>
                                    <button
                                        type="button"
                                        onClick={() => {
                                            window.location.href = exportUrl;
                                        }}
                                        className={cn(
                                            'border-border flex h-8 items-center gap-1.5 rounded-lg border px-2.5',
                                            'text-foreground dark:text-muted-foreground hover:bg-muted/40 text-xs transition-colors',
                                        )}
                                    >
                                        <Download className="h-3.5 w-3.5" />
                                        <span className="hidden sm:inline">
                                            Export
                                        </span>
                                    </button>
                                </TooltipTrigger>
                                <TooltipContent side="bottom">
                                    Export CSV
                                </TooltipContent>
                            </Tooltip>
                        )}

                        {/* Column visibility */}
                        <Tooltip>
                            <DropdownMenu>
                                <TooltipTrigger asChild>
                                    <DropdownMenuTrigger asChild>
                                        <button
                                            className={cn(
                                                'border-border flex h-8 items-center gap-1.5 rounded-lg border px-2.5',
                                                'text-foreground dark:text-muted-foreground hover:bg-muted/40 text-xs transition-colors',
                                            )}
                                        >
                                            <Columns3 className="h-3.5 w-3.5" />
                                            <span className="hidden sm:inline">
                                                Columns
                                            </span>
                                        </button>
                                    </DropdownMenuTrigger>
                                </TooltipTrigger>
                                <DropdownMenuContent
                                    align="end"
                                    className="w-44"
                                >
                                    <DropdownMenuLabel className="text-foreground dark:text-muted-foreground text-xs">
                                        Toggle columns
                                    </DropdownMenuLabel>
                                    <DropdownMenuSeparator />
                                    {table
                                        .getAllLeafColumns()
                                        .filter(
                                            (col) =>
                                                col.id !== '_select' &&
                                                col.id !== '_actions',
                                        )
                                        .map((col) => (
                                            <DropdownMenuCheckboxItem
                                                key={col.id}
                                                checked={col.getIsVisible()}
                                                onCheckedChange={(v) =>
                                                    col.toggleVisibility(v)
                                                }
                                                className="text-xs capitalize"
                                            >
                                                {col.columnDef.meta?.label ??
                                                    col.id.replace(/_/g, ' ')}
                                            </DropdownMenuCheckboxItem>
                                        ))}
                                </DropdownMenuContent>
                            </DropdownMenu>
                            <TooltipContent side="bottom">
                                Toggle columns
                            </TooltipContent>
                        </Tooltip>

                        {/* Custom toolbar slot */}
                        {toolbarSlot}
                    </div>
                </div>
            </div>

            {/* ── Loading bar (only while refetching with existing data) ── */}
            <div className="h-0.5 w-full overflow-hidden bg-transparent" />

            {/* ── Table ─────────────────────────────────────────────────── */}
            <div className="overflow-x-auto">
                <table className="w-full border-collapse text-sm">
                    <thead>
                        {table.getHeaderGroups().map((hg) => (
                            <tr
                                key={hg.id}
                                className="border-border/60 border-b"
                            >
                                {hg.headers.map((header) => (
                                    <th
                                        key={header.id}
                                        className={cn(
                                            'text-foreground dark:text-muted-foreground px-4 py-2.5 text-left text-xs font-medium',
                                            header.column.id === '_select' &&
                                                'w-10 px-3',
                                            header.column.id === '_actions' &&
                                                'w-10',
                                        )}
                                    >
                                        {header.isPlaceholder ? null : header
                                              .column.columnDef.meta
                                              ?.sortable ? (
                                            <SortButton
                                                columnId={header.column.id}
                                                sortKey={
                                                    header.column.columnDef.meta
                                                        ?.sortKey
                                                }
                                                params={params}
                                                onParamsChange={onParamsChange}
                                            >
                                                {flexRender(
                                                    header.column.columnDef
                                                        .header,
                                                    header.getContext(),
                                                )}
                                            </SortButton>
                                        ) : (
                                            flexRender(
                                                header.column.columnDef.header,
                                                header.getContext(),
                                            )
                                        )}
                                    </th>
                                ))}
                            </tr>
                        ))}
                    </thead>

                    <tbody>
                        {isLoading || isFetching ? (
                            Array.from({ length: 8 }).map((_, i) => (
                                <tr
                                    key={i}
                                    className="border-border/30 border-b"
                                >
                                    {Array.from({ length: colCount }).map(
                                        (_, j) => (
                                            <td
                                                key={j}
                                                className={cn(
                                                    'px-4 py-3',
                                                    j === 0 &&
                                                        hasBulkActions &&
                                                        'w-10 px-3',
                                                )}
                                            >
                                                {j === 0 && hasBulkActions ? (
                                                    <div className="bg-muted h-3.5 w-3.5 animate-pulse rounded" />
                                                ) : (
                                                    <SkeletonCell
                                                        col={
                                                            hasBulkActions
                                                                ? j - 1
                                                                : j
                                                        }
                                                    />
                                                )}
                                            </td>
                                        ),
                                    )}
                                </tr>
                            ))
                        ) : table.getRowModel().rows.length === 0 ? (
                            <tr>
                                <td
                                    colSpan={colCount}
                                    className="text-foreground dark:text-muted-foreground py-20 text-center text-sm"
                                >
                                    No {noun} found
                                    {params.search && (
                                        <>
                                            {' '}
                                            for{' '}
                                            <span className="text-foreground font-medium">
                                                "{params.search}"
                                            </span>
                                        </>
                                    )}
                                </td>
                            </tr>
                        ) : (
                            table.getRowModel().rows.map((row) => (
                                <tr
                                    key={row.id}
                                    className={cn(
                                        'border-border/30 border-b transition-colors',
                                        'hover:bg-muted/30',
                                        row.getIsSelected() && 'bg-primary/5',
                                    )}
                                >
                                    {row.getVisibleCells().map((cell) => (
                                        <td
                                            key={cell.id}
                                            className={cn(
                                                'px-4 py-3 align-middle',
                                                cell.column.id === '_select' &&
                                                    'w-10 px-3',
                                                cell.column.id === '_actions' &&
                                                    'w-10',
                                            )}
                                        >
                                            {flexRender(
                                                cell.column.columnDef.cell,
                                                cell.getContext(),
                                            )}
                                        </td>
                                    ))}
                                </tr>
                            ))
                        )}
                    </tbody>
                </table>
            </div>

            {/* ── Footer / Pagination ───────────────────────────────────── */}
            <div className="border-border/60 flex flex-wrap items-center justify-between gap-3 border-t px-4 py-3">
                <span className="text-foreground dark:text-muted-foreground text-xs tabular-nums">
                    {isLoading || isFetching
                        ? 'Loading…'
                        : meta.total === 0
                          ? `No ${noun}`
                          : `Showing ${meta.from}–${meta.to} of ${meta.total.toLocaleString()} ${noun}`}
                </span>

                <div className="flex items-center gap-2">
                    <button
                        disabled={params.page <= 1 || isLoading || isFetching}
                        onClick={() =>
                            onParamsChange({ page: params.page - 1 })
                        }
                        className={cn(
                            'border-border flex h-8 items-center gap-1 rounded-lg border px-3 text-xs font-medium transition-colors',
                            'hover:bg-muted/60 disabled:cursor-not-allowed disabled:opacity-40',
                        )}
                    >
                        ← Prev
                    </button>

                    {!isLoading && !isFetching && meta.last_page > 0 && (
                        <PagePicker
                            current={meta.current_page}
                            total={meta.last_page}
                            onChange={(page) => onParamsChange({ page })}
                        />
                    )}

                    <button
                        disabled={
                            params.page >= meta.last_page ||
                            isLoading ||
                            isFetching
                        }
                        onClick={() =>
                            onParamsChange({ page: params.page + 1 })
                        }
                        className={cn(
                            'border-border flex h-8 items-center gap-1 rounded-lg border px-3 text-xs font-medium transition-colors',
                            'hover:bg-muted/60 disabled:cursor-not-allowed disabled:opacity-40',
                        )}
                    >
                        Next →
                    </button>
                </div>
            </div>
        </div>
    );
}
