import type {
    ColumnDef,
    ColumnFiltersState,
    SortingState,
    VisibilityState,
    PaginationState,
} from '@tanstack/react-table';
import {
    flexRender,
    getCoreRowModel,
    getFilteredRowModel,
    getPaginationRowModel,
    getSortedRowModel,
    useReactTable,
} from '@tanstack/react-table';

import { Checkbox } from '@/components/ui/checkbox';
import {
    DropdownMenu,
    DropdownMenuCheckboxItem,
    DropdownMenuContent,
    DropdownMenuItem,
    DropdownMenuLabel,
    DropdownMenuSeparator,
    DropdownMenuSub,
    DropdownMenuSubContent,
    DropdownMenuSubTrigger,
    DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import {
    ChevronDown,
    ChevronLeft,
    ChevronRight,
    ChevronsLeft,
    ChevronsRight,
    ListFilter,
    Settings2,
    X,
} from 'lucide-react';
import React from 'react';

import {
    Empty,
    EmptyDescription,
    EmptyHeader,
    EmptyTitle,
} from '@/components/ui/empty';
import {
    Select,
    SelectContent,
    SelectItem,
    SelectTrigger,
    SelectValue,
} from '@/components/ui/select';
import {
    Table,
    TableBody,
    TableCell,
    TableHead,
    TableHeader,
    TableRow,
} from '@/components/ui/table';
import { Badge } from './ui/badge';
import { Button } from './ui/button';
import { Input } from './ui/input';

interface DataTableFilterOption {
    columnId: string;
    title: string;
    options: { label: string; value: string }[];
}

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

interface DataTableProps<TData, TValue> {
    columns: ColumnDef<TData, TValue>[];
    data: TData[];
    searchKey?: string;
    filters?: DataTableFilterOption[];
    bulkActions?: DataTableBulkAction<TData>[];
    showNumbering?: boolean;
    showSelection?: boolean;
    rowSelection?: any;
    onRowSelectionChange?: (selection: any) => void;
}

export function DataTable<TData, TValue>({
    columns,
    data,
    filters,
    bulkActions,
    showNumbering = true,
    showSelection = true,
    rowSelection = {},
    onRowSelectionChange,
}: DataTableProps<TData, TValue>) {
    const [sorting, setSorting] = React.useState<SortingState>([]);
    const [columnFilters, setColumnFilters] =
        React.useState<ColumnFiltersState>([]);
    const [columnVisibility, setColumnVisibility] =
        React.useState<VisibilityState>({});
    const [globalFilter, setGlobalFilter] = React.useState('');
    const [pagination, setPagination] = React.useState<PaginationState>({
        pageIndex: 0,
        pageSize: 10,
    });

    // Reset to page 1 when filters or search change
    React.useEffect(() => {
        setPagination((prev) => ({ ...prev, pageIndex: 0 }));
    }, [globalFilter, columnFilters]);

    const finalColumns = React.useMemo(() => {
        const computedColumns = [...columns];

        if (showNumbering) {
            const numberingColumn: ColumnDef<TData, TValue> = {
                id: 'numbering',
                header: 'No.',
                cell: ({ row }) => row.index + 1,
                enableHiding: false,
            };
            computedColumns.unshift(numberingColumn);
        }

        if (showSelection) {
            const selectColumn: ColumnDef<TData, TValue> = {
                id: 'select',
                header: ({ table }) => (
                    <Checkbox
                        checked={
                            table.getIsAllPageRowsSelected() ||
                            (table.getIsSomePageRowsSelected() &&
                                'indeterminate')
                        }
                        onCheckedChange={(value) =>
                            table.toggleAllPageRowsSelected(!!value)
                        }
                        aria-label="Select all"
                    />
                ),
                cell: ({ row }) => (
                    <Checkbox
                        checked={row.getIsSelected()}
                        onCheckedChange={(value) => row.toggleSelected(!!value)}
                        aria-label="Select row"
                    />
                ),
                enableSorting: false,
                enableHiding: false,
            };
            computedColumns.unshift(selectColumn);
        }

        return computedColumns;
    }, [columns, showNumbering, showSelection]);

    const table = useReactTable({
        data,
        columns: finalColumns,
        getCoreRowModel: getCoreRowModel(),
        getPaginationRowModel: getPaginationRowModel(),
        onSortingChange: setSorting,
        getSortedRowModel: getSortedRowModel(),
        onColumnFiltersChange: setColumnFilters,
        getFilteredRowModel: getFilteredRowModel(),
        onColumnVisibilityChange: setColumnVisibility,
        onRowSelectionChange: onRowSelectionChange,
        onGlobalFilterChange: setGlobalFilter,
        state: {
            sorting,
            columnFilters,
            columnVisibility,
            rowSelection,
            globalFilter,
            pagination,
        },
        onPaginationChange: setPagination,
        autoResetPageIndex: false,
    });

    const selectedRows = table.getFilteredSelectedRowModel().rows;

    return (
        <div className="space-y-4">
            <div className="flex flex-col justify-between gap-4 sm:flex-row sm:items-center">
                <div className="flex flex-1 flex-col items-stretch gap-2 sm:flex-row sm:items-center">
                    <Input
                        placeholder="Cari..."
                        value={globalFilter ?? ''}
                        onChange={(event) =>
                            setGlobalFilter(event.target.value)
                        }
                        className="max-w-sm"
                    />

                    {columnFilters.length > 0 && (
                        <div className="flex flex-wrap items-center gap-2">
                            {columnFilters.map((filter) => {
                                const filterDef = filters?.find(
                                    (f) => f.columnId === filter.id,
                                );
                                const option = filterDef?.options.find(
                                    (o) =>
                                        String(o.value) ===
                                        String(filter.value),
                                );
                                const label = option
                                    ? option.label
                                    : String(filter.value);

                                return (
                                    <Badge
                                        key={filter.id}
                                        variant="secondary"
                                        className="h-8 gap-1 pr-1 font-normal text-nowrap"
                                    >
                                        <span className="mr-1 text-muted-foreground">
                                            {filterDef?.title || filter.id}:
                                        </span>
                                        {label}
                                        <Button
                                            variant="ghost"
                                            size="icon"
                                            className="h-4 w-4 hover:bg-transparent"
                                            onClick={() =>
                                                table
                                                    .getColumn(filter.id)
                                                    ?.setFilterValue(undefined)
                                            }
                                        >
                                            <X className="h-3 w-3" />
                                        </Button>
                                    </Badge>
                                );
                            })}
                            <Button
                                variant="ghost"
                                size="sm"
                                onClick={() => {
                                    setColumnFilters([]);
                                    setGlobalFilter('');
                                }}
                                className="h-8 px-2 text-xs"
                            >
                                Reset Filter
                                <X className="ml-1 h-3 w-3" />
                            </Button>
                        </div>
                    )}
                </div>

                <div className="ml-auto flex items-center gap-2">
                    {selectedRows.length > 0 && bulkActions && (
                        <DropdownMenu>
                            <DropdownMenuTrigger asChild>
                                <Button
                                    variant="secondary"
                                    className="animate-in gap-2 transition-all fade-in slide-in-from-left-2"
                                >
                                    Aksi Massal ({selectedRows.length})
                                    <ChevronDown className="h-4 w-4" />
                                </Button>
                            </DropdownMenuTrigger>
                            <DropdownMenuContent align="end">
                                {bulkActions.map((action, i) => (
                                    <DropdownMenuItem
                                        key={i}
                                        onClick={() =>
                                            action.onClick(
                                                selectedRows.map(
                                                    (r) => r.original,
                                                ),
                                            )
                                        }
                                        className={
                                            action.variant === 'destructive'
                                                ? 'text-red-600 focus:text-red-600'
                                                : ''
                                        }
                                    >
                                        {action.icon && (
                                            <action.icon className="mr-2 h-4 w-4" />
                                        )}
                                        {action.label}
                                    </DropdownMenuItem>
                                ))}
                            </DropdownMenuContent>
                        </DropdownMenu>
                    )}

                    {filters && filters.length > 0 && (
                        <DropdownMenu>
                            <DropdownMenuTrigger asChild>
                                <Button variant="outline" className="gap-2">
                                    <ListFilter className="h-4 w-4" />
                                    Filter
                                </Button>
                            </DropdownMenuTrigger>
                            <DropdownMenuContent align="end" className="w-48">
                                <DropdownMenuLabel>
                                    Filter Berdasarkan
                                </DropdownMenuLabel>
                                <DropdownMenuSeparator />
                                {filters.map((filter) => {
                                    const column = table.getColumn(
                                        filter.columnId,
                                    );

                                    if (!column) {
                                        return null;
                                    }

                                    return (
                                        <DropdownMenuSub key={filter.columnId}>
                                            <DropdownMenuSubTrigger>
                                                <span>{filter.title}</span>
                                            </DropdownMenuSubTrigger>
                                            <DropdownMenuSubContent>
                                                <DropdownMenuItem
                                                    onSelect={(e) =>
                                                        e.preventDefault()
                                                    }
                                                    onClick={() =>
                                                        column.setFilterValue(
                                                            undefined,
                                                        )
                                                    }
                                                >
                                                    Semua
                                                </DropdownMenuItem>
                                                <DropdownMenuSeparator />
                                                {filter.options.map(
                                                    (option) => (
                                                        <DropdownMenuItem
                                                            key={option.value}
                                                            onSelect={(e) =>
                                                                e.preventDefault()
                                                            }
                                                            onClick={() => {
                                                                if (
                                                                    option.value ===
                                                                    'true'
                                                                ) {
                                                                    column.setFilterValue(
                                                                        true,
                                                                    );
                                                                } else if (
                                                                    option.value ===
                                                                    'false'
                                                                ) {
                                                                    column.setFilterValue(
                                                                        false,
                                                                    );
                                                                } else {
                                                                    column.setFilterValue(
                                                                        option.value,
                                                                    );
                                                                }
                                                            }}
                                                        >
                                                            {option.label}
                                                        </DropdownMenuItem>
                                                    ),
                                                )}
                                            </DropdownMenuSubContent>
                                        </DropdownMenuSub>
                                    );
                                })}
                            </DropdownMenuContent>
                        </DropdownMenu>
                    )}

                    <DropdownMenu>
                        <DropdownMenuTrigger asChild>
                            <Button variant="outline" className="gap-2">
                                <Settings2 className="h-4 w-4" />
                                Kolom
                            </Button>
                        </DropdownMenuTrigger>
                        <DropdownMenuContent align="end" className="w-48">
                            <DropdownMenuLabel>
                                Tampilan Kolom
                            </DropdownMenuLabel>
                            <DropdownMenuSeparator />
                            {table
                                .getAllColumns()
                                .filter((column) => column.getCanHide())
                                .map((column) => {
                                    return (
                                        <DropdownMenuCheckboxItem
                                            key={column.id}
                                            className="capitalize"
                                            checked={column.getIsVisible()}
                                            onCheckedChange={(value) =>
                                                column.toggleVisibility(!!value)
                                            }
                                            onSelect={(e) => e.preventDefault()}
                                        >
                                            {(column.columnDef.meta as any)
                                                ?.title ||
                                                (typeof column.columnDef
                                                    .header === 'string'
                                                    ? column.columnDef.header
                                                    : String(column.id).replace(
                                                          /_/g,
                                                          ' ',
                                                      ))}
                                        </DropdownMenuCheckboxItem>
                                    );
                                })}
                        </DropdownMenuContent>
                    </DropdownMenu>
                </div>
            </div>

            <div className="overflow-x-auto rounded-md border bg-background">
                <Table>
                    <TableHeader>
                        {table.getHeaderGroups().map((headerGroup) => (
                            <TableRow key={headerGroup.id}>
                                {headerGroup.headers.map((header) => {
                                    return (
                                        <TableHead
                                            key={header.id}
                                            className={
                                                header.column.id === 'select' ||
                                                header.column.id === 'numbering'
                                                    ? 'w-10 text-center'
                                                    : ''
                                            }
                                        >
                                            {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'
                                    }
                                >
                                    {row.getVisibleCells().map((cell) => (
                                        <TableCell
                                            key={cell.id}
                                            className={
                                                cell.column.id === 'select' ||
                                                cell.column.id === 'numbering'
                                                    ? 'w-10 text-center'
                                                    : ''
                                            }
                                        >
                                            {flexRender(
                                                cell.column.columnDef.cell,
                                                cell.getContext(),
                                            )}
                                        </TableCell>
                                    ))}
                                </TableRow>
                            ))
                        ) : (
                            <TableRow>
                                <TableCell
                                    colSpan={finalColumns.length}
                                    className="h-24 text-center"
                                >
                                    <Empty>
                                        <EmptyHeader>
                                            <EmptyTitle>Ooops...</EmptyTitle>
                                            <EmptyDescription>
                                                Tidak ada data yang ditemukan.
                                            </EmptyDescription>
                                        </EmptyHeader>
                                    </Empty>
                                </TableCell>
                            </TableRow>
                        )}
                    </TableBody>
                </Table>
            </div>

            <div className="flex flex-col items-center justify-between gap-4 py-4 sm:flex-row">
                <div className="text-sm text-muted-foreground">
                    {table.getFilteredSelectedRowModel().rows.length} dari{' '}
                    {table.getFilteredRowModel().rows.length} baris dipilih.
                </div>
                <div className="flex flex-col items-center gap-4 sm:flex-row sm:gap-6 lg:gap-8">
                    <div className="flex items-center gap-2">
                        <p className="text-sm font-medium">Baris per halaman</p>
                        <Select
                            value={`${table.getState().pagination.pageSize}`}
                            onValueChange={(value) => {
                                table.setPageSize(Number(value));
                            }}
                        >
                            <SelectTrigger className="h-8 w-[70px]">
                                <SelectValue
                                    placeholder={
                                        table.getState().pagination.pageSize
                                    }
                                />
                            </SelectTrigger>
                            <SelectContent side="top">
                                {[10, 20, 30, 40, 50].map((pageSize) => (
                                    <SelectItem
                                        key={pageSize}
                                        value={`${pageSize}`}
                                    >
                                        {pageSize}
                                    </SelectItem>
                                ))}
                            </SelectContent>
                        </Select>
                    </div>
                    <div className="flex items-center justify-center text-sm font-medium">
                        {table.getFilteredRowModel().rows.length > 0 ? (
                            <>
                                Menampilkan{' '}
                                {table.getState().pagination.pageIndex *
                                    table.getState().pagination.pageSize +
                                    1}{' '}
                                sampai{' '}
                                {Math.min(
                                    (table.getState().pagination.pageIndex +
                                        1) *
                                        table.getState().pagination.pageSize,
                                    table.getFilteredRowModel().rows.length,
                                )}{' '}
                                dari {table.getFilteredRowModel().rows.length}{' '}
                                data
                            </>
                        ) : (
                            'Tidak ada data'
                        )}
                    </div>
                    <div className="flex items-center space-x-2">
                        <Button
                            variant="outline"
                            className="hidden h-8 w-8 p-0 lg:flex"
                            onClick={() => table.setPageIndex(0)}
                            disabled={!table.getCanPreviousPage()}
                        >
                            <span className="sr-only">Halaman pertama</span>
                            <ChevronsLeft className="h-4 w-4" />
                        </Button>
                        <Button
                            variant="outline"
                            className="h-8 w-8 p-0"
                            onClick={() => table.previousPage()}
                            disabled={!table.getCanPreviousPage()}
                        >
                            <span className="sr-only">Halaman sebelumnya</span>
                            <ChevronLeft className="h-4 w-4" />
                        </Button>
                        <Button
                            variant="outline"
                            className="h-8 w-8 p-0"
                            onClick={() => table.nextPage()}
                            disabled={!table.getCanNextPage()}
                        >
                            <span className="sr-only">Halaman selanjutnya</span>
                            <ChevronRight className="h-4 w-4" />
                        </Button>
                        <Button
                            variant="outline"
                            className="hidden h-8 w-8 p-0 lg:flex"
                            onClick={() =>
                                table.setPageIndex(table.getPageCount() - 1)
                            }
                            disabled={!table.getCanNextPage()}
                        >
                            <span className="sr-only">Halaman terakhir</span>
                            <ChevronsRight className="h-4 w-4" />
                        </Button>
                    </div>
                </div>
            </div>
        </div>
    );
}
