import {
    flexRender,
    getCoreRowModel,
    getFilteredRowModel,
    getPaginationRowModel,
    getSortedRowModel,
    useReactTable,
} from '@tanstack/react-table';
import type {
    Column,
    ColumnDef,
    RowSelectionState,
    SortingState,
    VisibilityState,
} from '@tanstack/react-table';
import {
    ArrowDown,
    ArrowUp,
    ChevronsUpDown,
    Search,
    SlidersHorizontal,
    Trash2,
} from 'lucide-react';
import { useState } from 'react';
import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import {
    Dialog,
    DialogContent,
    DialogDescription,
    DialogFooter,
    DialogHeader,
    DialogTitle,
} from '@/components/ui/dialog';
import {
    DropdownMenu,
    DropdownMenuCheckboxItem,
    DropdownMenuContent,
    DropdownMenuLabel,
    DropdownMenuSeparator,
    DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { Input } from '@/components/ui/input';
import {
    Pagination,
    PaginationContent,
    PaginationEllipsis,
    PaginationItem,
    PaginationLink,
    PaginationNext,
    PaginationPrevious,
} from '@/components/ui/pagination';
import {
    Table,
    TableBody,
    TableCell,
    TableHead,
    TableHeader,
    TableRow,
} from '@/components/ui/table';
import { getPaginationItems } from '@/lib/pagination';
import { cn } from '@/lib/utils';

type DataTableProps<TData> = {
    // `any` sengaja: columnHelper menghasilkan array beragam TValue per kolom,
    // sehingga tidak bisa disempitkan ke satu TValue tunggal.
    columns: ColumnDef<TData, any>[];
    data: TData[];
    searchKey?: string | string[];
    searchPlaceholder?: string;
    // Kolom yang tersedia di dropdown "Tampilkan Kolom" tetapi disembunyikan
    // secara default (mis. created_at / updated_at).
    hiddenColumns?: string[];
    // Matikan pagination client bila halaman sudah memakai pagination server-side
    // (mis. inbox pesan) — DataTable menampilkan semua data yang diterima.
    clientPagination?: boolean;
    // Aktifkan row selection + tombol "Hapus Terpilih" (bulk delete). Callback
    // menerima id baris yang terpilih — halaman memanggil route bulk-destroy.
    onBulkDelete?: (ids: number[]) => void;
    // Baris yang tidak boleh dipilih (mis. akun sendiri di tabel users).
    isRowSelectable?: (row: TData) => boolean;
    // Slot tambahan di toolbar — dirender inline sejajar dengan search, di kiri sebelum bulk delete.
    // Digunakan untuk filter custom (mis. blog: status & kategori) agar tetap satu baris dengan Tampilkan Kolom di kanan.
    toolbarExtra?: React.ReactNode;
};

export function DataTable<TData>({
    columns,
    data,
    searchKey,
    searchPlaceholder = 'Cari...',
    hiddenColumns = [],
    clientPagination = true,
    onBulkDelete,
    isRowSelectable,
    toolbarExtra,
}: DataTableProps<TData>) {
    const [sorting, setSorting] = useState<SortingState>([]);
    const [columnVisibility, setColumnVisibility] = useState<VisibilityState>(
        () => Object.fromEntries(hiddenColumns.map((id) => [id, false])),
    );
    const [globalFilter, setGlobalFilter] = useState('');
    const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
    const [bulkIds, setBulkIds] = useState<number[]>([]);

    // Kolom checkbox untuk row selection (pola shadcn data-table) — hanya aktif
    // bila halaman menyediakan onBulkDelete.
    const selectColumn: ColumnDef<TData, any> = {
        id: 'select',
        enableHiding: false,
        enableSorting: false,
        header: ({ table }) => (
            <Checkbox
                checked={
                    table.getIsAllPageRowsSelected() ||
                    (table.getIsSomePageRowsSelected() && 'indeterminate')
                }
                onCheckedChange={(value) =>
                    table.toggleAllPageRowsSelected(!!value)
                }
                aria-label="Pilih semua baris"
            />
        ),
        cell: ({ row }) => (
            <Checkbox
                checked={row.getIsSelected()}
                disabled={
                    isRowSelectable ? !isRowSelectable(row.original) : false
                }
                onCheckedChange={(value) => row.toggleSelected(!!value)}
                aria-label="Pilih baris"
            />
        ),
    };

    const table = useReactTable({
        data,
        columns: onBulkDelete ? [selectColumn, ...columns] : columns,
        state: {
            sorting,
            columnVisibility,
            globalFilter,
            rowSelection,
        },
        onSortingChange: setSorting,
        onColumnVisibilityChange: setColumnVisibility,
        onGlobalFilterChange: setGlobalFilter,
        onRowSelectionChange: setRowSelection,
        // id stabil → selection bertahan lintas halaman (bukan index baris).
        getRowId: (originalRow) => String((originalRow as { id: number }).id),
        getCoreRowModel: getCoreRowModel(),
        getSortedRowModel: getSortedRowModel(),
        // Dipakai global search di toolbar (kotak pencarian di atas tabel).
        getFilteredRowModel: getFilteredRowModel(),
        ...(clientPagination
            ? { getPaginationRowModel: getPaginationRowModel() }
            : {}),
        globalFilterFn: (row, _columnId, filterValue) => {
            // Tanpa searchKey atau filter kosong → semua baris lolos.
            if (!searchKey || !filterValue) {
                return true;
            }

            const keys = Array.isArray(searchKey) ? searchKey : [searchKey];
            const needle = String(filterValue).toLowerCase();

            return keys.some((key) =>
                String(row.getValue(key) ?? '')
                    .toLowerCase()
                    .includes(needle),
            );
        },
        ...(clientPagination
            ? {
                  initialState: {
                      pagination: {
                          pageSize: 10,
                      },
                  },
              }
            : {}),
    });

    const { pageIndex, pageSize } = table.getState().pagination;
    const pageCount = table.getPageCount();
    const totalRows = table.getFilteredRowModel().rows.length;
    const rangeStart = totalRows === 0 ? 0 : pageIndex * pageSize + 1;
    const rangeEnd = Math.min((pageIndex + 1) * pageSize, totalRows);

    // Core row model (bukan filtered) agar baris terpilih tetap terhitung walau
    // tersaring dari hasil global search.
    const selectedRows = table
        .getCoreRowModel()
        .flatRows.filter((row) => row.getIsSelected());
    const selectedIds = selectedRows.map(
        (row) => (row.original as { id: number }).id,
    );

    const getColumnLabel = (column: Column<TData, any>) => {
        const rawHeader = column.columnDef.header;

        return typeof rawHeader === 'string' ? rawHeader : column.id;
    };

    return (
        <div className="space-y-4">
            {/* Toolbar: live search + bulk delete + toggle visibilitas kolom */}
            <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
                <div className="flex flex-wrap items-center gap-3">
                    {searchKey && (
                        <div className="relative w-full max-w-md">
                            <Search className="pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2 text-muted-foreground" />
                            <Input
                                type="search"
                                value={globalFilter}
                                onChange={(event) =>
                                    setGlobalFilter(event.target.value)
                                }
                                placeholder={searchPlaceholder}
                                aria-label={searchPlaceholder}
                                className="pl-9"
                            />
                        </div>
                    )}

                    {/* Slot filter tambahan — sejajar inline dengan search */}
                    {toolbarExtra && (
                        <div className="flex flex-wrap items-center gap-2">
                            {toolbarExtra}
                        </div>
                    )}

                    {onBulkDelete && selectedIds.length > 0 && (
                        <Button
                            type="button"
                            variant="destructive"
                            size="xs"
                            onClick={() => setBulkIds(selectedIds)}
                        >
                            <Trash2 className="size-3" />
                            Hapus Terpilih ({selectedIds.length})
                        </Button>
                    )}
                </div>

                <DropdownMenu>
                    <DropdownMenuTrigger asChild>
                        <Button
                            variant="outline"
                            size="sm"
                            className="sm:ml-auto"
                        >
                            <SlidersHorizontal className="size-4" />
                            Tampilkan Kolom
                        </Button>
                    </DropdownMenuTrigger>
                    <DropdownMenuContent align="end" className="w-48">
                        <DropdownMenuLabel className="text-xs font-medium text-muted-foreground">
                            Pilih kolom yang tampil
                        </DropdownMenuLabel>
                        <DropdownMenuSeparator />
                        {table
                            .getAllColumns()
                            .filter((column) => column.getCanHide())
                            .map((column) => {
                                const label = getColumnLabel(column);

                                return (
                                    <DropdownMenuCheckboxItem
                                        key={column.id}
                                        className="capitalize"
                                        checked={column.getIsVisible()}
                                        onCheckedChange={(value) =>
                                            column.toggleVisibility(!!value)
                                        }
                                    >
                                        {label}
                                    </DropdownMenuCheckboxItem>
                                );
                            })}
                    </DropdownMenuContent>
                </DropdownMenu>
            </div>

            <div className="overflow-x-auto rounded-lg border">
                <Table className="min-w-[640px]">
                    <TableHeader>
                        {table.getHeaderGroups().map((headerGroup) => (
                            <TableRow key={headerGroup.id}>
                                {headerGroup.headers.map((header) => {
                                    const sorted = header.column.getIsSorted();

                                    return (
                                        <TableHead
                                            key={header.id}
                                            className="text-xs font-semibold tracking-wide text-muted-foreground"
                                        >
                                            {header.isPlaceholder ? null : header.column.getCanSort() ? (
                                                <Button
                                                    type="button"
                                                    variant="ghost"
                                                    size="sm"
                                                    className={cn(
                                                        '-ml-3 h-8 text-xs font-semibold tracking-wide text-muted-foreground hover:bg-primary hover:text-white dark:hover:bg-primary dark:hover:text-white focus-visible:bg-primary focus-visible:text-white',
                                                        sorted &&
                                                            'text-foreground',
                                                    )}
                                                    onClick={() =>
                                                        header.column.toggleSorting(
                                                            sorted === 'asc',
                                                        )
                                                    }
                                                >
                                                    {flexRender(
                                                        header.column.columnDef
                                                            .header,
                                                        header.getContext(),
                                                    )}
                                                    {sorted === 'asc' ? (
                                                        <ArrowUp className="ml-2 size-3.5" />
                                                    ) : sorted === 'desc' ? (
                                                        <ArrowDown className="ml-2 size-3.5" />
                                                    ) : (
                                                        <ChevronsUpDown className="ml-2 size-3.5" />
                                                    )}
                                                </Button>
                                            ) : (
                                                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'
                                    }
                                >
                                    {row.getVisibleCells().map((cell) => (
                                        <TableCell key={cell.id}>
                                            {flexRender(
                                                cell.column.columnDef.cell,
                                                cell.getContext(),
                                            )}
                                        </TableCell>
                                    ))}
                                </TableRow>
                            ))
                        ) : (
                            <TableRow>
                                <TableCell
                                    colSpan={
                                        table.getVisibleLeafColumns().length
                                    }
                                    className="h-24 text-center"
                                >
                                    Tidak ada data.
                                </TableCell>
                            </TableRow>
                        )}
                    </TableBody>
                </Table>
            </div>

            {clientPagination && (
                <div className="flex flex-wrap items-center justify-between gap-4 text-sm text-muted-foreground">
                    <span>
                        Menampilkan {rangeStart}–{rangeEnd} dari {totalRows}{' '}
                        data
                    </span>
                    {pageCount > 1 && (
                        <Pagination className="w-auto">
                            <PaginationContent>
                                <PaginationItem>
                                    <PaginationPrevious
                                        href="#"
                                        aria-disabled={
                                            !table.getCanPreviousPage()
                                        }
                                        className={
                                            !table.getCanPreviousPage()
                                                ? 'pointer-events-none opacity-50'
                                                : undefined
                                        }
                                        onClick={(event) => {
                                            event.preventDefault();
                                            table.previousPage();
                                        }}
                                    />
                                </PaginationItem>

                                {getPaginationItems(
                                    pageIndex + 1,
                                    pageCount,
                                ).map((item, index) =>
                                    item === 'ellipsis' ? (
                                        <PaginationItem
                                            key={`ellipsis-${index}`}
                                        >
                                            <PaginationEllipsis />
                                        </PaginationItem>
                                    ) : (
                                        <PaginationItem key={item}>
                                            <PaginationLink
                                                href="#"
                                                isActive={
                                                    item === pageIndex + 1
                                                }
                                                onClick={(event) => {
                                                    event.preventDefault();
                                                    table.setPageIndex(
                                                        item - 1,
                                                    );
                                                }}
                                            >
                                                {item}
                                            </PaginationLink>
                                        </PaginationItem>
                                    ),
                                )}

                                <PaginationItem>
                                    <PaginationNext
                                        href="#"
                                        aria-disabled={!table.getCanNextPage()}
                                        className={
                                            !table.getCanNextPage()
                                                ? 'pointer-events-none opacity-50'
                                                : undefined
                                        }
                                        onClick={(event) => {
                                            event.preventDefault();
                                            table.nextPage();
                                        }}
                                    />
                                </PaginationItem>
                            </PaginationContent>
                        </Pagination>
                    )}
                </div>
            )}

            {/* Dialog konfirmasi hapus massal */}
            <Dialog
                open={bulkIds.length > 0}
                onOpenChange={(open) => !open && setBulkIds([])}
            >
                <DialogContent>
                    <DialogHeader>
                        <DialogTitle>
                            Hapus {bulkIds.length} data terpilih?
                        </DialogTitle>
                        <DialogDescription>
                            Data yang dipilih akan dihapus. Tindakan ini tidak
                            dapat dibatalkan dari panel admin.
                        </DialogDescription>
                    </DialogHeader>
                    <DialogFooter>
                        <Button
                            variant="outline"
                            onClick={() => setBulkIds([])}
                        >
                            Batal
                        </Button>
                        <Button
                            variant="destructive"
                            onClick={() => {
                                onBulkDelete?.(bulkIds);
                                setBulkIds([]);
                            }}
                        >
                            Ya, hapus
                        </Button>
                    </DialogFooter>
                </DialogContent>
            </Dialog>
        </div>
    );
}
