'use client';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { useControllableState, useEventCallback, useFirstMount } from '@fluentui/react-utilities';
import { useOptionCollection } from '../utils/useOptionCollection';
import { useSelection } from '../utils/useSelection';
/**
 * State shared between Combobox and Dropdown components
 *
 * @internal
 */ export const useComboboxBaseState = (props)=>{
    'use no memo';
    const { appearance = 'outline', disableAutoFocus, children, clearable = false, editable = false, inlinePopup = false, mountNode = undefined, multiselect, onOpenChange, size = 'medium', activeDescendantController, freeform = false, disabled = false, onActiveOptionChange = null } = props;
    const optionCollection = useOptionCollection();
    const { getOptionsMatchingValue } = optionCollection;
    const { getOptionById } = optionCollection;
    const getActiveOption = React.useCallback(()=>{
        const activeOptionId = activeDescendantController.active();
        return activeOptionId ? getOptionById(activeOptionId) : undefined;
    }, [
        activeDescendantController,
        getOptionById
    ]);
    // Keeping some kind of backwards compatible functionality here
    // eslint-disable-next-line @typescript-eslint/naming-convention
    const UNSAFE_activeOption = getActiveOption();
    // eslint-disable-next-line @typescript-eslint/naming-convention
    const UNSAFE_setActiveOption = React.useCallback((option)=>{
        let nextOption = undefined;
        if (typeof option === 'function') {
            const activeOption = getActiveOption();
            nextOption = option(activeOption);
        }
        if (nextOption) {
            activeDescendantController.focus(nextOption.id);
        } else {
            activeDescendantController.blur();
        }
    }, [
        activeDescendantController,
        getActiveOption
    ]);
    // track whether keyboard focus outline should be shown
    // tabster/keyborg doesn't work here, since the actual keyboard focus target doesn't move
    const [focusVisible, setFocusVisible] = React.useState(false);
    // track focused state to conditionally render collapsed listbox
    // when the trigger is focused - the listbox should but hidden until the open state is changed
    const [hasFocus, setHasFocus] = React.useState(false);
    const ignoreNextBlur = React.useRef(false);
    // calculate value based on props, internal value changes, and selected options
    const isFirstMount = useFirstMount();
    const [controllableValue, setValue] = useControllableState({
        state: props.value,
        initialState: undefined
    });
    const { selectedOptions, selectOption: baseSelectOption, clearSelection } = useSelection(props);
    // reset any typed value when an option is selected
    const selectOption = React.useCallback((ev, option)=>{
        ReactDOM.unstable_batchedUpdates(()=>{
            setValue(undefined);
            baseSelectOption(ev, option);
        });
    }, [
        setValue,
        baseSelectOption
    ]);
    const value = React.useMemo(()=>{
        // don't compute the value if it is defined through props or setValue,
        if (controllableValue !== undefined) {
            return controllableValue;
        }
        // handle defaultValue here, so it is overridden by selection
        if (isFirstMount && props.defaultValue !== undefined) {
            return props.defaultValue;
        }
        const selectedOptionsText = getOptionsMatchingValue((optionValue)=>{
            return selectedOptions.includes(optionValue);
        }).map((option)=>option.text);
        if (multiselect) {
            // editable inputs should not display multiple selected options in the input as text
            return editable ? '' : selectedOptionsText.join(', ');
        }
        return selectedOptionsText[0];
    // do not change value after isFirstMount changes,
    // we do not want to accidentally override defaultValue on a second render
    // unless another value is intentionally set
    // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [
        controllableValue,
        editable,
        getOptionsMatchingValue,
        multiselect,
        selectedOptions
    ]);
    // Handle open state, which is shared with options in context
    const [open, setOpenState] = useControllableState({
        state: props.open,
        defaultState: props.defaultOpen,
        initialState: false
    });
    const setOpen = React.useCallback((event, newState)=>{
        if (disabled) {
            return;
        }
        onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(event, {
            open: newState
        });
        ReactDOM.unstable_batchedUpdates(()=>{
            if (!newState && !freeform) {
                setValue(undefined);
            }
            setOpenState(newState);
        });
    }, [
        onOpenChange,
        setOpenState,
        setValue,
        freeform,
        disabled
    ]);
    // update active option based on change in open state
    React.useEffect(()=>{
        if (open) {
            // if it is single-select and there is a selected option, start at the selected option
            if (!multiselect && selectedOptions.length > 0) {
                const selectedOption = getOptionsMatchingValue((v)=>v === selectedOptions[0]).pop();
                if (selectedOption === null || selectedOption === void 0 ? void 0 : selectedOption.id) {
                    activeDescendantController.focus(selectedOption.id);
                }
            }
        } else {
            activeDescendantController.blur();
        }
    // this should only be run in response to changes in the open state
    // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [
        open,
        activeDescendantController
    ]);
    // Fallback focus when children are updated in an open popover results in no item being focused
    React.useEffect(()=>{
        if (open && !disableAutoFocus && !activeDescendantController.active()) {
            activeDescendantController.first();
        }
    // this should only be run in response to changes in the open state or children
    }, [
        open,
        children,
        disableAutoFocus,
        activeDescendantController,
        getOptionById
    ]);
    const onActiveDescendantChange = useEventCallback((event)=>{
        const previousOption = event.detail.previousId ? optionCollection.getOptionById(event.detail.previousId) : null;
        const nextOption = optionCollection.getOptionById(event.detail.id);
        onActiveOptionChange === null || onActiveOptionChange === void 0 ? void 0 : onActiveOptionChange(event, {
            event,
            type: 'change',
            previousOption,
            nextOption
        });
    });
    return {
        ...optionCollection,
        freeform,
        disabled,
        selectOption,
        clearSelection,
        selectedOptions,
        activeOption: UNSAFE_activeOption,
        appearance,
        clearable,
        focusVisible,
        ignoreNextBlur,
        inlinePopup,
        mountNode,
        open,
        hasFocus,
        setActiveOption: UNSAFE_setActiveOption,
        setFocusVisible,
        setHasFocus,
        setOpen,
        setValue,
        size,
        value,
        multiselect,
        onOptionClick: useEventCallback((e)=>{
            if (!multiselect) {
                setOpen(e, false);
            }
        }),
        onActiveDescendantChange
    };
};
