import { useMemo, useState } from 'react';
import { ChevronDown, ChevronLeft, ChevronRight, ChevronUp, ChevronsUpDown, Search, X } from 'lucide-react';
import { Input } from '@/components/ui/input';

export type Column<T extends Record<string, unknown>> = {
    key: string;
    header: string;
    sortable?: boolean;
    searchable?: boolean;
    render?: (row: T) => React.ReactNode;
    headerClass?: string;
    cellClass?: string;
};

export type TableFilter = {
    key: string;
    label: string;
    allLabel?: string;
    options: { label: string; value: string }[];
};

type SortState = { key: string; dir: 'asc' | 'desc' } | null;

type Props<T extends Record<string, unknown>> = {
    data: T[];
    columns: Column<T>[];
    filters?: TableFilter[];
    searchPlaceholder?: string;
    defaultPageSize?: number;
    emptyIcon?: React.ElementType;
    emptyMessage?: string;
    emptyDescription?: string;
};

const PAGE_SIZES = [10, 25, 50];

function getValue(obj: unknown, path: string): unknown {
    return path.split('.').reduce(
        (cur: unknown, key: string) => (cur != null ? (cur as Record<string, unknown>)[key] : undefined),
        obj,
    );
}

function toStr(val: unknown): string {
    if (val == null) return '';
    if (typeof val === 'object') return JSON.stringify(val);
    return String(val).toLowerCase();
}

function compareValues(a: unknown, b: unknown, dir: 'asc' | 'desc'): number {
    if (a == null && b == null) return 0;
    if (a == null) return dir === 'asc' ? 1 : -1;
    if (b == null) return dir === 'asc' ? -1 : 1;
    if (typeof a === 'number' && typeof b === 'number') return dir === 'asc' ? a - b : b - a;
    const cmp = String(a).toLowerCase().localeCompare(String(b).toLowerCase(), 'id');
    return dir === 'asc' ? cmp : -cmp;
}

