/**
 * @license MIT
 * author: Cube Dev Team
 * @cube-dev/ui-kit v0.0.0-canary-43d3dd9
 * Released under the MIT license.
 */

import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { forwardRef, useCallback, useEffect, useMemo, useRef, useState, } from 'react';
import { FocusScope, useKeyboard } from 'react-aria';
import { Section as BaseSection, useListState } from 'react-stately';
import { useEvent } from '../../../_internal/index.js';
import { useWarn } from '../../../_internal/hooks/use-warn.js';
import { CloseIcon, DirectionIcon, LoadingIcon } from '../../../icons/index.js';
import { useProviderProps } from '../../../provider.js';
import { BASE_STYLES, COLOR_STYLES, extractStyles, filterBaseProps, OUTER_STYLES, tasty, } from '../../../tasty/index.js';
import { generateRandomId } from '../../../utils/random.js';
import { useEventBus } from '../../../utils/react/useEventBus.js';
import { ItemAction, ItemButton } from '../../actions/index.js';
import { Text } from '../../content/Text.js';
import { useFieldProps, useFormProps, wrapWithField } from '../../form/index.js';
import { Dialog, DialogTrigger } from '../../overlays/Dialog/index.js';
import { ListBox } from '../ListBox/ListBox.js';
const PROP_STYLES = [...BASE_STYLES, ...OUTER_STYLES, ...COLOR_STYLES];
const PickerWrapper = tasty({
    qa: 'PickerWrapper',
    styles: {
        display: 'inline-grid',
        flow: 'column',
        gridRows: '1sf',
        placeContent: 'stretch',
        placeItems: 'stretch',
    },
});
export const Picker = forwardRef(function Picker(props, ref) {
    props = useProviderProps(props);
    props = useFormProps(props);
    props = useFieldProps(props, {
        valuePropsMapper: ({ value, onChange }) => {
            const fieldProps = {};
            if (props.selectionMode === 'multiple') {
                fieldProps.selectedKeys = value || [];
            }
            else {
                fieldProps.selectedKey = value ?? null;
            }
            fieldProps.onSelectionChange = (key) => {
                if (props.selectionMode === 'multiple') {
                    // Handle "all" selection and array selections
                    if (key === 'all') {
                        onChange('all');
                    }
                    else {
                        onChange(key ? (Array.isArray(key) ? key : [key]) : []);
                    }
                }
                else {
                    onChange(Array.isArray(key) ? key[0] : key);
                }
            };
            return fieldProps;
        },
    });
    let { id, qa, label, extra, icon, rightIcon, prefix, suffix, hotkeys, triggerTooltip, triggerDescription, labelStyles, isRequired, necessityIndicator, validationState, isDisabled, isLoading, message, mods: externalMods, description, descriptionPlacement, placeholder, size = 'medium', styles, listBoxStyles, popoverStyles, type = 'outline', theme = 'default', shape, labelSuffix, shouldFocusWrap, children, shouldFlip = true, containerPadding = 8, selectedKey, defaultSelectedKey, selectedKeys, defaultSelectedKeys, disabledKeys, onSelectionChange, selectionMode = 'single', listStateRef, focusOnHover, showSelectAll, selectAllLabel = 'All', items, header, footer, headerStyles, footerStyles, triggerStyles, renderSummary, isCheckable, allValueProps, listStyles, optionStyles, sectionStyles, headingStyles, listRef, disallowEmptySelection, shouldUseVirtualFocus, onEscape, onOptionClick, isClearable, onClear, sortSelectedToTop, onOpenChange, listStateRef: externalListStateRef, ...otherProps } = props;
    styles = extractStyles(otherProps, PROP_STYLES, styles);
    // Generate a unique ID for this Picker instance
    const pickerId = useMemo(() => generateRandomId(), []);
    // Get event bus for menu synchronization
    const { emit, on } = useEventBus();
    // Warn if isCheckable is false in single selection mode
    useWarn(isCheckable === false && selectionMode === 'single', {
        key: ['picker-checkable-single-mode'],
        args: [
            'CubeUIKit: isCheckable=false is not recommended in single selection mode as it may confuse users about selection behavior.',
        ],
    });
    // Internal selection state (uncontrolled scenario)
    const [internalSelectedKey, setInternalSelectedKey] = useState(defaultSelectedKey ?? null);
    const [internalSelectedKeys, setInternalSelectedKeys] = useState(defaultSelectedKeys ?? []);
    // Track popover open/close and capture children order for session
    const [isPopoverOpen, setIsPopoverOpen] = useState(false);
    const triggerRef = useRef(null);
    const isControlledSingle = selectedKey !== undefined;
    const isControlledMultiple = selectedKeys !== undefined;
    const effectiveSelectedKey = isControlledSingle
        ? selectedKey
        : internalSelectedKey;
    const effectiveSelectedKeys = isControlledMultiple
        ? selectedKeys
        : internalSelectedKeys;
    // Given an iterable of keys (array or Set) toggle membership for duplicates
    const processSelectionArray = (iterable) => {
        const resultSet = new Set();
        for (const key of iterable) {
            const nKey = String(key);
            if (resultSet.has(nKey)) {
                resultSet.delete(nKey); // toggle off if clicked twice
            }
            else {
                resultSet.add(nKey); // select
            }
        }
        return Array.from(resultSet);
    };
    // Ref to access internal ListBox state for collection API
    const internalListStateRef = useRef(null);
    // Sync internal ref with external ref if provided
    useEffect(() => {
        if (externalListStateRef && internalListStateRef.current) {
            externalListStateRef.current = internalListStateRef.current;
        }
    }, [externalListStateRef]);
    // Cache for sorted items array when using `items` prop
    const cachedItemsOrder = useRef(null);
    const selectionWhenClosed = useRef({ single: null, multiple: [] });
    // Track if sortSelectedToTop was explicitly provided
    const sortSelectedToTopExplicit = sortSelectedToTop !== undefined;
    // Default to true if items are provided, false otherwise
    const shouldSortSelectedToTop = sortSelectedToTop ?? (items ? true : false);
    // Invalidate cache when items change
    useEffect(() => {
        cachedItemsOrder.current = null;
    }, [items]);
    // Capture selection when popover closes
    useEffect(() => {
        if (!isPopoverOpen) {
            selectionWhenClosed.current = {
                single: effectiveSelectedKey != null ? String(effectiveSelectedKey) : null,
                multiple: selectionMode === 'multiple' && effectiveSelectedKeys !== 'all'
                    ? (effectiveSelectedKeys || []).map(String)
                    : [],
            };
            cachedItemsOrder.current = null;
        }
    }, [
        isPopoverOpen,
        effectiveSelectedKey,
        effectiveSelectedKeys,
        selectionMode,
    ]);
    // Call onOpenChange when popover state changes
    useEffect(() => {
        onOpenChange?.(isPopoverOpen);
    }, [isPopoverOpen]);
    // Sort items with selected on top if enabled
    const getSortedItems = useCallback(() => {
        if (!items || !shouldSortSelectedToTop)
            return items;
        // Reuse cached order if available
        if (cachedItemsOrder.current) {
            return cachedItemsOrder.current;
        }
        // Warn if explicitly requested but JSX children used
        if (sortSelectedToTopExplicit && !items) {
            console.warn('Picker: sortSelectedToTop only works with the items prop. ' +
                'Sorting will be skipped when using JSX children.');
            return items;
        }
        const selectedKeys = new Set();
        if (selectionMode === 'multiple') {
            // Don't sort when "all" is selected
            if (selectionWhenClosed.current.multiple.length === 0 ||
                effectiveSelectedKeys === 'all') {
                return items;
            }
            selectionWhenClosed.current.multiple.forEach((k) => selectedKeys.add(k));
        }
        else if (selectionWhenClosed.current.single) {
            selectedKeys.add(selectionWhenClosed.current.single);
        }
        if (selectedKeys.size === 0)
            return items;
        const itemsArray = Array.isArray(items) ? items : Array.from(items);
        const selectedItems = [];
        const unselectedItems = [];
        itemsArray.forEach((item) => {
            const key = item?.key ?? item?.id;
            if (key != null && selectedKeys.has(String(key))) {
                selectedItems.push(item);
            }
            else {
                unselectedItems.push(item);
            }
        });
        const sorted = [...selectedItems, ...unselectedItems];
        if (isPopoverOpen) {
            cachedItemsOrder.current = sorted;
        }
        return sorted;
    }, [
        items,
        shouldSortSelectedToTop,
        sortSelectedToTopExplicit,
        selectionMode,
        effectiveSelectedKeys,
        isPopoverOpen,
    ]);
    const finalItems = getSortedItems();
    // Create local collection state for reading item data (labels, etc.)
    // This allows us to read item labels even before the popover opens
    const localCollectionState = useListState({
        children,
        items: finalItems, // Use sorted items to match what's shown in popover
        selectionMode: 'none', // Don't manage selection in this state
    });
    // Helper to get label from local collection
    const getItemLabel = useCallback((key) => {
        const item = localCollectionState?.collection?.getItem(key);
        return item?.textValue || String(key);
    }, [localCollectionState?.collection]);
    const selectedLabels = useMemo(() => {
        const keysToGet = selectionMode === 'multiple' && effectiveSelectedKeys !== 'all'
            ? effectiveSelectedKeys || []
            : effectiveSelectedKey != null
                ? [effectiveSelectedKey]
                : [];
        // Handle "all" selection
        if (selectionMode === 'multiple' && effectiveSelectedKeys === 'all') {
            if (!localCollectionState?.collection)
                return [];
            const labels = [];
            for (const item of localCollectionState.collection) {
                if (item.type === 'item') {
                    labels.push(item.textValue || String(item.key));
                }
            }
            return labels;
        }
        // Get labels for selected keys
        return keysToGet.map((key) => getItemLabel(key)).filter(Boolean);
    }, [
        selectionMode,
        effectiveSelectedKeys,
        effectiveSelectedKey,
        getItemLabel,
        localCollectionState?.collection,
    ]);
    const hasSelection = selectedLabels.length > 0;
    const renderTriggerContent = () => {
        // When there is a selection and a custom summary renderer is provided – use it.
        if (typeof renderSummary === 'function') {
            if (selectionMode === 'single') {
                return renderSummary({
                    selectedLabel: selectedLabels[0],
                    selectedKey: effectiveSelectedKey ?? null,
                    selectedLabels,
                    selectedKeys: effectiveSelectedKeys,
                    selectionMode: 'single',
                });
            }
            return renderSummary({
                selectedLabels,
                selectedKeys: effectiveSelectedKeys,
                selectionMode: 'multiple',
            });
        }
        else if (renderSummary === false) {
            return null;
        }
        let content = '';
        if (!hasSelection) {
            return _jsx(Text.Placeholder, { children: placeholder });
        }
        else if (selectionMode === 'single') {
            content = selectedLabels[0];
        }
        else if (effectiveSelectedKeys === 'all') {
            content = selectAllLabel;
        }
        else {
            content = selectedLabels.join(', ');
        }
        if (!content) {
            return null;
        }
        return content;
    };
    const [shouldUpdatePosition, setShouldUpdatePosition] = useState(true);
    // Capture trigger width for overlay min-width
    const triggerWidth = triggerRef?.current?.UNSAFE_getDOMNode()?.offsetWidth;
    // The trigger is rendered as a function so we can access the dialog state
    const renderTrigger = (state) => {
        // Listen for other menus opening and close this one if needed
        useEffect(() => {
            const unsubscribe = on('popover:open', (data) => {
                // If another menu is opening and this Picker is open, close this one
                if (data.menuId !== pickerId && state.isOpen) {
                    state.close();
                }
            });
            return unsubscribe;
        }, [on, pickerId, state]);
        // Emit event when this Picker opens
        useEffect(() => {
            if (state.isOpen) {
                emit('popover:open', { menuId: pickerId });
            }
        }, [state.isOpen, emit, pickerId]);
        // Track popover open/close state to control sorting
        useEffect(() => {
            if (state.isOpen !== isPopoverOpen) {
                setIsPopoverOpen(state.isOpen);
            }
        }, [state.isOpen, isPopoverOpen]);
        // Add keyboard support for arrow keys to open the popover
        const { keyboardProps } = useKeyboard({
            onKeyDown: (e) => {
                if ((e.key === 'ArrowUp' || e.key === 'ArrowDown') && !state.isOpen) {
                    e.preventDefault();
                    state.open();
                }
            },
        });
        useEffect(() => {
            // Allow initial positioning & flipping when opening, then lock placement after transition
            // Popover transition is ~120ms, give it a bit more time to finalize placement
            if (state.isOpen) {
                setShouldUpdatePosition(true);
                const id = window.setTimeout(() => setShouldUpdatePosition(false), 160);
                return () => window.clearTimeout(id);
            }
            else {
                setShouldUpdatePosition(true);
            }
        }, [state.isOpen]);
        // Clear button logic
        let showClearButton = isClearable && hasSelection && !isDisabled && !props.isReadOnly;
        // Clear function
        let clearValue = useEvent(() => {
            if (selectionMode === 'multiple') {
                if (!isControlledMultiple) {
                    setInternalSelectedKeys([]);
                }
                onSelectionChange?.([]);
            }
            else {
                if (!isControlledSingle) {
                    setInternalSelectedKey(null);
                }
                onSelectionChange?.(null);
            }
            if (state.isOpen) {
                state.close();
            }
            triggerRef?.current?.focus?.();
            onClear?.();
            return false;
        });
        return (_jsx(ItemButton, { ref: triggerRef, "data-popover-trigger": true, id: id, qa: qa || 'PickerTrigger', type: type, theme: validationState === 'invalid' ? 'danger' : theme, size: size, shape: shape, isDisabled: isDisabled || isLoading, "data-input-type": "picker", mods: {
                placeholder: !hasSelection,
                ...externalMods,
            }, icon: icon, rightIcon: isLoading ? (_jsx(LoadingIcon, {})) : rightIcon !== undefined ? (rightIcon) : showClearButton ? (_jsx(ItemAction, { icon: _jsx(CloseIcon, {}), size: size, theme: validationState === 'invalid' ? 'danger' : undefined, qa: "PickerClearButton", mods: { pressed: false }, onPress: clearValue })) : (_jsx(DirectionIcon, { to: state.isOpen ? 'top' : 'bottom' })), prefix: prefix, suffix: suffix, hotkeys: hotkeys, tooltip: triggerTooltip, description: triggerDescription, descriptionPlacement: descriptionPlacement, styles: styles, ...keyboardProps, "aria-label": `${props['aria-label'] ?? props.label ?? ''}`, children: renderTriggerContent() }));
    };
    const pickerField = (_jsx(PickerWrapper, { styles: styles, ...filterBaseProps(otherProps, { eventProps: true }), children: _jsxs(DialogTrigger, { type: "popover", placement: "bottom start", styles: triggerStyles, containerPadding: containerPadding, shouldUpdatePosition: shouldUpdatePosition, shouldFlip: shouldFlip && shouldUpdatePosition, isDismissable: true, shouldCloseOnInteractOutside: (el) => {
                const menuTriggerEl = el.closest('[data-popover-trigger]');
                // If no menu trigger was clicked, allow closing
                if (!menuTriggerEl)
                    return true;
                // If the same trigger that opened this popover was clicked, allow closing (toggle)
                if (menuTriggerEl === triggerRef?.current)
                    return true;
                // Otherwise, don't close here. Let the event bus handle closing when the other opens.
                return false;
            }, children: [renderTrigger, (close) => (_jsx(Dialog, { qa: "PickerOverlay", display: "grid", styles: {
                        gridRows: '1sf',
                        width: 'max($overlay-min-width, 30x) max-content 50vw',
                        '$overlay-min-width': '30x',
                        ...popoverStyles,
                    }, style: triggerWidth
                        ? { '--overlay-min-width': `${triggerWidth}px` }
                        : undefined, children: _jsx(FocusScope, { restoreFocus: true, children: _jsx(ListBox, { autoFocus: true, items: items ? finalItems : undefined, "aria-label": `${props['aria-label'] ?? props.label ?? ''} Picker`, selectedKey: selectionMode === 'single' ? effectiveSelectedKey : undefined, selectedKeys: selectionMode === 'multiple'
                                ? effectiveSelectedKeys
                                : undefined, listStyles: listStyles, optionStyles: optionStyles, sectionStyles: sectionStyles, headingStyles: headingStyles, listRef: listRef, disallowEmptySelection: disallowEmptySelection, disabledKeys: disabledKeys, focusOnHover: focusOnHover, shouldFocusWrap: shouldFocusWrap, selectionMode: selectionMode, validationState: validationState, isDisabled: isDisabled, isLoading: isLoading, stateRef: internalListStateRef, isCheckable: isCheckable, shape: "popover", size: "medium", showSelectAll: showSelectAll, selectAllLabel: selectAllLabel, header: header, footer: footer, headerStyles: headerStyles, footerStyles: footerStyles, qa: `${props.qa || 'Picker'}ListBox`, allValueProps: allValueProps, onEscape: () => close(), onOptionClick: (key) => {
                                // For Picker, clicking the content area should close the popover
                                // in multiple selection mode (single mode already closes via onSelectionChange)
                                if ((selectionMode === 'multiple' && isCheckable) ||
                                    key === '__ALL__') {
                                    close();
                                }
                            }, onSelectionChange: (selection) => {
                                // No need to change any flags - children order is cached
                                // Update internal state if uncontrolled
                                if (selectionMode === 'single') {
                                    if (!isControlledSingle) {
                                        setInternalSelectedKey(selection);
                                    }
                                }
                                else {
                                    if (!isControlledMultiple) {
                                        let normalized = selection;
                                        if (selection === 'all') {
                                            normalized = 'all';
                                        }
                                        else if (Array.isArray(selection)) {
                                            normalized = processSelectionArray(selection);
                                        }
                                        else if (selection &&
                                            typeof selection === 'object' &&
                                            selection instanceof Set) {
                                            normalized = processSelectionArray(selection);
                                        }
                                        setInternalSelectedKeys(normalized);
                                    }
                                }
                                onSelectionChange?.(selection);
                                if (selectionMode === 'single') {
                                    close();
                                }
                            }, children: children }) }) }))] }) }));
    return wrapWithField(pickerField, ref, props);
});
Picker.Item = ListBox.Item;
Picker.Section = BaseSection;
Object.defineProperty(Picker, 'cubeInputType', {
    value: 'Picker',
    enumerable: false,
    configurable: false,
});

        