import Info from '@/Components/Info';
import InputError from '@/Components/InputError';
import ShowIf from '@/Components/ShowIf';
import { Button } from '@/Components/ui/button';
import {
    Command,
    CommandEmpty,
    CommandGroup,
    CommandInput,
    CommandItem,
    CommandList,
} from '@/Components/ui/command';
import { Label } from '@/Components/ui/label';
import { PopoverContent } from '@/Components/ui/popover';
import { cn, t } from '@/lib/utils';
import { Popover, PopoverTrigger } from '@radix-ui/react-popover';
import { Check, Minus, Plus } from 'lucide-react';
import React from 'react';

interface Option {
    id: string | number;
}

interface MultiSelectInputProps<T extends Option> {
    label: string;
    listEmptyMessage: string;
    selectEmptyMessage?: string;
    availableOptions: T[];
    selectedOptions: T[];
    getAvailableOptionValue: (option: T) => string;
    renderAvailableOption: (option: T) => React.ReactNode;
    renderSelectedOption: (option: T) => React.ReactNode;
    errors?: string[] | string | Record<string, string>;
    onChange: (selectedOptions: T[]) => void;
    onSearchChange?: (search: string) => void;
    readOnly?: boolean;
    description?: string;
}

export default function MultiSelectInput<T extends Option>({
    label,
    listEmptyMessage,
    selectEmptyMessage = 'Nessun risultato',
    availableOptions,
    selectedOptions,
    getAvailableOptionValue,
    renderAvailableOption,
    renderSelectedOption,
    errors,
    onChange,
    onSearchChange,
    readOnly,
    description,
}: MultiSelectInputProps<T>) {
    const [open, setOpen] = React.useState(false);

    function onSelect(option: T) {
        const updatedOptions = selectedOptions.filter(
            (selectedOption) => selectedOption.id !== option.id,
        );
        // Value was not selected
        if (updatedOptions.length === selectedOptions.length) {
            updatedOptions.push(option);
        }
        onChange(updatedOptions);
        setOpen(false);
    }

    function removeDevice(e: React.MouseEvent, option: T) {
        e.preventDefault();
        onSelect(option);
    }

    const items = availableOptions.map((option) => {
        const handleSelect = () => onSelect(option);
        const selected = selectedOptions.find(
            (selectedOption) => selectedOption.id === option.id,
        );
        return (
            <CommandItem
                key={option.id}
                value={getAvailableOptionValue(option)}
                onSelect={handleSelect}
            >
                {renderAvailableOption(option)}
                <Check
                    className={cn(
                        'ml-auto',
                        selected ? 'opacity-100' : 'opacity-0',
                    )}
                />
            </CommandItem>
        );
    });

    const selectedItems = selectedOptions.map((option, index) => {
        const error =
            errors && typeof errors !== 'string'
                ? ((errors as Record<string, string>)[index] ?? '')
                : '';
        return (
            <li key={option.id} className="my-4">
                <div className="flex items-center justify-between gap-4 rounded-md bg-neutral-50 p-2 text-zinc-500">
                    {renderSelectedOption(option)}
                    <ShowIf condition={!readOnly}>
                        <Button
                            variant="ghost"
                            className="h-8 w-8 p-0"
                            onClick={(e) => removeDevice(e, option)}
                        >
                            <Minus />
                        </Button>
                    </ShowIf>
                </div>
                <ShowIf condition={Array.isArray(errors)}>
                    <InputError
                        className="mt-2 text-right"
                        message={t(error)}
                    />
                </ShowIf>
            </li>
        );
    });
    return (
        <div>
            <div className="flex items-center justify-between">
                <div className="flex gap-2">
                    <Label>{label}</Label>
                    <ShowIf condition={description !== null}>
                        <Info message={description} />
                    </ShowIf>
                </div>
                <ShowIf condition={!readOnly}>
                    <Popover open={open} onOpenChange={setOpen}>
                        <PopoverTrigger asChild>
                            <Button variant="ghost" className="h-8 w-8 p-0">
                                <Plus />
                            </Button>
                        </PopoverTrigger>
                        <PopoverContent className="w-full p-0">
                            <Command>
                                <CommandInput
                                    className="h-9 border-0 focus:ring-0"
                                    onValueChange={onSearchChange}
                                />
                                <CommandList>
                                    <CommandEmpty>
                                        {selectEmptyMessage}
                                    </CommandEmpty>
                                    <CommandGroup>{items}</CommandGroup>
                                </CommandList>
                            </Command>
                        </PopoverContent>
                    </Popover>
                </ShowIf>
            </div>
            <ul className="mt-2">
                {selectedItems.length ? (
                    selectedItems
                ) : (
                    <span className="text-zinc-500">{listEmptyMessage}</span>
                )}
            </ul>
            <ShowIf condition={typeof errors === 'string'}>
                <InputError
                    className="mt-2 text-right"
                    message={t(errors as string)}
                />
            </ShowIf>
        </div>
    );
}