export function DataTable<T extends Record<string, unknown>>({
    data,
    columns,
    filters: filterDefs = [],
    searchPlaceholder = 'Cari...',
    defaultPageSize = 10,
    emptyIcon: EmptyIcon,
    emptyMessage = 'Tidak ada data',
    emptyDescription = 'Coba ubah filter atau kata kunci pencarian',
}: Props<T>) {
    const [search, setSearch] = useState('');
    const [sort, setSort] = useState<SortState>(null);
    const [activeFilters, setActiveFilters] = useState<Record<string, string>>({});
    const [page, setPage] = useState(1);
    const [pageSize, setPageSize] = useState(defaultPageSize);

    const searchableKeys = useMemo(
        () => columns.filter((c) => c.searchable !== false && !c.render).map((c) => c.key),
        [columns],
    );

    function toggleSort(key: string) {
        setSort((prev) => {
            if (prev?.key !== key) return { key, dir: 'asc' };
            if (prev.dir === 'asc') return { key, dir: 'desc' };
            return null;
        });
        setPage(1);
    }

    function setFilter(key: string, value: string) {
        setActiveFilters((prev) => ({ ...prev, [key]: value }));
        setPage(1);
    }

    const filtered = useMemo(() => {
        let result = [...data];

        for (const [key, val] of Object.entries(activeFilters)) {
            if (!val) continue;
            result = result.filter((row) => String(getValue(row, key) ?? '') === val);
        }

        if (search.trim()) {
            const q = search.toLowerCase();
            result = result.filter((row) => searchableKeys.some((key) => toStr(getValue(row, key)).includes(q)));
        }

        if (sort) {
            result.sort((a, b) => compareValues(getValue(a, sort.key), getValue(b, sort.key), sort.dir));
        }

        return result;
    }, [data, activeFilters, search, sort, searchableKeys]);

    const totalPages = Math.max(1, Math.ceil(filtered.length / pageSize));
    const safePage = Math.min(page, totalPages);
    const pageData = filtered.slice((safePage - 1) * pageSize, safePage * pageSize);

    const pageNumbers = useMemo((): (number | 'ellipsis')[] => {
        const pages: (number | 'ellipsis')[] = [];
        if (totalPages <= 7) {
            for (let i = 1; i <= totalPages; i++) pages.push(i);
        } else {
            pages.push(1);
            if (safePage > 3) pages.push('ellipsis');
            for (let i = Math.max(2, safePage - 1); i <= Math.min(totalPages - 1, safePage + 1); i++) {
                pages.push(i);
            }
            if (safePage < totalPages - 2) pages.push('ellipsis');
            pages.push(totalPages);
        }
        return pages;
    }, [totalPages, safePage]);

    function SortIcon({ colKey }: { colKey: string }) {
        if (sort?.key !== colKey) return <ChevronsUpDown className="h-3.5 w-3.5 opacity-40" />;
        return sort.dir === 'asc' ? (
            <ChevronUp className="h-3.5 w-3.5 text-blue-600" />
        ) : (
            <ChevronDown className="h-3.5 w-3.5 text-blue-600" />
        );
    }

    return (
        <div className="overflow-hidden rounded-2xl border border-blue-100 bg-white shadow-sm dark:border-blue-900/30 dark:bg-card">
            {/* Toolbar: search + filters */}
            <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between border-b border-blue-50 px-4 py-3 dark:border-blue-900/20">
                <div className="relative max-w-xs flex-1">
                    <Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
                    <Input
                        placeholder={searchPlaceholder}
                        value={search}
                        onChange={(e) => {
                            setSearch(e.target.value);
                            setPage(1);
                        }}
                        className="pl-9 pr-8 h-9 rounded-xl border-blue-100/80 focus-visible:ring-blue-200"
                    />
                    {search && (
                        <button
                            onClick={() => {
                                setSearch('');
                                setPage(1);
                            }}
                            className="absolute right-2.5 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
                        >
                            <X className="h-3.5 w-3.5" />
                        </button>
                    )}
                </div>

                {filterDefs.length > 0 && (
                    <div className="flex flex-wrap items-center gap-3">
                        {filterDefs.map((f) => (
                            <div key={f.key} className="flex items-center gap-1.5">
                                <span className="text-xs font-medium text-zinc-650 dark:text-zinc-400">{f.label}:</span>
                                <div className="flex flex-wrap gap-1">
                                    <button
                                        onClick={() => setFilter(f.key, '')}
                                        className={`rounded-full px-3.5 py-1.5 text-xs font-medium transition-all cursor-pointer ${
                                            !activeFilters[f.key]
                                                ? 'bg-blue-600 text-white shadow-xs'
                                                : 'bg-zinc-100 text-zinc-700 hover:bg-zinc-250 hover:text-blue-700 dark:bg-zinc-800 dark:text-zinc-355 dark:hover:bg-zinc-700/80 dark:hover:text-blue-400'
                                        }`}
                                    >
                                        {f.allLabel ?? 'Semua'}
                                    </button>
                                    {f.options.map((opt) => (
                                        <button
                                            key={opt.value}
                                            onClick={() => setFilter(f.key, opt.value)}
                                            className={`rounded-full px-3.5 py-1.5 text-xs font-medium transition-all cursor-pointer ${
                                                activeFilters[f.key] === opt.value
                                                    ? 'bg-blue-600 text-white shadow-xs'
                                                    : 'bg-zinc-100 text-zinc-700 hover:bg-zinc-250 hover:text-blue-700 dark:bg-zinc-800 dark:text-zinc-355 dark:hover:bg-zinc-700/80 dark:hover:text-blue-400'
                                            }`}
                                        >
                                            {opt.label}
                                        </button>
                                    ))}
                                </div>
                            </div>
                        ))}
                    </div>
                )}
            </div>

            <div className="overflow-x-auto">
                <table className="w-full text-sm">
                        <thead>
                            <tr className="border-b border-blue-100 bg-blue-50/80 dark:border-blue-900/20 dark:bg-blue-950/30">
                                {columns.map((col) => (
                                    <th
                                        key={col.key}
                                        className={[
                                            'px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-blue-700 dark:text-blue-400',
                                            col.sortable !== false
                                                ? 'cursor-pointer select-none hover:text-blue-900 dark:hover:text-blue-200'
                                                : '',
                                            col.headerClass ?? '',
                                        ]
                                            .filter(Boolean)
                                            .join(' ')}
                                        onClick={() => col.sortable !== false && toggleSort(col.key)}
                                    >
                                        <div className="flex items-center gap-1.5">
                                            {col.header}
                                            {col.sortable !== false && <SortIcon colKey={col.key} />}
                                        </div>
                                    </th>
                                ))}
                            </tr>
                        </thead>
                        <tbody className="divide-y divide-blue-50 dark:divide-blue-900/20">
                            {pageData.length === 0 ? (
                                <tr>
                                    <td colSpan={columns.length}>
                                        <div className="flex flex-col items-center justify-center py-16 text-center">
                                            {EmptyIcon && (
                                                <div className="mb-3 flex h-14 w-14 items-center justify-center rounded-2xl bg-blue-50 dark:bg-blue-900/20">
                                                    <EmptyIcon className="h-7 w-7 text-blue-300" />
                                                </div>
                                            )}
                                            <p className="font-semibold text-foreground">{emptyMessage}</p>
                                            <p className="mt-1 text-sm text-muted-foreground">{emptyDescription}</p>
                                        </div>
                                    </td>
                                </tr>
                            ) : (
                                pageData.map((row, rowIdx) => (
                                    <tr
                                        key={(row.id as string | number | undefined) ?? rowIdx}
                                        className="transition-colors hover:bg-blue-50/40 dark:hover:bg-blue-950/20"
                                    >
                                        {columns.map((col) => (
                                            <td key={col.key} className={['px-4 py-3.5', col.cellClass ?? ''].join(' ')}>
                                                {col.render ? col.render(row) : String(getValue(row, col.key) ?? '—')}
                                            </td>
                                        ))}
                                    </tr>
                                ))
                            )}
                        </tbody>
                    </table>
                </div>

                {/* Pagination footer */}
                {filtered.length > pageSize && (
                    <div className="flex flex-wrap items-center justify-between gap-3 border-t border-blue-50 px-4 py-3 dark:border-blue-900/20">
                        <div className="flex items-center gap-2">
                            <span className="text-xs text-muted-foreground">Baris:</span>
                            <select
                                value={pageSize}
                                onChange={(e) => {
                                    setPageSize(Number(e.target.value));
                                    setPage(1);
                                }}
                                className="rounded-lg border border-input bg-background px-2 py-1 text-xs focus:outline-none focus:ring-1 focus:ring-blue-300"
                            >
                                {PAGE_SIZES.map((s) => (
                                    <option key={s} value={s}>
                                        {s}
                                    </option>
                                ))}
                            </select>
                            <span className="text-xs text-muted-foreground">
                                {(safePage - 1) * pageSize + 1}–{Math.min(safePage * pageSize, filtered.length)} dari{' '}
                                {filtered.length}
                            </span>
                        </div>

                        <div className="flex items-center gap-1">
                            <button
                                disabled={safePage === 1}
                                onClick={() => setPage((p) => p - 1)}
                                className="flex h-8 w-8 items-center justify-center rounded-lg border text-muted-foreground transition-colors hover:bg-blue-50 hover:text-blue-700 disabled:opacity-30 dark:hover:bg-blue-900/20"
                            >
                                <ChevronLeft className="h-4 w-4" />
                            </button>

                            {pageNumbers.map((p, i) =>
                                p === 'ellipsis' ? (
                                    <span
                                        key={`e-${i}`}
                                        className="flex h-8 w-8 items-center justify-center text-xs text-muted-foreground"
                                    >
                                        …
                                    </span>
                                ) : (
                                    <button
                                        key={p}
                                        onClick={() => setPage(p as number)}
                                        className={`flex h-8 w-8 items-center justify-center rounded-lg text-xs font-medium transition-colors ${
                                            safePage === p
                                                ? 'bg-blue-200 text-blue-800 shadow-sm'
                                                : 'hover:bg-blue-50 hover:text-blue-700 dark:hover:bg-blue-900/20'
                                        }`}
                                    >
                                        {p}
                                    </button>
                                ),
                            )}

                            <button
                                disabled={safePage === totalPages}
                                onClick={() => setPage((p) => p + 1)}
                                className="flex h-8 w-8 items-center justify-center rounded-lg border text-muted-foreground transition-colors hover:bg-blue-50 hover:text-blue-700 disabled:opacity-30 dark:hover:bg-blue-900/20"
                            >
                                <ChevronRight className="h-4 w-4" />
                            </button>
                        </div>
                    </div>
                )}
            </div>
    );
}
