import * as React from 'react';
import { useState, useMemo, useCallback } from 'react';
import { Box, Flex, Text } from 'rebass';
import { DragDropContext, Draggable, Droppable } from '../../../components/dnd';
import { CheckBox } from '../../../components/CheckBox';
import { Icon } from '../../../components/icons';
import { EllipsisContainer } from '../../../components/EllipsisContainer';
import SimpleButton from '../../../components/SimpleButton';
import Radio from '../../../components/Radio';
import { Tag } from '../../../components/Tag';
import { AdaptableFormControlTextClear } from '../Forms/AdaptableFormControlTextClear';
import ArrayExtensions from '../../../Utilities/Extensions/ArrayExtensions';
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, selectionBoxPosition = 'bottom', 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, { className: `${baseClassName}__option`, alignItems: "center", mt: index ? 1 : 0, key: identifier ?? index, backgroundColor: 'primary', padding: 2, "data-index": index, "data-id": identifier, "data-name": "option", ...props },
                React.createElement(Flex, { flex: 1, flexDirection: "row", alignItems: "center" },
                    reorderable ? (React.createElement(Box, { mr: 3, ...dragHandleProps },
                        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(Text, { flex: 1, ml: 2 }, 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 showSelectedOnlyPosition = props.showSelectedOnlyPosition ?? 'floating';
    const selectionSectionProps = {
        ...props,
        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: baseClassName, flexDirection: "column", flex: 1 },
        React.createElement(Flex, { mb: 1 },
            showFilterInput && filter ? (React.createElement(AdaptableFormControlTextClear, { value: searchInputValue, OnTextChange: setSearchInputValue, placeholder: "Type to search", style: { flex: 1, border: 0, margin: 3 } })) : (React.createElement(Box, { flex: 1 })),
            showSelectedOnlyPosition === 'top' && React.createElement(Box, { ml: 20 }, showOnlySelectedCheckbox)),
        selectionBoxPosition === 'top' && 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`, flexDirection: "column", flex: 1, style: { overflow: 'auto' } },
                showSelectedOnlyPosition === 'floating' && (React.createElement(Box, { className: `${baseClassName}__show-selected-only-checkbox` },
                    React.createElement(Box, { className: `${baseClassName}__show-selected-only-checkbox__text`, "data-name": "show-selected-only", backgroundColor: "defaultbackground" }, showOnlySelectedCheckbox))),
                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));
                }))),
        selectionBoxPosition === 'bottom' && renderSelectionSection(selectionSectionProps)));
}
export const renderSelectionSection = (props) => {
    const { noSelectionLabel, clearSelectionLabel, value, options, disabled, singleSelect, toLabel, toIdentifier, selectionBoxPosition = 'bottom', } = props;
    const selectionBox = (React.createElement(Box, { fontSize: 2 }, !value.length ? (React.createElement(React.Fragment, null,
        noSelectionLabel ?? 'No selected options',
        !singleSelect ? (React.createElement(SimpleButton, { px: 1, disabled: disabled, variant: "text", style: { textDecoration: 'underline', display: 'inline-block' }, onClick: props.onSelectAll }, "Select all")) : null)) : (React.createElement(React.Fragment, null,
        React.createElement(SimpleButton, { disabled: disabled, tabIndex: 0, px: 1, mr: 1, variant: "text", style: { textDecoration: 'underline', display: 'inline-block' }, onClick: props.onClear }, clearSelectionLabel ?? 'Clear selection.'),
        props.xSelectedLabel
            ? props.xSelectedLabel(value.length)
            : `Your selected ${value.length} ${value.length > 1 ? 'options' : 'option'}.`))));
    return (React.createElement(Box, { className: `${baseClassName}__header`, mt: selectionBoxPosition === 'bottom' ? 3 : 0, mb: selectionBoxPosition === 'top' ? 3 : 0 },
        selectionBox,
        React.createElement(ValueOptionsTags, { options: options, value: value, toLabel: toLabel, toIdentifier: toIdentifier, onClearOption: props.onClearOption, readOnly: disabled, isOptionDisabled: props.isOptionDisabled })));
};
export function ValueOptionsTags(props) {
    const { allowWrap, value, toLabel, isOptionDisabled, onChange, onClearOption, readOnly: isReadOnly, renderLabel, } = props;
    const { selectedMap } = useValuesMap(props);
    const renderOptionTag = (index) => {
        const optionId = value[index];
        const clear = () => {
            selectedMap.delete(optionId);
            onClearOption?.(optionId);
            if (onChange) {
                onChange([...selectedMap.keys()], new Map(selectedMap));
            }
        };
        const option = selectedMap.get(optionId);
        const readOnly = isReadOnly || (isOptionDisabled ? isOptionDisabled(option) : false);
        const label = option != null ? toLabel(option) : `${optionId}`;
        return (React.createElement(Tag, { flexDirection: "row", alignItems: "center", "data-name": "selected-option", "data-id": optionId, mt: allowWrap ? 1 : 0, mr: 1, className: "ab-ValueSelector__tag", onClick: (e) => {
                e.target?.lastChild?.focus?.();
            }, style: { flex: 'none' } },
            renderLabel ? renderLabel(label) : label,
            React.createElement(SimpleButton, { icon: "close", ml: 2, iconSize: 14, variant: "text", style: { border: 'none', visibility: readOnly ? 'hidden' : 'visible' }, onClick: clear })));
    };
    const renderEllipsis = useCallback(({ remaining }) => {
        return (React.createElement(Text, { fontSize: 2, style: { whiteSpace: 'nowrap' } },
            "+",
            remaining,
            " ",
            remaining > 1 ? 'others' : 'other'));
    }, []);
    return (React.createElement(EllipsisContainer, { style: props.style, marginRight: 4, my: 1, allowWrap: allowWrap, count: value.length, direction: "horizontal", renderItem: renderOptionTag, renderEllipsis: renderEllipsis }));
}
