import {
    ChevronLeft,
    ChevronRight,
    ChevronsLeft,
    ChevronsRight,
    Search,
} 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 {
    Select,
    SelectContent,
    SelectItem,
    SelectTrigger,
    SelectValue,
} from '@/components/ui/select';

export interface PaginationState {
    current_page: number;
    last_page: number;
    per_page: number;
    total: number;
}

interface CardRenderContext<TData> {
    item: TData;
    index: number;
    isExpanded: boolean;
    onToggleExpand: () => void;
}

interface CardTableProps<TData> {
    data: TData[];
    getItemKey: (item: TData) => number | string;

    renderCard: (ctx: CardRenderContext<TData>) => React.ReactNode;
    renderSubContent: (item: TData) => React.ReactNode;

    expandedKeys: Set<number | string> | 'all';
    onToggleExpand: (key: number | string) => void;

    searchPlaceholder?: string;
    searchValue?: string;
    onSearchChange?: (value: string) => void;

    toolbar?: React.ReactNode;

    pagination?: PaginationState;
    onPageChange?: (page: number) => void;
    onPerPageChange?: (perPage: number) => void;

    emptyText?: string;
    rowClassName?: (item: TData) => string;
}

function useDebounce(callback: (value: string) => void, delay: number) {
    const timeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);

    return React.useCallback(
        (value: string) => {
            if (timeoutRef.current) {
                clearTimeout(timeoutRef.current);
            }

            timeoutRef.current = setTimeout(() => {
                callback(value);
            }, delay);
        },
        [callback, delay],
    );
}

export function CardTable<TData>({
    data,
    getItemKey,
    renderCard,
    renderSubContent,
    expandedKeys,
    onToggleExpand,
    searchPlaceholder = 'Ketikkan sesuatu...',
    searchValue,
    onSearchChange,
    toolbar,
    pagination,
    onPageChange,
    onPerPageChange,
    emptyText = 'Tidak ada data.',
    rowClassName,
}: CardTableProps<TData>) {
    const [localSearch, setLocalSearch] = React.useState(searchValue ?? '');

    React.useEffect(() => {
        setLocalSearch(searchValue ?? '');
    }, [searchValue]);

    const isServerMode = !!pagination && !!onPageChange;

    const handleSearchDebounced = useDebounce(
        (value: string) => onSearchChange?.(value),
        300,
    );

    function handleSearchChange(value: string) {
        setLocalSearch(value);

        if (isServerMode) {
            handleSearchDebounced(value);
        }
    }

    function isItemExpanded(key: number | string): boolean {
        if (expandedKeys === 'all') {
            return true;
        }

        return expandedKeys.has(key);
    }

    const totalPages = pagination?.last_page ?? 1;
    const currentPage = pagination?.current_page ?? 1;

    return (
        <>
            {(onSearchChange || toolbar || isServerMode) && (
                <div className="flex items-center gap-2">
                    {onSearchChange && (
                        <div className="relative max-w-sm flex-1">
                            <Search className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
                            <Input
                                placeholder={searchPlaceholder}
                                value={localSearch}
                                onChange={(e) =>
                                    handleSearchChange(e.target.value)
                                }
                                className="pl-9"
                            />
                        </div>
                    )}
                    {toolbar}
                    <div className="ml-auto flex items-center gap-2">
                        {isServerMode && onPerPageChange && (
                            <Select
                                value={String(pagination?.per_page ?? 25)}
                                onValueChange={(value) =>
                                    onPerPageChange(Number(value))
                                }
                            >
                                <SelectTrigger className="h-8 w-[70px]">
                                    <SelectValue />
                                </SelectTrigger>
                                <SelectContent>
                                    <SelectItem value="25">25</SelectItem>
                                    <SelectItem value="50">50</SelectItem>
                                    <SelectItem value="100">100</SelectItem>
                                    <SelectItem value="999999">
                                        Semua
                                    </SelectItem>
                                </SelectContent>
                            </Select>
                        )}
                    </div>
                </div>
            )}

            <div className="flex flex-col gap-3">
                {data.length === 0 ? (
                    <Card>
                        <CardContent className="flex h-24 items-center justify-center text-muted-foreground">
                            {emptyText}
                        </CardContent>
                    </Card>
                ) : (
                    data.map((item, index) => {
                        const key = getItemKey(item);
                        const isExpanded = isItemExpanded(key);

                        return (
                            <div key={key} className={`flex flex-col ${rowClassName?.(item) ?? ''}`}>
                                {renderCard({
                                    item,
                                    index,
                                    isExpanded,
                                    onToggleExpand: () => onToggleExpand(key),
                                })}
                                {isExpanded && (
                                    <div className="rounded-b-lg border border-t-0 bg-muted/30 p-4">
                                        {renderSubContent(item)}
                                    </div>
                                )}
                            </div>
                        );
                    })
                )}
            </div>

            {isServerMode && (
                <div className="flex items-center justify-end gap-2">
                    <span className="text-sm text-muted-foreground">
                        Halaman {currentPage} dari {totalPages}
                    </span>
                    <div className="flex items-center gap-1">
                        <Button
                            variant="outline"
                            size="sm"
                            onClick={() => onPageChange(1)}
                            disabled={currentPage <= 1}
                        >
                            <ChevronsLeft className="h-4 w-4" />
                        </Button>
                        <Button
                            variant="outline"
                            size="sm"
                            onClick={() => onPageChange(currentPage - 1)}
                            disabled={currentPage <= 1}
                        >
                            <ChevronLeft className="h-4 w-4" />
                        </Button>
                        <Button
                            variant="outline"
                            size="sm"
                            onClick={() => onPageChange(currentPage + 1)}
                            disabled={currentPage >= totalPages}
                        >
                            <ChevronRight className="h-4 w-4" />
                        </Button>
                        <Button
                            variant="outline"
                            size="sm"
                            onClick={() => onPageChange(totalPages)}
                            disabled={currentPage >= totalPages}
                        >
                            <ChevronsRight className="h-4 w-4" />
                        </Button>
                    </div>
                </div>
            )}
        </>
    );
}
