import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
import { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import Icon, { registerIcon } from '../Icon';
import * as caretDownIcon from '../Icon/icons/caret-down.icon';
import * as caretUpIcon from '../Icon/icons/caret-up.icon';
import { useConsolidatedRef, useFocusWithin, useI18n, useUID, useElement, useDirection, useTestIds, useEscape } from '../../hooks';
import Popover from '../Popover';
import FormField from '../FormField';
import { StyledFormControl } from '../FormControl';
import Menu from '../Menu';
import Button from '../Button';
import { navigatorIsAvailable, withTestIds, getFocusables, createClassName } from '../../utils';
import menuHelpers from '../Menu/helpers';
import { fitToContent } from '../Popover/modifiers';
import VisuallyHiddenText from '../VisuallyHiddenText';
import ComboBoxInput from './ComboBoxInput';
import { StyledComboBox } from './ComboBox.styles';
import { getComboBoxTestIds } from './ComboxBox.test-ids';
registerIcon(caretDownIcon, caretUpIcon);
const isMobile = navigatorIsAvailable && navigator.userAgent.includes('Mobile');
export default withTestIds(forwardRef(function ComboBox(props, ref) {
    const uid = useUID();
    const { testId, additionalInfo, value, required, id = uid, label, labelHidden, info, status, readOnly, disabled, mode = 'single-select', selected, onChange, actions, onFocus, onBlur, onResolveSuggestion, onDropdownButtonClick: onDropdownButtonClickProp, onMenuOpen, onClick, onKeyDown, menu, className, ...restProps } = props;
    const testIds = useTestIds(testId, getComboBoxTestIds);
    const bodyRef = useRef(document.body);
    const [pauseMenuDescendantEvaluation, setPauseMenuDescendantEvaluation] = useState(true);
    const [pauseSelectedDescendantEvaluation, setPauseSelectedDescendantEvaluation] = useState(false);
    const t = useI18n();
    const listId = `${id}-list`;
    const inputRef = useRef(null);
    const popoverRef = useRef(null);
    const [open, setOpen] = useState(false);
    const menuRef = useRef(null);
    const menuComponentId = `${id}-listbox`;
    const { start } = useDirection();
    // Force a re-render to make sure useFocusWithin has valid elements to set listeners on.
    const [, setContainerEl] = useElement();
    const containerRef = useConsolidatedRef(ref, setContainerEl);
    const toggleMenuDescendantsActive = useCallback((bool) => {
        setPauseMenuDescendantEvaluation(!bool);
        setPauseSelectedDescendantEvaluation(bool);
    }, []);
    const toggleSelectedDescendantsActive = useCallback((bool) => {
        setPauseSelectedDescendantEvaluation(!bool);
        setPauseMenuDescendantEvaluation(bool);
    }, []);
    const focus = useFocusWithin([containerRef, popoverRef], focused => {
        if (!focused) {
            setOpen(false);
            toggleMenuDescendantsActive(false);
            onBlur?.(selected?.items);
        }
        else {
            onFocus?.(selected?.items);
            if (isMobile) {
                setTimeout(() => {
                    containerRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' });
                }, 150); // delay it to let keyboard expand first
            }
        }
    });
    const handleEscape = useCallback((e) => {
        e.preventDefault();
        if (open) {
            e.stopPropagation();
            setOpen(false);
            toggleMenuDescendantsActive(false);
        }
    }, [open]);
    useEscape(handleEscape, containerRef, [handleEscape]);
    const onInputKeyDown = useCallback((e) => {
        if (!menu || readOnly)
            return;
        if (onKeyDown?.(e) === false) {
            return;
        }
        switch (e.key) {
            case 'ArrowUp':
                if (e.altKey) {
                    e.preventDefault();
                    if (open) {
                        setOpen(false);
                        toggleMenuDescendantsActive(false);
                    }
                }
                break;
            case 'ArrowDown':
                e.preventDefault();
                if (!open) {
                    setOpen(true);
                    toggleMenuDescendantsActive(true);
                }
                break;
            case 'Tab': {
                const forward = !e.shiftKey;
                if (forward) {
                    const popoverFocusables = getFocusables(popoverRef);
                    if (popoverFocusables.length) {
                        e.preventDefault();
                        popoverFocusables[0].focus();
                        break;
                    }
                }
                setOpen(false);
                toggleMenuDescendantsActive(false);
                break;
            }
            default:
        }
    }, [open, readOnly, menu, value, onKeyDown]);
    const onDropdownButtonClick = useCallback(() => {
        onDropdownButtonClickProp?.(!open);
        setOpen(!open);
    }, [open, onDropdownButtonClickProp]);
    const dropdownButton = useMemo(() => {
        return (menu?.items || onDropdownButtonClickProp || !onChange) && !readOnly ? (_jsx(Button, { "aria-label": t(open ? 'combobox_close_list_button_a11y' : 'combobox_open_list_button_a11y'), icon: true, variant: 'simple', onClick: onDropdownButtonClick, onMouseDown: (e) => {
                e.preventDefault();
                inputRef.current?.focus();
            }, disabled: disabled, tabIndex: '-1', children: _jsx(Icon, { name: open ? 'caret-up' : 'caret-down' }) })) : null;
    }, [onDropdownButtonClick, onChange, readOnly, open, disabled]);
    const ariaDescribedBy = useMemo(() => {
        let idString = `${id}-inputDescription`;
        if (info) {
            idString = idString.concat(` ${id}-info`);
        }
        if (onChange) {
            idString = idString.concat(` ${id}-searchDescription`);
        }
        if (mode === 'multi-select') {
            idString = idString.concat(` ${id}-multiselectDescription`);
        }
        return idString;
    }, [info, id, mode, onChange]);
    const onInputClick = useCallback((e) => {
        if (readOnly)
            return;
        setOpen(true);
        toggleMenuDescendantsActive(true);
        onClick?.(e);
    }, [readOnly, onClick]);
    useEffect(() => {
        if (focus && menu && value && value.length > 0) {
            setOpen(true);
            toggleMenuDescendantsActive(true);
        }
    }, [menu, value, focus]);
    useEffect(() => {
        if (open)
            onMenuOpen?.();
    }, [open]);
    // Workaround for the following error from jsx-ast-utils, fixed in version 3.5.0.
    // The prop value with an expression type of JSXFragment could not be resolved. Please file issue to get this fixed immediately.
    const inputActions = (_jsxs(_Fragment, { children: [dropdownButton, actions] }));
    const Comp = (_jsxs(StyledComboBox, { ref: containerRef, as: StyledFormControl, id: `${id}-combobox`, "data-testid": label ? undefined : testIds.root, children: [_jsx(ComboBoxInput, { "data-testid": testIds.control, ref: inputRef, role: 'combobox', "aria-autocomplete": 'list', "aria-expanded": !readOnly && !disabled && open, "aria-controls": open && menu !== undefined && !pauseMenuDescendantEvaluation ? listId : undefined, "aria-haspopup": 'listbox', "aria-describedby": ariaDescribedBy, className: createClassName('combo-box', className, { mode }), selected: selected?.items, spellCheck: false, id: id, readOnly: readOnly, disabled: disabled, onChange: onChange, value: value, mode: mode, status: status, onResolveSuggestion: onResolveSuggestion, actions: inputActions, onRemove: selected?.onRemove, onClick: onInputClick, onKeyDown: onInputKeyDown, required: required, pauseDescendantEvaluation: pauseSelectedDescendantEvaluation, onSelectedFocus: () => {
                    toggleSelectedDescendantsActive(true);
                }, onSelectedBlur: () => {
                    toggleSelectedDescendantsActive(false);
                }, ...restProps }), _jsx(Popover, { hideOnTargetHidden: true, ref: popoverRef, show: open && menu !== undefined, target: containerRef.current, placement: 'bottom-start', modifiers: [fitToContent], 
                // FIXME: Removed absolute strategy for mobile to avoid overflow issues.
                strategy: 'fixed', onMouseDown: (e) => e.preventDefault(), onKeyDown: (e) => {
                    if (!inputRef.current)
                        return;
                    if (e.key === 'Tab') {
                        const popoverFocusables = getFocusables(popoverRef);
                        const currentIndex = popoverFocusables.indexOf(e.target);
                        const forward = !e.shiftKey;
                        const isLast = currentIndex === popoverFocusables.length - 1;
                        if (forward && isLast) {
                            e.preventDefault();
                            const allFocusable = getFocusables(bodyRef);
                            const nextIndex = allFocusable.indexOf(inputRef.current) + 1;
                            allFocusable[nextIndex]?.focus();
                            setOpen(false);
                            toggleMenuDescendantsActive(false);
                        }
                        else if (!forward && currentIndex === 0) {
                            e.preventDefault();
                            inputRef.current.focus();
                        }
                    }
                    if (e.key === 'Escape') {
                        handleEscape(e);
                        inputRef.current.focus();
                    }
                }, children: menu && (_jsx(Menu, { ref: menuRef, listId: listId, id: menuComponentId, role: 'listbox', mode: mode, ...menu, items: menu.items, focusControlEl: inputRef.current || undefined, onItemClick: (itemId, e) => {
                        if (mode === 'single-select') {
                            setOpen(false);
                            toggleMenuDescendantsActive(true);
                        }
                        menu.onItemClick?.(itemId, e);
                        const clickedItem = menuHelpers.getItem(menu.items, itemId);
                        if (clickedItem?.primary && !clickedItem?.selected) {
                            selected?.onNew?.(clickedItem?.primary);
                        }
                    }, "aria-label": t('menu_option_list'), pauseDescendantEvaluation: pauseMenuDescendantEvaluation, arrowNavigationUnsupported: true })) })] }));
    return (_jsxs(_Fragment, { children: [label ? (_jsx(FormField, { testId: testIds, label: label, labelHidden: labelHidden, id: id, info: info, readOnly: readOnly, status: status, required: required, disabled: disabled, onResolveSuggestion: onResolveSuggestion, additionalInfo: additionalInfo, children: Comp })) : (Comp), _jsx(VisuallyHiddenText, { id: `${id}-inputDescription`, "aria-hidden": true, children: `${t('combobox_open_close')} ` }), _jsx(VisuallyHiddenText, { id: `${id}-searchDescription`, "aria-hidden": true, children: t('combobox_search_instructions') }), _jsx(VisuallyHiddenText, { id: `${id}-multiselectDescription`, "aria-hidden": true, children: t('multiselect_instructions', [start]) })] }));
}), getComboBoxTestIds);
//# sourceMappingURL=ComboBox.js.map