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

import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useSyncRef } from '@react-aria/utils';
import { useDOMRef } from '@react-spectrum/utils';
import React, { useCallback, useMemo, useRef, useState, } from 'react';
import { useFilter, useMenu } from 'react-aria';
// Import Item and Section from Menu for CommandMenu compound component
import { Item, Section, useTreeState } from 'react-stately';
import { LoadingIcon } from '../../../icons/index.js';
import { CONTAINER_STYLES, extractStyles, filterBaseProps, } from '../../../tasty/index.js';
import { mergeProps } from '../../../utils/react/index.js';
import { TooltipProvider } from '../../overlays/Tooltip/TooltipProvider.js';
import { useMenuContext } from '../Menu/index.js';
import { MenuItem } from '../Menu/MenuItem.js';
import { MenuSection } from '../Menu/MenuSection.js';
import { MenuTrigger } from '../Menu/MenuTrigger.js';
import { StyledDivider, StyledFooter, StyledHeader, StyledMenu, } from '../Menu/styled.js';
import { StyledCommandMenu, StyledEmptyState, StyledLoadingWrapper, StyledSearchInput, } from './styled.js';
function CommandMenu(props, ref) {
    const { searchPlaceholder = 'Search commands...', searchValue: controlledSearchValue, onSearchChange, filter: customFilter, emptyLabel = 'No commands found', searchInputStyles, headerStyles, footerStyles, isLoading = false, shouldFilter = true, autoFocus = true, size = 'medium', qa, styles, selectedKeys, defaultSelectedKeys, onSelectionChange, header, footer, ...restMenuProps } = props;
    const domRef = useDOMRef(ref);
    const searchInputRef = useRef(null);
    const contextProps = useMenuContext();
    // Convert string[] to Set<Key> for React Aria compatibility
    const ariaSelectedKeys = selectedKeys ? new Set(selectedKeys) : undefined;
    const ariaDefaultSelectedKeys = defaultSelectedKeys
        ? new Set(defaultSelectedKeys)
        : undefined;
    const handleSelectionChange = onSelectionChange
        ? (keys) => {
            if (keys === 'all') {
                // Handle 'all' selection case - collect all available keys
                const allKeys = Array.from(treeState.collection.getKeys()).map((key) => String(key));
                onSelectionChange(allKeys);
            }
            else if (keys instanceof Set) {
                onSelectionChange(Array.from(keys).map((key) => String(key)));
            }
            else {
                onSelectionChange([]);
            }
        }
        : undefined;
    const completeProps = mergeProps(contextProps, restMenuProps, {
        selectedKeys: ariaSelectedKeys,
        defaultSelectedKeys: ariaDefaultSelectedKeys,
        onSelectionChange: handleSelectionChange,
    });
    // Search state management
    const [internalSearchValue, setInternalSearchValue] = useState('');
    const searchValue = controlledSearchValue ?? internalSearchValue;
    const handleSearchChange = useCallback((value) => {
        if (controlledSearchValue === undefined) {
            setInternalSearchValue(value);
        }
        onSearchChange?.(value);
    }, [controlledSearchValue, onSearchChange]);
    // Filter setup
    const { contains } = useFilter({ sensitivity: 'base' });
    const textFilterFn = useMemo(() => customFilter || contains, [customFilter, contains]);
    // Enhanced filter function that supports keywords and forceMount
    const enhancedFilter = useCallback((textValue, inputValue, item) => {
        // Always show force-mounted items
        if (item?.forceMount) {
            return true;
        }
        // If shouldFilter is false, show all items
        if (!shouldFilter) {
            return true;
        }
        // Split input value into individual words and filter out empty strings
        const searchWords = inputValue
            .trim()
            .toLowerCase()
            .split(/\s+/)
            .filter((word) => word.length > 0);
        // If no search words, show all items
        if (searchWords.length === 0) {
            return true;
        }
        // Collect all searchable text for this item
        const searchableTexts = [];
        // Add main text value
        searchableTexts.push(textValue.toLowerCase());
        // Add keywords if available
        if (item?.keywords && Array.isArray(item.keywords)) {
            searchableTexts.push(...item.keywords.map((keyword) => keyword.toLowerCase()));
        }
        // Check if ALL search words match at least one of the searchable texts
        return searchWords.every((searchWord) => searchableTexts.some((text) => text.includes(searchWord)));
    }, [shouldFilter]);
    // Collection filter for React Stately
    const collectionFilter = useCallback((nodes) => {
        const term = searchValue.trim();
        // If no search term, return all nodes
        if (!term) {
            return nodes;
        }
        // Split search term into words for multi-word filtering
        const searchWords = term
            .toLowerCase()
            .split(/\s+/)
            .filter((word) => word.length > 0);
        // If no valid search words, return all nodes
        if (searchWords.length === 0) {
            return nodes;
        }
        // Recursive helper to filter sections and items
        const filterNodes = (iter) => {
            const result = [];
            for (const node of iter) {
                if (node.type === 'section') {
                    const filteredChildren = filterNodes(node.childNodes);
                    if (filteredChildren.length) {
                        result.push({
                            ...node,
                            childNodes: filteredChildren,
                        });
                    }
                }
                else {
                    const text = node.textValue ?? String(node.rendered ?? '');
                    if (enhancedFilter(text, term, node.props)) {
                        result.push(node);
                    }
                }
            }
            return result;
        };
        return filterNodes(nodes);
    }, [searchValue, enhancedFilter]);
    // Create tree state with filter for both keyboard navigation and rendering
    const treeStateProps = {
        ...completeProps,
        filter: collectionFilter,
        shouldUseVirtualFocus: true, // Always use virtual focus for CommandMenu
    };
    const treeState = useTreeState(treeStateProps);
    const collectionItems = [...treeState.collection];
    const hasSections = collectionItems.some((item) => item.type === 'section');
    // Track focused key for aria-activedescendant
    const [focusedKey, setFocusedKey] = React.useState(null);
    const focusedKeyRef = useRef(null);
    // Apply filtering to collection items for rendering and empty state checks
    const filteredCollectionItems = useMemo(() => {
        const term = searchValue.trim();
        if (!term) {
            return collectionItems;
        }
        // Split search term into words for multi-word filtering
        const searchWords = term
            .toLowerCase()
            .split(/\s+/)
            .filter((word) => word.length > 0);
        // If no valid search words, return all items
        if (searchWords.length === 0) {
            return collectionItems;
        }
        const filterNodes = (items) => {
            const result = [];
            [...items].forEach((item) => {
                if (item.type === 'section') {
                    const filteredChildren = filterNodes(item.childNodes);
                    if (filteredChildren.length) {
                        result.push({
                            ...item,
                            childNodes: filteredChildren,
                        });
                    }
                }
                else {
                    const text = item.textValue ?? String(item.rendered ?? '');
                    if (enhancedFilter(text, term, item.props)) {
                        result.push(item);
                    }
                }
            });
            return result;
        };
        return filterNodes(collectionItems);
    }, [collectionItems, searchValue, enhancedFilter]);
    const hasFilteredItems = filteredCollectionItems.length > 0;
    const viewHasSections = filteredCollectionItems.some((item) => item.type === 'section');
    // Helper function to find the first selectable item from filtered results
    const findFirstSelectableItem = useCallback(() => {
        const { selectionManager } = treeState;
        const visit = (items) => {
            for (const node of items) {
                if (node?.type === 'section') {
                    const childResult = visit(Array.from(node.childNodes ?? []));
                    if (childResult != null)
                        return childResult;
                }
                else if (node?.type === 'item' &&
                    !selectionManager.isDisabled(node.key)) {
                    return node.key;
                }
            }
            return null;
        };
        return visit(filteredCollectionItems);
    }, [filteredCollectionItems, treeState.selectionManager]);
    // Create a ref for the menu container
    const menuRef = useRef(null);
    // Use menu hook for accessibility
    const { menuProps } = useMenu({
        ...completeProps,
        'aria-label': 'Command palette menu',
        filter: collectionFilter,
        shouldUseVirtualFocus: true,
    }, treeState, menuRef);
    // Manual rendering of menu items (similar to Menu component)
    const renderedItems = useMemo(() => {
        const items = [];
        let isFirstSection = true;
        filteredCollectionItems.forEach((item) => {
            if (item.type === 'section') {
                if (!isFirstSection) {
                    items.push(_jsx(StyledDivider, { role: "separator", "aria-orientation": "horizontal" }, `divider-${String(item.key)}`));
                }
                items.push(_jsx(MenuSection, { item: item, state: treeState, styles: completeProps.sectionStyles, itemStyles: completeProps.itemStyles, headingStyles: completeProps.sectionHeadingStyles, size: size }, item.key));
                isFirstSection = false;
                return;
            }
            let menuItem = (_jsx(MenuItem, { item: item, state: treeState, styles: completeProps.itemStyles, size: size, onAction: item.onAction }, item.key));
            // Apply tooltip wrapper if tooltip property is provided
            if (item.props.tooltip) {
                const tooltipProps = typeof item.props.tooltip === 'string'
                    ? { title: item.props.tooltip }
                    : item.props.tooltip;
                menuItem = (_jsx(TooltipProvider, { activeWrap: true, placement: "right", ...tooltipProps, children: menuItem }, item.key));
            }
            // Apply custom wrapper if provided
            if (item.props.wrapper) {
                menuItem = item.props.wrapper(menuItem);
            }
            // Ensure every child has a stable key, even if the wrapper component didn't set one.
            items.push(React.cloneElement(menuItem, { key: item.key }));
        });
        return items;
    }, [
        filteredCollectionItems,
        treeState,
        completeProps.sectionStyles,
        completeProps.itemStyles,
        completeProps.sectionHeadingStyles,
    ]);
    // Auto-focus search input
    React.useEffect(() => {
        if (autoFocus && searchInputRef.current) {
            // Use a small timeout to ensure the element is visible and focusable
            // This is especially important when the CommandMenu is opened in a popover
            const timeoutId = setTimeout(() => {
                if (searchInputRef.current) {
                    searchInputRef.current.focus();
                }
            }, 0);
            return () => clearTimeout(timeoutId);
        }
    }, [autoFocus]);
    // Also focus when the component becomes visible (for trigger/popover usage)
    React.useEffect(() => {
        // Check if autoFocus is enabled and we're in a trigger context
        if (autoFocus && contextProps.autoFocus && searchInputRef.current) {
            // Use a small timeout to ensure the popover is fully rendered
            const timeoutId = setTimeout(() => {
                if (searchInputRef.current) {
                    searchInputRef.current.focus();
                }
            }, 50); // Slightly longer timeout for popover context
            return () => clearTimeout(timeoutId);
        }
    }, [autoFocus, contextProps.autoFocus]);
    // Track the previous search value to only run auto-focus when search actually changes
    const prevSearchValueRef = useRef('');
    // Auto-focus first item when search value changes (but not on initial render)
    React.useEffect(() => {
        const currentSearchValue = searchValue.trim();
        const prevSearchValue = prevSearchValueRef.current;
        // Only auto-focus when search value actually changes
        if (currentSearchValue !== prevSearchValue && currentSearchValue !== '') {
            const firstSelectableKey = findFirstSelectableItem();
            if (firstSelectableKey && hasFilteredItems) {
                // Focus the first item in the selection manager
                treeState.selectionManager.setFocusedKey(firstSelectableKey);
                setFocusedKey(firstSelectableKey);
                focusedKeyRef.current = firstSelectableKey;
            }
            else {
                // Clear focus if no items are available
                treeState.selectionManager.setFocusedKey(null);
                setFocusedKey(null);
                focusedKeyRef.current = null;
            }
        }
        // Update the previous search value
        prevSearchValueRef.current = currentSearchValue;
    }, [searchValue, findFirstSelectableItem, hasFilteredItems]);
    // Extract styles
    const extractedStyles = useMemo(() => extractStyles(props, CONTAINER_STYLES), [props]);
    // Determine if we should show empty state based on actual filtered collection
    const hasSearchTerm = searchValue.trim().length > 0;
    const showEmptyState = hasSearchTerm && !hasFilteredItems && !isLoading;
    // Sync refs
    useSyncRef(contextProps, menuRef);
    const mods = useMemo(() => {
        // Determine mods based on menu context
        let popoverMod = completeProps.mods?.popover;
        let trayMod = completeProps.mods?.tray;
        return {
            sections: viewHasSections,
            footer: !!footer,
            header: !!header,
            popover: popoverMod,
            tray: trayMod,
        };
    }, [viewHasSections, footer, header, completeProps.mods]);
    return (_jsxs(StyledCommandMenu, { ...filterBaseProps(props), ref: domRef, qa: qa || 'CommandMenu', "data-size": size, mods: mods, styles: mergeProps(extractedStyles, styles), children: [header && (_jsx(StyledHeader, { role: "presentation", "data-size": size, styles: { border: 'none', ...headerStyles }, children: header })), _jsx(StyledSearchInput, { ref: searchInputRef, type: "search", placeholder: searchPlaceholder, value: searchValue, styles: searchInputStyles, "data-size": size, "aria-controls": `${qa || 'CommandMenu'}-menu`, role: "combobox", "aria-expanded": "true", "aria-haspopup": "listbox", "aria-activedescendant": focusedKey != null
                    ? `${qa || 'CommandMenu'}-menu-option-${focusedKey}`
                    : undefined, onChange: (e) => handleSearchChange(e.target.value), onKeyDown: (e) => {
                    if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
                        e.preventDefault();
                        const isArrowDown = e.key === 'ArrowDown';
                        const { selectionManager } = treeState;
                        // Use the ref to get the current focused key synchronously
                        const currentKey = focusedKeyRef.current || selectionManager.focusedKey;
                        // Helper function to get all visible item keys accounting for sections
                        const getVisibleItemKeys = () => {
                            const keys = [];
                            const visit = (items) => {
                                for (const node of items) {
                                    if (node?.type === 'section') {
                                        visit(Array.from(node.childNodes ?? []));
                                    }
                                    else if (node?.type === 'item') {
                                        keys.push(node.key);
                                    }
                                }
                            };
                            visit(filteredCollectionItems);
                            return keys;
                        };
                        // Helper function to find next selectable key in a direction
                        const findNextSelectableKey = (currentIndex, direction, visibleKeys) => {
                            const increment = direction === 'forward' ? 1 : -1;
                            for (let i = currentIndex + increment; i >= 0 && i < visibleKeys.length; i += increment) {
                                const key = visibleKeys[i];
                                if (!selectionManager.isDisabled(key)) {
                                    return key;
                                }
                            }
                            return null;
                        };
                        // Helper function to find first or last selectable key
                        const findFirstLastSelectableKey = (direction, visibleKeys) => {
                            const keysToCheck = direction === 'forward'
                                ? visibleKeys
                                : [...visibleKeys].reverse();
                            for (const key of keysToCheck) {
                                if (!selectionManager.isDisabled(key)) {
                                    return key;
                                }
                            }
                            return null;
                        };
                        const visibleKeys = getVisibleItemKeys();
                        if (visibleKeys.length === 0) {
                            return; // No visible items to navigate
                        }
                        let nextKey;
                        const direction = isArrowDown ? 'forward' : 'backward';
                        if (currentKey == null) {
                            // No current focus, start from the first/last item
                            nextKey = findFirstLastSelectableKey(direction, visibleKeys);
                        }
                        else {
                            // Find current position in visible keys
                            const currentIndex = visibleKeys.indexOf(currentKey);
                            if (currentIndex === -1) {
                                // Current key not in visible items, start from beginning/end
                                nextKey = findFirstLastSelectableKey(direction, visibleKeys);
                            }
                            else {
                                // Find next selectable item from current position
                                nextKey = findNextSelectableKey(currentIndex, direction, visibleKeys);
                                // If no next key found, wrap to first/last selectable item
                                if (nextKey == null) {
                                    nextKey = findFirstLastSelectableKey(direction, visibleKeys);
                                }
                            }
                        }
                        if (nextKey != null) {
                            selectionManager.setFocusedKey(nextKey);
                            setFocusedKey(nextKey);
                            focusedKeyRef.current = nextKey; // Update ref immediately
                        }
                    }
                    else if (e.key === 'Enter' ||
                        (e.key === ' ' && !searchValue.trim())) {
                        const currentFocusedKey = focusedKey || treeState.selectionManager.focusedKey;
                        if (currentFocusedKey != null) {
                            e.preventDefault();
                            // Trigger action for the focused item (like Menu does)
                            // First check if there's a selection mode, if so, handle selection
                            if (treeState.selectionManager.selectionMode !== 'none') {
                                treeState.selectionManager.select(currentFocusedKey, e);
                            }
                            else {
                                // Default behavior: trigger action
                                const node = treeState.collection.getItem(currentFocusedKey);
                                if (node) {
                                    // Call the tree state's action handler
                                    const onAction = completeProps.onAction;
                                    if (onAction) {
                                        onAction(currentFocusedKey);
                                    }
                                    // Also call the item's individual onAction if it exists
                                    if (node.props?.onAction) {
                                        node.props.onAction(currentFocusedKey);
                                    }
                                }
                            }
                            // Close the menu if we're in a trigger context and closeOnSelect is enabled (default behavior)
                            const { onClose, closeOnSelect } = contextProps;
                            if (onClose && closeOnSelect !== false) {
                                onClose();
                            }
                        }
                    }
                    else if (e.key === 'Escape') {
                        if (searchValue) {
                            e.preventDefault();
                            handleSearchChange('');
                        }
                    }
                } }), isLoading && (_jsx(StyledLoadingWrapper, { children: _jsx(LoadingIcon, { role: "progressbar", "aria-label": "Loading commands", "aria-hidden": false }) })), !isLoading && !showEmptyState && (_jsx(StyledMenu, { ...menuProps, ref: menuRef, id: `${qa || 'CommandMenu'}-menu`, "aria-label": "Command menu", qa: "Menu", "data-size": size, mods: mods, styles: {
                    border: 'none',
                    boxShadow: 'none',
                    radius: 0,
                    padding: '0.5x',
                }, children: renderedItems })), !isLoading && showEmptyState && (_jsx(StyledEmptyState, { children: emptyLabel })), footer && (_jsx(StyledFooter, { role: "presentation", "data-size": size, styles: footerStyles, children: footer }))] }));
}
// forwardRef doesn't support generic parameters, so cast the result to the correct type
const _CommandMenu = React.forwardRef(CommandMenu);
// Attach Trigger alias from MenuTrigger for consistent API
// Also attach Item and Section for compound component pattern
const __CommandMenu = Object.assign(_CommandMenu, {
    Trigger: MenuTrigger,
    Item,
    Section,
    displayName: 'CommandMenu',
});
export { __CommandMenu as CommandMenu };

        