import React, {FC, useMemo, useReducer} from "react";
import {MultiSelectState, MultiSelectStateProps} from "./MultiSelectState";
import {MultiSelectSearch, MultiSelectSearchProps, SelectOption} from "./MultiSelectSearch";
import {fieldsCache} from "../FieldsCache";
import {Omit} from "react-redux";
import {map} from "lodash";
import {mapLocalMetaFieldOptions} from "../cards/FieldHelpers";

export type MultiSelectProps<Option extends SelectOption> = {
    options: MultiSelectSearchProps<Option>['options'],
    onSelectedValue: (value: Option) => void,
    onRemovedValue: (value: Option) => void,
    SelectStateProps?: MultiSelectStateProps,
    SelectSearchProps?: Omit<MultiSelectSearchProps<Option>, 'options' | 'selectedValues' | 'onSelectedValue' | 'autoFocusTrigger'>,
    stateFirst?: boolean,
} & ({
    value?: SelectOption['id'][],
} | {
    value?: SelectOption['id']
})

type ReducerAction<T> =
    | { type: 'add'; value: T }
    | { type: 'remove'; value: T };

const reducer = <T extends SelectOption>(state: T[], action: ReducerAction<T>): T[] => {
    switch (action.type) {
        case 'add':
            return [...state, action.value];
        case 'remove':
            return state.filter(item => item !== action.value);
        default:
            console.error('Unknown action type:', (action as any).type);
            return state;
    }
}

const emptyProps = {}

function isRemote(options: any) {
    return typeof options === 'object' && options.type === 'remote' && (('where' in options) || ('path' in options));
}

export const MultiSelect = <Option extends SelectOption = SelectOption>({
                                                                            options,
                                                                            value = [],
                                                                            onSelectedValue,
                                                                            onRemovedValue,
                                                                            SelectStateProps,
                                                                            SelectSearchProps,
                                                                            stateFirst = true
                                                                        }: MultiSelectProps<Option>) => {
    const values = Array.isArray(value) ? value : (value ? [value] : []);

    const isLocalOptions = options => !isRemote(options) && !Array.isArray(options);
    const optionsDetectedAsLocal = isLocalOptions(options)

    if (optionsDetectedAsLocal && typeof options === 'object') {
        // convert disctionary to an array of option objects
        options = mapLocalMetaFieldOptions(options)
    }

    const selectedValues: Option[] = useMemo(
        () => {
            if (Array.isArray(options)) {
                return values.map(id => options.find(option => option.id === id)).filter(Boolean) as Option[]
            } else {
                return values.map(id => {
                    if (typeof id === 'object' && 'id' in id) {
                        // for custom objects, this is NOT the same as
                        // getting it from the fieldsCache.get()
                        return {id: (id as Record<any, any>).id, value: id}
                    }
                    if (fieldsCache.has(id)) {
                        return fieldsCache.get<Option>(id);
                    }

                    console.warn(`Option with id ${id} not found in cache or options array`);
                }).filter(Boolean) as Option[];
            }
        },
        [value]
    )
    const shouldAutoFocusSearch = typeof SelectSearchProps?.autoFocus === 'boolean' ? SelectSearchProps.autoFocus : true
    const [autoFocusTrigger, triggerAutoFocus] = useReducer((current: number) => current + 1, 0)

    const multiSelectState = <MultiSelectState
        {...SelectStateProps || emptyProps}
        values={selectedValues}
        onRemoveValue={value => {
            onRemovedValue(value)

            if (shouldAutoFocusSearch) {
                triggerAutoFocus()
            }
        }}
    />;

    // only when is local options and has no values selected
    let forceMenuToOpen = Array.isArray(options) && options.length && !selectedValues.length

    return <div className="space-y-5 --mt-6 ---px-3 ---py-3 ---rounded-1 --border-px --boder-gray-200">
        {stateFirst && multiSelectState}
        <MultiSelectSearch<Option> {...{
            ...SelectSearchProps || emptyProps,
            forceMenuToOpen,
            autoFocusTrigger
        }}
                                   options={options}
                                    onSelectedValue={value => {
                                        onSelectedValue(value)

                                        if (shouldAutoFocusSearch) {
                                            triggerAutoFocus()
                                        }
                                    }}
                                    selectedValues={selectedValues}
        />
        {!stateFirst && multiSelectState}
    </div>
}
