import * as React from 'react';
import { useState, useMemo, useCallback } from 'react';
import { DragDropContext, Draggable, Droppable } from '../../../components/dnd';
import { CheckBox } from '../../../components/CheckBox';
import { Icon } from '../../../components/icons';
import Radio from '../../../components/Radio';
import { AdaptableFormControlTextClear } from '../Forms/AdaptableFormControlTextClear';
import ArrayExtensions from '../../../Utilities/Extensions/ArrayExtensions';
import { Box, Flex } from '../../../components/Flex';
import clsx from 'clsx';
function useValuesMap({ options, value, toIdentifier, selectedMap, }) {
    const optionsMap = useMemo(() => {
        if (selectedMap) {
            return;
        }
        const map = new Map([]);
        options.forEach((option) => {
            map.set(toIdentifier(option), option);
        });
        return map;
    }, [options, selectedMap]);
    const result = useMemo(() => {
        if (selectedMap) {
            return selectedMap;
        }
        const map = new Map([]);
        value.forEach((id) => {
            map.set(id, optionsMap.get(id) ?? null);
        });
        return map;
    }, [optionsMap, selectedMap, value]);
    return { selectedMap: result, optionsMap };
}
const baseClassName = 'ab-ValueSelector';
export function ValueSelector(props) {
    const { options, value, filter, onChange, allowReorder = true, singleSelect, toLabel, toListLabel, toIdentifier, showSelectedOnlyLabel, showFilterInput, onShowSelectedOnlyChange, isOptionDisabled, disabled, } = props;
    const [searchInputValue, setSearchInputValue] = React.useState('');
    const preparedToLabel = toListLabel ?? toLabel;
    const [selectedOnly, doSetSelectedOnly] = useState(false);
    const setSelectedOnly = useCallback((selectedOnly) => {
        doSetSelectedOnly(selectedOnly);
        onShowSelectedOnlyChange?.(selectedOnly);
    }, [onShowSelectedOnlyChange]);
    const { selectedMap } = useValuesMap({
        options,
        toIdentifier,
        value,
    });
    const notifyChange = useCallback(() => {
        const newSelection = [...selectedMap.keys()];
        if (!newSelection.length && selectedOnly) {
            setSelectedOnly(false);
        }
        onChange(newSelection, new Map(selectedMap));
    }, [onChange, selectedOnly, selectedMap]);
    const renderOption = (option, index) => {
        const identifier = toIdentifier(option);
        const label = !allowReorder ? preparedToLabel(option) : null;
        const reorderable = typeof allowReorder === 'function' ? allowReorder(option) : allowReorder;
        const renderNode = (props, dragHandleProps) => {
            return (React.createElement(Flex, { alignItems: "center", key: identifier ?? index, "data-index": index, "data-id": identifier, "data-name": "option", ...props, className: clsx(`twa:bg-primary twa:text-text-on-primary twa:rounded-standard twa:p-2`, {
                    'twa:mt-1': index,
                    'twa:mt-0': !index,
                }, props.className, `${baseClassName}__option`) },
                React.createElement(Flex, { flexDirection: "row", alignItems: "center", className: "twa:flex-1" },
                    reorderable ? (React.createElement(Box, { ...dragHandleProps, className: clsx('twa:mr-3', dragHandleProps?.className) },
                        React.createElement(Icon, { name: "drag", style: { cursor: 'grab' } }))) : null,
                    singleSelect ? (React.createElement(Radio, { checked: selectedMap.has(identifier), "data-name": identifier, onChange: (checked) => {
                            if (checked) {
                                selectedMap.clear();
                                selectedMap.set(identifier, option);
                            }
                            else {
                                selectedMap.delete(identifier);
                            }
                            notifyChange();
                        } }, label)) : (React.createElement(CheckBox, { "data-name": identifier, disabled: disabled || (isOptionDisabled ? isOptionDisabled(option) : false), onChange: (checked) => {
                            if (checked) {
                                selectedMap.set(identifier, option);
                            }
                            else {
                                selectedMap.delete(identifier);
                            }
                            notifyChange();
                        }, checked: selectedMap.has(identifier) }, label)),
                    React.createElement(Box, { className: "twa:ml-2 twa:flex-1" }, allowReorder ? preparedToLabel(option) : null))));
        };
        return (React.createElement(Draggable, { key: identifier, index: index, draggableId: `${identifier}`, isDragDisabled: !reorderable }, (draggableProvided) => {
            return renderNode({
                ref: draggableProvided.innerRef,
                ...draggableProvided.draggableProps,
                style: draggableProvided.draggableProps.style,
            }, draggableProvided.dragHandleProps);
        }));
    };
    const showOnlySelectedCheckbox = (React.createElement(CheckBox, { disabled: !value.length, checked: selectedOnly, onChange: setSelectedOnly }, showSelectedOnlyLabel ?? 'Show Selected Only'));
    const selectionSectionProps = {
        ...props,
        showOnlySelectedCheckbox,
        onSelectAll: () => {
            options.forEach((option) => {
                selectedMap.set(toIdentifier(option), option);
            });
            notifyChange();
        },
        onClearOption: (id) => {
            selectedMap.delete(id);
            notifyChange();
        },
        onClear: () => {
            selectedMap.clear();
            notifyChange();
        },
    };
    return (React.createElement(Flex, { style: props.style, className: clsx('twa:flex-1', baseClassName), flexDirection: "column" },
        React.createElement(Flex, { className: "twa:mb-1" }, showFilterInput && filter ? (React.createElement(AdaptableFormControlTextClear, { value: searchInputValue, OnTextChange: setSearchInputValue, placeholder: "Type to search", className: "twa:flex-1 twa:border-0 twa:m-[3px]" })) : (React.createElement(Box, { className: "twa:flex-1" }))),
        renderSelectionSection(selectionSectionProps),
        React.createElement(DragDropContext, { onDragEnd: (result) => {
                const { source, destination } = result;
                const selection = [];
                const extraKeys = [];
                for (let [key, value] of selectedMap) {
                    if (value != null) {
                        selection.push(key);
                    }
                    else {
                        // null/non-existent keys have to be stored separately
                        extraKeys.push(key);
                    }
                }
                const clone = new Map(selectedMap);
                const newSelection = ArrayExtensions.reorderArray(selection, source.index, destination.index);
                // and then pushed back in the new order, at the end
                newSelection.push(...extraKeys);
                selectedMap.clear();
                newSelection.forEach((key) => {
                    selectedMap.set(key, clone.get(key));
                });
                notifyChange();
            } },
            React.createElement(Flex, { className: `${baseClassName}__body twa:flex-1 twa:overflow-auto`, flexDirection: "column" },
                React.createElement(Droppable, { droppableId: "droppable" }, (droppableProvided) => {
                    return (React.createElement("div", { ref: droppableProvided.innerRef, ...droppableProvided.droppableProps },
                        options
                            .filter((option) => {
                            let result = true;
                            if (filter) {
                                result = filter(option, searchInputValue);
                            }
                            result =
                                result && (selectedOnly ? selectedMap.has(toIdentifier(option)) : true);
                            return result;
                        })
                            .map(renderOption),
                        droppableProvided.placeholder));
                })))));
}
export const renderSelectionSection = (props) => {
    const { value, options, disabled, singleSelect, toLabel, toIdentifier, } = props;
    const selectionBox = (React.createElement(Box, { className: "twa:flex twa:flex-col twa:gap-2 twa:py-2" },
        React.createElement(Box, { className: clsx('twa:flex twa:flex-row', 'twa:border-b twa:border-primarydark/30 twa:rounded-standard') },
            React.createElement(CheckBox, { className: "twa:ml-0.5 twa:font-bold", checked: !value.length ? false : value.length === options.length ? true : null, onChange: (checked) => {
                    if (checked) {
                        props.onSelectAll();
                    }
                    else {
                        props.onClear();
                    }
                } },
                React.createElement(Box, { className: "twa:flex twa:flex-row twa:items-center twa:gap-3" },
                    "Select All",
                    React.createElement(Box, { className: "twa:text-2 twa:font-normal" },
                        "(",
                        value.length,
                        " of ",
                        options.length,
                        " selected)"))),
            React.createElement("div", { className: "twa:flex-1" }),
            React.createElement(Box, { className: "twa:mr-2 twa:text-2 twa:font-normal" }, props.showOnlySelectedCheckbox))));
    return React.createElement(Box, { className: clsx(`${baseClassName}__header `) }, selectionBox);
};
