import { Button } from '@/components/ui/button';
import {
    Command,
    CommandEmpty,
    CommandInput,
    CommandItem,
    CommandList,
} from '@/components/ui/command';
import {
    Popover,
    PopoverContent,
    PopoverTrigger,
} from '@/components/ui/popover';
import { cn } from '@/lib/utils';
import { type Category, getCategoryColorClasses } from '@/types/category';
import { __ } from '@/utils/i18n';
import * as Icons from 'lucide-react';
import {
    Check,
    ChevronsUpDown,
    HelpCircle,
    type LucideIcon,
} from 'lucide-react';
import { memo, useEffect, useRef, useState } from 'react';

const iconCache = new Map<string, LucideIcon>();

function getIconComponent(iconName?: string): LucideIcon | null {
    if (!iconName) return null;
    if (iconCache.has(iconName)) {
        return iconCache.get(iconName)!;
    }
    const icon = Icons[iconName as keyof typeof Icons] as
        | LucideIcon
        | undefined;
    if (icon) {
        iconCache.set(iconName, icon);
    }
    return icon ?? null;
}

interface CategoryComboboxProps {
    value: string | null;
    onValueChange: (value: string) => void;
    categories: Category[];
    disabled?: boolean;
    placeholder?: string;
    triggerClassName?: string;
    showUncategorized?: boolean;
    withoutChevronIcon?: boolean;
    'data-testid'?: string;
}

export function CategoryCombobox({
    value,
    onValueChange,
    categories,
    disabled = false,
    placeholder = 'Select category',
    triggerClassName,
    showUncategorized = true,
    withoutChevronIcon = false,
    'data-testid': dataTestId,
}: CategoryComboboxProps) {
    const [open, setOpen] = useState(false);
    const [filterValue, setFilterValue] = useState('');
    const listRef = useRef<HTMLDivElement>(null);

    const selectedCategory =
        value && value !== 'null'
            ? categories.find((c) => c.id === value)
            : null;

    const sortedCategories = [...categories].sort((a, b) =>
        a.name.localeCompare(b.name),
    );

    useEffect(() => {
        if (filterValue && listRef.current) {
            listRef.current.scrollTop = 0;
        }
    }, [filterValue]);

    useEffect(() => {
        if (open && listRef.current) {
            listRef.current.scrollTop = 0;
        }
        if (!open) {
            setFilterValue('');
        }
    }, [open]);

    return (
        <Popover modal open={open} onOpenChange={setOpen}>
            <PopoverTrigger asChild>
                <Button
                    variant="outline"
                    role="combobox"
                    aria-expanded={open}
                    className={cn(
                        'w-full max-w-full justify-between !pr-2 !pl-1',
                        triggerClassName,
                    )}
                    disabled={disabled}
                    onClick={(e) => e.stopPropagation()}
                    data-testid={dataTestId}
                >
                    {selectedCategory ? (
                        <div className="flex items-center gap-2 overflow-x-hidden">
                            <CategoryIcon category={selectedCategory} />
                            <span className="truncate">
                                {selectedCategory.name}
                            </span>
                        </div>
                    ) : value === 'null' ? (
                        <div className="flex items-center gap-2 truncate">
                            <div className="flex h-6 w-6 items-center justify-center rounded-full bg-zinc-100 dark:bg-zinc-800">
                                <HelpCircle className="h-3 w-3 text-zinc-500" />
                            </div>
                            <span className="truncate text-zinc-500">
                                {__('Uncategorized')}
                            </span>
                        </div>
                    ) : (
                        <span className="truncate text-muted-foreground">
                            {placeholder}
                        </span>
                    )}
                    {withoutChevronIcon === false && (
                        <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
                    )}
                </Button>
            </PopoverTrigger>
            <PopoverContent className="p-0" align="start">
                <Command>
                    <CommandInput
                        placeholder={__('Search categories...')}
                        value={filterValue}
                        onValueChange={setFilterValue}
                    />

                    <CommandList ref={listRef}>
                        <CommandEmpty>{__('No category found.')}</CommandEmpty>
                        {showUncategorized && (
                            <CommandItem
                                value="uncategorized"
                                onSelect={() => {
                                    onValueChange('null');
                                    setOpen(false);
                                }}
                            >
                                <div className="flex items-center gap-2">
                                    <div className="flex h-6 w-6 items-center justify-center rounded-full bg-zinc-100 dark:bg-zinc-800">
                                        <HelpCircle className="h-3 w-3 text-zinc-500" />
                                    </div>
                                    <span>{__('Uncategorized')}</span>
                                </div>
                                <Check
                                    className={cn(
                                        'ml-auto h-4 w-4',
                                        value === 'null'
                                            ? 'opacity-100'
                                            : 'opacity-0',
                                    )}
                                />
                            </CommandItem>
                        )}
                        {sortedCategories.map((category) => (
                            <CommandItem
                                key={category.id}
                                value={category.name}
                                onSelect={() => {
                                    onValueChange(String(category.id));
                                    setOpen(false);
                                }}
                            >
                                <div className="flex items-center gap-2">
                                    <CategoryIcon category={category} />
                                    <span className="truncate">
                                        {category.name}
                                    </span>
                                </div>
                                <Check
                                    className={cn(
                                        'ml-auto h-4 w-4',
                                        value === String(category.id)
                                            ? 'opacity-100'
                                            : 'opacity-0',
                                    )}
                                />
                            </CommandItem>
                        ))}
                    </CommandList>
                </Command>
            </PopoverContent>
        </Popover>
    );
}

export const CategoryIcon = memo(function CategoryIcon({
    category,
}: {
    category: Category;
}) {
    const colorClasses = getCategoryColorClasses(category.color);
    const iconName = category.icon;

    return (
        <div
            className={cn(
                'flex aspect-square items-center justify-center rounded-full p-1',
                colorClasses.bg,
            )}
        >
            <DynamicIcon
                name={iconName}
                className={cn(`size-4 sm:size-3.5`, colorClasses.text)}
            />
        </div>
    );
});

const DynamicIcon = memo(function DynamicIcon({
    name,
    className,
}: {
    name?: string;
    className?: string;
}) {
    const Icon = getIconComponent(name);
    if (!Icon) return null;
    return <Icon className={className} />;
});
