import {
    DndContext,

    KeyboardSensor,
    PointerSensor,
    useSensor,
    useSensors
} from '@dnd-kit/core';
import type { DragEndEvent } from '@dnd-kit/core';
import {
    SortableContext,
    useSortable,
    verticalListSortingStrategy,
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import {



    flexRender,
    getCoreRowModel,
    getFilteredRowModel,
    getPaginationRowModel,
    getSortedRowModel,
    useReactTable
} from '@tanstack/react-table';
import type { ColumnDef, ColumnFiltersState, SortingState } from '@tanstack/react-table';
import { GripVertical } from 'lucide-react';
import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react';
import * as React from 'react';

import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import {
    Table,
    TableBody,
    TableCell,
    TableHead,
    TableHeader,
    TableRow,
} from '@/components/ui/table';

interface DataTableProps<TData, TValue> {
    columns: ColumnDef<TData, TValue>[];
    data: TData[];
    searchKey?: string;
    searchPlaceholder?: string;
    emptyText?: string;
    onReorder?: (items: TData[]) => void;
    getRowId?: (item: TData) => string | number;
    toolbar?: React.ReactNode;
}

const DragHandleContext = React.createContext<{
    listeners?: Record<string, unknown>;
    attributes?: Record<string, string>;
}>({});

export function DragHandleTrigger() {
    const { listeners, attributes } = React.useContext(DragHandleContext);

    return (
        <Button
            variant="ghost"
            size="icon"
            className="-ml-2 cursor-grab active:cursor-grabbing"
            {...listeners}
            {...attributes}
        >
            <GripVertical className="h-4 w-4 text-muted-foreground" />
        </Button>
    );
}

function SortableTableRow({
    id,
    children,
}: {
    id: string;
    children: React.ReactNode;
}) {
    const {
        attributes,
        listeners,
        setNodeRef,
        transform,
        transition,
        isDragging,
    } = useSortable({ id });

    const style = {
        transform: CSS.Transform.toString(transform),
        transition,
        opacity: isDragging ? 0.4 : undefined,
    };

    return (
        <DragHandleContext.Provider value={{ listeners, attributes }}>
            <TableRow
                ref={setNodeRef}
                style={style}
                data-state={isDragging ? 'dragging' : undefined}
            >
                {children}
            </TableRow>
        </DragHandleContext.Provider>
    );
}

export function DataTable<TData, TValue>({
    columns,
    data,
    searchKey,
    searchPlaceholder = 'Cari...',
    emptyText = 'Tidak ada data.',
    onReorder,
    getRowId,
    toolbar,
}: DataTableProps<TData, TValue>) {
    const [sorting, setSorting] = React.useState<SortingState>([]);
    const [columnFilters, setColumnFilters] =
        React.useState<ColumnFiltersState>([]);

    const isSortable = !!onReorder && !!getRowId;

    const visibleColumns = isSortable
        ? [
            {
                id: 'drag',
                header: '',
                cell: () => <DragHandleTrigger />,
                meta: {
                    className: 'w-[40px]',
                    headerClassName: 'w-[40px]',
                },
            } as ColumnDef<TData, TValue>,
            ...columns,
        ]
        : columns;

    const sensors = useSensors(
        useSensor(PointerSensor, {
            activationConstraint: { distance: 8 },
        }),
        useSensor(KeyboardSensor),
    );

    const table = useReactTable({
        data,
        columns: visibleColumns,
        getCoreRowModel: getCoreRowModel(),
        getPaginationRowModel: getPaginationRowModel(),
        onSortingChange: setSorting,
        getSortedRowModel: getSortedRowModel(),
        onColumnFiltersChange: setColumnFilters,
        getFilteredRowModel: getFilteredRowModel(),
        state: {
            sorting,
            columnFilters,
        },
    });

    const itemIds = React.useMemo(
        () => data.map((item) => String(getRowId ? getRowId(item) : '')),
        [data, getRowId],
    );

    function handleDragEnd(event: DragEndEvent) {
        const { active, over } = event;

        if (!over || active.id === over.id) {
            return;
        }

        const oldIndex = itemIds.indexOf(String(active.id));
        const newIndex = itemIds.indexOf(String(over.id));

        if (oldIndex === -1 || newIndex === -1) {
            return;
        }

        const reordered = [...data];
        const [moved] = reordered.splice(oldIndex, 1);
        reordered.splice(newIndex, 0, moved);

        onReorder?.(reordered);
    }

    return (
        <>
            {(searchKey || toolbar) && (
                <div className="flex items-center gap-2">
                    {searchKey && (
                        <Input
                            placeholder={searchPlaceholder}
                            value={
                                (table
                                    .getColumn(searchKey)
                                    ?.getFilterValue() as string) ?? ''
                            }
                            onChange={(event) =>
                                table
                                    .getColumn(searchKey)
                                    ?.setFilterValue(event.target.value)
                            }
                            className="max-w-sm"
                        />
                    )}
                    {toolbar}
                </div>
            )}
            <Card className="bg-sidebar p-0">
                <CardContent className="p-0">
                    <Table>
                        <TableHeader>
                            {table.getHeaderGroups().map((headerGroup) => (
                                <TableRow key={headerGroup.id}>
                                    {headerGroup.headers.map((header) => (
                                        <TableHead
                                            key={header.id}
                                            className={
                                                (
                                                    header.column.columnDef
                                                        .meta as {
                                                            headerClassName?: string;
                                                        }
                                                )?.headerClassName
                                            }
                                        >
                                            {header.isPlaceholder
                                                ? null
                                                : flexRender(
                                                    header.column.columnDef
                                                        .header,
                                                    header.getContext(),
                                                )}
                                        </TableHead>
                                    ))}
                                </TableRow>
                            ))}
                        </TableHeader>
                        <TableBody>
                            {table.getRowModel().rows?.length ? (
                                isSortable ? (
                                    <DndContext
                                        sensors={sensors}
                                        onDragEnd={handleDragEnd}
                                    >
                                        <SortableContext
                                            items={itemIds}
                                            strategy={
                                                verticalListSortingStrategy
                                            }
                                        >
                                            {table
                                                .getRowModel()
                                                .rows.map((row) => (
                                                    <SortableTableRow
                                                        key={row.id}
                                                        id={String(
                                                            getRowId!(
                                                                row.original,
                                                            ),
                                                        )}
                                                    >
                                                        {row
                                                            .getVisibleCells()
                                                            .map((cell) => (
                                                                <TableCell
                                                                    key={
                                                                        cell.id
                                                                    }
                                                                    className={
                                                                        (
                                                                            cell
                                                                                .column
                                                                                .columnDef
                                                                                .meta as {
                                                                                    className?: string;
                                                                                }
                                                                        )
                                                                            ?.className
                                                                    }
                                                                >
                                                                    {flexRender(
                                                                        cell
                                                                            .column
                                                                            .columnDef
                                                                            .cell,
                                                                        cell.getContext(),
                                                                    )}
                                                                </TableCell>
                                                            ))}
                                                    </SortableTableRow>
                                                ))}
                                        </SortableContext>
                                    </DndContext>
                                ) : (
                                    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
                                                                        .columnDef
                                                                        .meta as {
                                                                            className?: string;
                                                                        }
                                                                )?.className
                                                            }
                                                        >
                                                            {flexRender(
                                                                cell.column
                                                                    .columnDef
                                                                    .cell,
                                                                cell.getContext(),
                                                            )}
                                                        </TableCell>
                                                    ))}
                                            </TableRow>
                                        ))
                                )
                            ) : (
                                <TableRow>
                                    <TableCell
                                        colSpan={visibleColumns.length}
                                        className="h-24 text-center"
                                    >
                                        {emptyText}
                                    </TableCell>
                                </TableRow>
                            )}
                        </TableBody>
                    </Table>
                </CardContent>
            </Card>

            <div className="flex items-center justify-between">
                <div className="text-sm text-muted-foreground">
                    Halaman {table.getState().pagination.pageIndex + 1} dari{' '}
                    {table.getPageCount()}
                </div>
                <div className="flex items-center gap-2">
                    <Button
                        variant="outline"
                        size="sm"
                        onClick={() => table.setPageIndex(0)}
                        disabled={!table.getCanPreviousPage()}
                    >
                        <ChevronsLeft className="h-4 w-4" />
                    </Button>
                    <Button
                        variant="outline"
                        size="sm"
                        onClick={() => table.previousPage()}
                        disabled={!table.getCanPreviousPage()}
                    >
                        <ChevronLeft className="h-4 w-4" />
                    </Button>
                    <Button
                        variant="outline"
                        size="sm"
                        onClick={() => table.nextPage()}
                        disabled={!table.getCanNextPage()}
                    >
                        <ChevronRight className="h-4 w-4" />
                    </Button>
                    <Button
                        variant="outline"
                        size="sm"
                        onClick={() => table.setPageIndex(table.getPageCount() - 1)}
                        disabled={!table.getCanNextPage()}
                    >
                        <ChevronsRight className="h-4 w-4" />
                    </Button>
                </div>
            </div>
        </>
    );
}
