import React, { Fragment } from 'react';
import { Listbox, Transition } from '@headlessui/react';
import { Close16, ChevronDown32, ChevronUp32 } from '@carbon/icons-react';
import { twMerge } from 'tailwind-merge';
import Scrollbars from '../Scrollbars';
import Block from '../../atoms/Block';
import style from '../../../style/';
import clsx from 'clsx';
import { isEmpty } from 'lodash-es';

export interface SelectProps<T> {
    className?: string;
    label?: string;
    options: Option<T>[];
    value?: Option<T> | Option<T>[];
    disabled?: boolean;
    multiple?: boolean;
    /**
     * name for the forms.
     */
    name?: string;
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    onChange?: (value: React.SetStateAction<any>) => void;
    initialOption?: Option<T>;
    initialOptionLabel?: string;
    /**
     * Color variation that will switch between styles for the Select .
     */
    variation?: 'initial' | 'default' | 'primary' | 'secondary';
    /**
     * Placeholder that will be shown if no value is selected .
     */
    placeholder?: string;
}

export interface Option<T> {
    /**
     * Value of the select option.
     */
    value: T;
    /**
     * Text of the select option.
     */
    label: string;
    /**
     * Icon of the select option. Imported from carbon icons
     */
    icon?: JSX.Element;
}

const Select = <T,>(props: SelectProps<T>) => {
    const {
        options,
        onChange,
        value,
        className,
        initialOption,
        initialOptionLabel,
        multiple,
        variation,
        disabled,
        label,
        name,
        placeholder,
    } = props;

    const initialCss = `${style.button.init} border-gray-300`;

    const disabledCss = disabled ? ' opacity-50 cursor-not-allowed ' : '';

    const variationButtonClasses = clsx({
        [style.button.default]: variation === 'default',
        [style.button.primary]: variation === 'primary',
        [style.button.secondary]: variation === 'secondary',
    });

    const classesMerged = `${initialCss} ${variationButtonClasses} w-full pl-4 pr-10 relative ${
        className ? className : ''
    } ${disabledCss} `;

    let arrayValue: Option<T>[] = [];

    if (value) {
        arrayValue = multiple ? (value as Option<T>[]) : [value as Option<T>];
    }

    function isSelected(option: Option<T>) {
        return arrayValue.some((val) => val.value === option.value);
    }

    function handleChange(option: Option<T>) {
        isSelected(option) ? removeItem(option) : addItem(option);
    }

    function removeItem(value: Option<T>) {
        const filtered = arrayValue.filter((selected) => selected.value !== value.value);
        onChange && onChange(filtered);
    }

    function addItem(option: Option<T>) {
        onChange && onChange([...arrayValue, option]);
    }

    return (
        <Listbox disabled={disabled} value={value} onChange={multiple ? handleChange : onChange ? onChange : () => ''}>
            {({ open }) => (
                <Block name={name} className="flex flex-col flex-auto">
                    {label && <Listbox.Label className={style.label}>{label}</Listbox.Label>}
                    <Block className="relative flex-auto">
                        <Listbox.Button className={twMerge(`${classesMerged} `)}>
                            {!isEmpty(arrayValue) || !isEmpty(value) ? (
                                <Block className={multiple ? 'flex flex-wrap' : 'flex items-center'}>
                                    {arrayValue.map((valueOption) => {
                                        return multiple ? (
                                            <span
                                                key={`${valueOption.value}`}
                                                className="inline-flex items-center px-1 mb-1 mr-1 rounded cursor-pointer hover:bg-black hover:bg-opacity-10"
                                            >
                                                {valueOption.icon && (
                                                    <span className="inline-flex pr-1">{valueOption.icon}</span>
                                                )}
                                                {valueOption.label ? valueOption.label : initialOptionLabel}
                                                <div
                                                    className="ml-1 rounded-full cursor-pointer"
                                                    onClick={(e) => {
                                                        e.stopPropagation();
                                                        removeItem(valueOption);
                                                    }}
                                                >
                                                    <Close16 />
                                                </div>
                                            </span>
                                        ) : (
                                            <React.Fragment key={`${valueOption.value}`}>
                                                {valueOption.icon && (
                                                    <span className="inline-flex pr-2">{valueOption.icon}</span>
                                                )}
                                                {valueOption.label ? valueOption.label : initialOptionLabel}
                                            </React.Fragment>
                                        );
                                    })}
                                </Block>
                            ) : (
                                <span className="opacity-60">{placeholder}</span>
                            )}

                            <span className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
                                {open ? (
                                    <ChevronUp32 className="w-5 h-5" aria-hidden="true" />
                                ) : (
                                    <ChevronDown32 className="w-5 h-5" aria-hidden="true" />
                                )}
                            </span>
                        </Listbox.Button>
                        <Transition
                            as={Fragment}
                            leave="transition ease-in duration-100"
                            leaveFrom="opacity-100"
                            leaveTo="opacity-0"
                        >
                            <Listbox.Options className="absolute z-50 flex-auto w-full mt-1 overflow-hidden text-base bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
                                <Scrollbars translateContentSize={true} autoHide={false} className="max-h-60">
                                    {initialOption?.label && (
                                        <Listbox.Option
                                            className={({ active }) =>
                                                `${
                                                    active ? 'text-gray-900 bg-gray-100' : 'text-gray-800'
                                                }                 ${isSelected(initialOption) ? 'bg-blue-50' : ''}
                    cursor-pointer select-none relative py-2 px-4`
                                            }
                                            value={{
                                                value: initialOption.value,
                                                label: initialOption.label,
                                            }}
                                        >
                                            <span
                                                className={`${
                                                    isSelected(initialOption) ? 'font-medium' : 'font-normal'
                                                } block truncate`}
                                            >
                                                {initialOption.icon && (
                                                    <span className="inline-flex pr-2">{initialOption.icon}</span>
                                                )}
                                                {initialOption.label}
                                            </span>
                                        </Listbox.Option>
                                    )}
                                    {options?.map((option) => (
                                        <Listbox.Option
                                            key={`${option.value}`}
                                            className={({ active }) =>
                                                `${active ? 'text-gray-900 bg-gray-100' : 'text-gray-800'}
                 ${isSelected(option) ? 'bg-blue-50' : ''}
                    cursor-pointer select-none relative py-2 px-4`
                                            }
                                            value={option}
                                        >
                                            <span
                                                className={`
                    ${isSelected(option) ? 'font-medium' : 'font-normal'}
                    flex items-center truncate
                `}
                                            >
                                                {option.icon && <span className="inline-flex pr-2">{option.icon}</span>}
                                                {option.label}
                                            </span>
                                        </Listbox.Option>
                                    ))}
                                </Scrollbars>
                            </Listbox.Options>
                        </Transition>
                    </Block>
                </Block>
            )}
        </Listbox>
    );
};

Select.displayName = 'Select';

export default Select;
