import { FC } from 'react';
import { observer, useLocalStore } from 'mobx-react';
import { fromPromise } from 'mobx-utils';

import { DropDownList } from '@progress/kendo-react-dropdowns';

import { TableColumnMenuProps } from '@servicetitan/design-system';
import { GridColumnMenuFilterUIProps as TableColumnMenuFilterUIProps } from '@progress/kendo-react-grid/dist/npm/interfaces/GridColumnMenuFilterUIProps';
import { IdType } from '@servicetitan/data-query';
import { TableState } from '../../table-state';

import { TableColumnMenuFilterExt } from './table-column-menu-filter-ext';
import { FilterCellExt, FilterCellExtProps } from './filter-cell-ext';
//

type AcceptableOption = string | number | { value: any; text: string };

interface OptionsConfig<T, TId extends IdType = never> {
    /* tableState is used to extract values set for multiselect */
    tableState?: TableState<T, TId>;
    /* values longer than maxExtractedLength wouldn't be extracted from tableState (for sanity). Default is 65 characters */
    maxExtractedLength?: number;
    /* set of values for multiselect */
    options?: AcceptableOption[];
}

interface FilterCellConfig {
    /* maximum number of values to show in Multiselect dropdown list at one time */
    maxFoundValues?: number;
    /* allow user to input custom values; default: true */
    allowCustomValues?: boolean;
    /* convert user-added Multiselect options to numbers; default: false */
    handleCustomValuesAsNumber?: boolean;
    /* allow only "Is [not] one of" and "Is [not] empty" operators; default: false */
    onlyMultiselectAndEmptyOperators?: boolean;
    /* Multiselect itemRender property (for very special cases) */
    multiselectItemRender?: FilterCellExtProps['multiselectItemRender'];
    /* Multiselect tagRender property (for very special cases) */
    multiselectTagRender?: FilterCellExtProps['multiselectTagRender'];
    /* Multiselect listNoDataRender property (for very special cases) */
    multiselectListNoDataRender?: FilterCellExtProps['multiselectListNoDataRender'];
}

/*
 * StandardFilterWithMultiselect is like standard Kendo filter, but with additional operators 'Is one of' / 'Is not one of'.
 * Intended to be used with string fields (though it's possible to adopt for number fields as well, if someone will have a need).
 */

export function standardFilterWithMultiselect<T, TId extends IdType = never>(
    optionsConfig: OptionsConfig<T, TId> = {},
    filterCellConfig: FilterCellConfig = {}
) {
    const optionsConfigFull = {
        maxExtractedLength: 64,
        options: [] as string[],
        ...optionsConfig,
    };

    const filterCellConfigFull = {
        allowCustomValues: true,
        maxFoundValues: 1000,
        ...filterCellConfig,
    };

    // Filter UI with data fetcher
    const FilterUIWithValues: FC<TableColumnMenuFilterUIProps> = observer(props => {
        const store = useLocalStore(
            oprops => ({
                get field() {
                    return oprops.firstFilterProps.field as keyof T;
                },
                get itemsFromDataSource() {
                    const dataSource = optionsConfigFull.tableState?.dataSource;
                    return dataSource && this.field && fromPromise(dataSource.getData({}));
                },
                get valuesfromTableState(): AcceptableOption[] {
                    if (
                        !this.itemsFromDataSource ||
                        !(this.itemsFromDataSource.state === 'fulfilled')
                    ) {
                        return [];
                    }
                    return (this.itemsFromDataSource.value.data as T[])
                        .map(i => i[this.field])
                        .filter(
                            v =>
                                typeof v === 'number' ||
                                (typeof v === 'string' &&
                                    v.length > 0 &&
                                    v.length <= optionsConfigFull.maxExtractedLength)
                        ) as any[];
                },
                get options() {
                    return Array.from(
                        new Set(this.valuesfromTableState.concat(optionsConfigFull.options))
                    ).map(v => {
                        switch (typeof v) {
                            case 'string':
                                return {
                                    value: v,
                                    text: v,
                                    textLC: v.toLowerCase(),
                                };
                            case 'number':
                                return {
                                    value: v,
                                    text: v.toString(),
                                    textLC: v.toString().toLowerCase(),
                                };
                            case 'object':
                                return {
                                    value: v.value,
                                    text: v.text,
                                    textLC: v.text.toLowerCase(),
                                };
                            default:
                                throw 'Incompatible option type.';
                        }
                    });
                },
            }),
            props
        );

        const {
            firstFilterProps,
            secondFilterProps,
            hideSecondFilter,
            logicData,
            logicValue,
            onLogicChange,
        } = props;

        return (
            <div>
                <FilterCellExt
                    {...firstFilterProps}
                    options={store.options}
                    {...filterCellConfigFull}
                />
                {!hideSecondFilter && (
                    <div>
                        <DropDownList
                            data={logicData}
                            value={logicValue}
                            onChange={onLogicChange}
                            className="k-filter-and m-b-1-i"
                            textField="text"
                        />
                        <FilterCellExt
                            {...secondFilterProps}
                            options={store.options}
                            {...filterCellConfigFull}
                        />
                    </div>
                )}
            </div>
        );
    });

    return (props: TableColumnMenuProps) => (
        <TableColumnMenuFilterExt {...props} filterUI={FilterUIWithValues} expanded />
    );
}
