import React, { FC, LegacyRef, RefObject } from 'react';
import { Select } from '@theorchard/frontend-react-components';
import { formatMessage } from '@theorchard/suite-frontend';
import cx from 'classnames';

export interface DropdownOption<T extends object = object> {
    value: string;
    label: string;
    icon?: FC;
    caption?: string;
    disabled?: boolean;
    data?: T;
    type?: string;
}

export interface DropdownOptionFormatContext {
    context: 'value' | 'menu';
}

export interface DropdownGroup<T extends object = object> {
    label: string;
    options?: DropdownOption<T>[];
}

export type InputChangeCallback = (term: string, actions: { action: string }) => void;

export type SelectRef = { select: {
    openMenu: () => void;
    blurInput: () => void;
} };

export interface SelectComponentsProps<T, U extends object = object> {
    children: React.ReactNode;
    setValue: (value: object) => void;
    getValue: () => T[];
    hasValue: boolean;
    options?: DropdownOption<U>[];
    getStyles?: (key: string, props: object) => object;
    innerProps?: object;
    innerRef?: LegacyRef<HTMLDivElement>;
    value?: string;
    cx?: (...args: string[]) => string;
}

export interface DropdownProps<T extends object> {
    name: string;
    value?: DropdownOption<T>;
    className?: string;
    isPlainValue?: boolean;
    options: DropdownOption<T>[];
    onChange?: (name: string, param?: string, data?: T) => void;
    clearable?: boolean;
    enabled?: boolean;
    placeholder?: string;
    maxMenuHeight?: number;
    formatOptionLabel?: (option: DropdownOption<T>, context: DropdownOptionFormatContext) => React.ReactNode;
    onInputChange?: InputChangeCallback;
    alignRight?: boolean;
    separateFirstOption?: boolean;
    loading?: boolean;
    closeMenuOnSelect?: boolean;
    menuIsOpen?: boolean;
    components?: object;
    onMenuClose?: () => void;
    selectRef?: RefObject<SelectRef>;
    isSearchable?: boolean;
}

export const CLASSNAME = 'Dropdown';
export const CLASSNAME_OPTION = `${CLASSNAME}-option`;
export const CLASSNAME_OPTION_ICON = `${CLASSNAME}-option-icon`;
const MAX_MENU_HEIGHT = 350;

// Temporary fix for missing types in FRC
interface SelectProps {
    isClearable: boolean;
    onChange: (option?: DropdownOption) => void;
    placeholder: string;
    isDisabled: boolean;
    maxMenuHeight?: number;
    formatOptionLabel?: (option: DropdownOption, context: DropdownOptionFormatContext) => React.ReactNode;
    onInputChange?: InputChangeCallback;
    isLoading?: boolean;
    loadingMessage: () => string;
    onMenuClose?: () => void;
    isOptionDisabled: (option?: DropdownOption) => boolean;
    closeMenuOnSelect?: boolean;
    menuIsOpen?: boolean;
    components?: object;
    isSearchable?: boolean;
}
const SelectEx = Select as (typeof Select & FC<SelectProps>);

export const defaultOptionLabelFormatter = (options: DropdownOption[]) => (
    <>
        {
            options.map((option: DropdownOption) => <div key={ option.label } className={ CLASSNAME_OPTION }>{option.label}</div>)
        }
    </>
);

function Dropdown<T extends object = object>({
    name,
    className,
    value,
    options,
    onChange,
    clearable = false,
    enabled = true,
    isSearchable = true,
    placeholder,
    maxMenuHeight = MAX_MENU_HEIGHT,
    formatOptionLabel,
    onInputChange,
    alignRight,
    loading,
    separateFirstOption,
    closeMenuOnSelect = true,
    components,
    selectRef,
    onMenuClose,
    menuIsOpen
}: DropdownProps<T>) {
    const optionLabelFormatter = formatOptionLabel ?? defaultOptionLabelFormatter(options);

    return (
        <div className={ cx(CLASSNAME, className, { alignRight, separateFirstOption }) } data-testid={ `${CLASSNAME}_${name}` }>
            <SelectEx
                value={ value }
                placeholder={ placeholder }
                isClearable={ clearable }
                options={ options }
                onChange={ (option: DropdownOption<T>) => onChange && onChange(name, option.value) }
                isDisabled={ !enabled }
                isSearchable={ isSearchable }
                maxMenuHeight={ maxMenuHeight }
                formatOptionLabel={ optionLabelFormatter }
                onInputChange={ onInputChange }
                isLoading={ loading }
                loadingMessage={ () => formatMessage('loading.pleaseWait') }
                isOptionDisabled={ (option: DropdownOption<T>) => option.disabled }
                closeMenuOnSelect={ closeMenuOnSelect }
                selectRef={ selectRef }
                onMenuClose={ onMenuClose }
                { ...Boolean(components) && { components } }
                menuIsOpen={ menuIsOpen }
            />
        </div>
    );
}

export default Dropdown;
