import * as React from 'react';
import { Check, ChevronsUpDown, Search } from 'lucide-react';
import { Popover as PopoverPrimitive } from 'radix-ui';

import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { cn } from '@/lib/utils';

type ComboboxOption = {
    value: string;
    label: string;
};

type ComboboxProps = {
    options: ComboboxOption[];
    /** Nilai terpilih: string (single) atau string[] (multiple). */
    value?: string | string[];
    onValueChange?: (value: string | string[]) => void;
    /** Mode multi-pilih — opsi bisa dicentang lebih dari satu, dropdown tetap terbuka. */
    multiple?: boolean;
    /** Label opsi "kosongkan pilihan" (mis. "Tanpa kategori") — tampil di atas daftar. */
    clearOptionLabel?: string;
    placeholder?: string;
    searchPlaceholder?: string;
    emptyText?: string;
    disabled?: boolean;
    className?: string;
};

/**
 * Dropdown pilihan dengan fitur pencarian (filter live berdasarkan label).
 * Dibangun di atas Radix Popover — cocok untuk daftar opsi yang panjang
 * (mis. daftar kategori), berbeda dengan `Select` Radix yang tidak bisa dicari.
 * Mode `multiple` untuk multi-select (nilai berupa array).
 */
function Combobox({
    options,
    value,
    onValueChange,
    multiple = false,
    clearOptionLabel,
    placeholder = 'Pilih...',
    searchPlaceholder = 'Cari...',
    emptyText = 'Tidak ada hasil',
    disabled,
    className,
}: ComboboxProps) {
    const [open, setOpen] = React.useState(false);
    const [search, setSearch] = React.useState('');

    // Selalu berupa array agar pemilihan mudah dibaca terlepas dari mode.
    const selectedOptions = multiple
        ? options.filter((option) => (value as string[] | undefined)?.includes(option.value))
        : options.filter((option) => option.value === value);

    const isSelected = (optionValue: string) =>
        multiple
            ? (value as string[] | undefined)?.includes(optionValue) ?? false
            : optionValue === value;

    const filtered = options.filter((option) =>
        option.label.toLowerCase().includes(search.trim().toLowerCase()),
    );

    // Reset kata kunci pencarian setiap dropdown ditutup.
    React.useEffect(() => {
        if (!open) {
            setSearch('');
        }
    }, [open]);

    const selectedCount = multiple
        ? (value as string[] | undefined)?.length ?? 0
        : selectedOptions.length;

    const selectedLabel = multiple
        ? selectedCount === 0
            ? placeholder
            : selectedOptions
                  .slice(0, 2)
                  .map((option) => option.label)
                  .join(', ') +
              (selectedCount > 2 ? ` +${selectedCount - 2}` : '')
        : selectedOptions[0]?.label ?? placeholder;

    const handleSelect = (optionValue: string) => {
        if (!multiple) {
            onValueChange?.(optionValue);
            setOpen(false);

            return;
        }

        const current = (value as string[] | undefined) ?? [];
        const next = current.includes(optionValue)
            ? current.filter((item) => item !== optionValue)
            : [...current, optionValue];

        onValueChange?.(next);
    };

    return (
        <PopoverPrimitive.Root open={open} onOpenChange={setOpen}>
            <PopoverPrimitive.Trigger asChild>
                <Button
                    type="button"
                    variant="outline"
                    role="combobox"
                    aria-expanded={open}
                    disabled={disabled}
                    className={cn(
                        'h-10 w-full justify-between font-normal',
                        selectedCount === 0 && 'text-muted-foreground',
                        className,
                    )}
                >
                    <span className="truncate">{selectedLabel}</span>
                    <ChevronsUpDown className="size-4 shrink-0 opacity-50" />
                </Button>
            </PopoverPrimitive.Trigger>
            <PopoverPrimitive.Portal>
                <PopoverPrimitive.Content
                    align="start"
                    side="bottom"
                    sideOffset={4}
                    className="z-50 w-[var(--radix-popover-trigger-width)] min-w-64 rounded-md border bg-popover p-1 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2"
                >
                    <div className="relative">
                        <Search className="pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground" />
                        <Input
                            autoFocus
                            value={search}
                            onChange={(event) => setSearch(event.target.value)}
                            placeholder={searchPlaceholder}
                            className="h-8 pl-8"
                        />
                    </div>

                    <div className="mt-1 max-h-56 overflow-y-auto">
                        {clearOptionLabel && (
                            <button
                                type="button"
                                onClick={() => {
                                    onValueChange?.(multiple ? [] : '');

                                    if (!multiple) {
                                        setOpen(false);
                                    }
                                }}
                                className={cn(
                                    'relative flex w-full cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none hover:bg-accent hover:text-accent-foreground',
                                    selectedCount === 0 && 'bg-accent text-accent-foreground',
                                )}
                            >
                                <span className="truncate">{clearOptionLabel}</span>
                                {selectedCount === 0 && (
                                    <Check className="ml-auto size-4 shrink-0 text-primary" />
                                )}
                            </button>
                        )}
                        {filtered.length === 0 ? (
                            <p className="px-2 py-6 text-center text-sm text-muted-foreground">
                                {emptyText}
                            </p>
                        ) : (
                            filtered.map((option) => (
                                <button
                                    key={option.value}
                                    type="button"
                                    onClick={() => handleSelect(option.value)}
                                    className={cn(
                                        'relative flex w-full cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none hover:bg-accent hover:text-accent-foreground',
                                        isSelected(option.value) &&
                                            'bg-accent text-accent-foreground',
                                    )}
                                >
                                    <span className="truncate">{option.label}</span>
                                    {isSelected(option.value) && (
                                        <Check className="ml-auto size-4 shrink-0 text-primary" />
                                    )}
                                </button>
                            ))
                        )}
                    </div>
                </PopoverPrimitive.Content>
            </PopoverPrimitive.Portal>
        </PopoverPrimitive.Root>
    );
}

export { Combobox };
export type { ComboboxOption };
