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

import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { Children, forwardRef, isValidElement, useEffect, useMemo, useRef, } from 'react';
import { useTabList } from 'react-aria';
import { Item as CollectionItem, useTabListState, } from 'react-stately';
import { useEvent, useWarn } from '../../../_internal/hooks/index.js';
import { DirectionIcon } from '../../../icons/index.js';
import { extractStyles, mergeStyles, OUTER_STYLES } from '../../../tasty/index.js';
import { mergeProps, useMergeStyles } from '../../../utils/react/index.js';
import { useTinyScrollbar } from '../../content/Layout/hooks/useTinyScrollbar.js';
import { DraggableTabList } from './DraggableTabList.js';
import { TabIndicatorElement, TabsElement } from './styled.js';
import { TabButton } from './TabButton.js';
import { CachedPanelRenderer, TabPanelRenderer } from './TabPanel.js';
import { TabPicker } from './TabPicker.js';
import { TabsAction } from './TabsAction.js';
import { TabsProvider } from './TabsContext.js';
import { useTabEditing } from './use-tab-editing.js';
import { useTabIndicator } from './use-tab-indicator.js';
// =============================================================================
// Helper Functions
// =============================================================================
/**
 * Generic helper to check if a child is a specific component by displayName.
 */
function isComponentElement(child, displayName) {
    return (isValidElement(child) &&
        typeof child.type === 'function' &&
        child.type.displayName === displayName);
}
/** Checks if a child is a Tab component. */
const isTabElement = (child) => isComponentElement(child, 'CubeTab');
/** Checks if a child is a TabPanel component. */
const isTabPanelElement = (child) => isComponentElement(child, 'CubeTabPanel');
/** Checks if a child is a TabList component. */
const isTabListElement = (child) => isComponentElement(child, 'CubeTabList');
/**
 * Extracts the raw key from a React element, stripping the ".$" prefix
 * that React adds via Children.map/toArray.
 */
function getRawKey(element) {
    if (element.key == null)
        return null;
    const keyStr = String(element.key);
    return keyStr.startsWith('.$') ? keyStr.slice(2) : keyStr;
}
// =============================================================================
// Tab Component (configuration only - not rendered directly)
// =============================================================================
function Tab(_props) {
    return null;
}
Tab.displayName = 'CubeTab';
// =============================================================================
// TabPanel Component (configuration only - not rendered directly)
// =============================================================================
function TabPanel(_props) {
    return null;
}
TabPanel.displayName = 'CubeTabPanel';
// =============================================================================
// TabList Component (configuration only - not rendered directly)
// =============================================================================
function TabList(_props) {
    return null;
}
TabList.displayName = 'CubeTabList';
// =============================================================================
// Main Tabs Component
// =============================================================================
function TabsComponent(props, ref) {
    const { label = 'Tabs', defaultActiveKey, activeKey, size, type = 'default', onChange, onDelete, onTitleChange, autoHideActions, isEditable: parentIsEditable, menu: parentMenu, menuTriggerProps: parentMenuTriggerProps, menuProps: parentMenuProps, contextMenu: parentContextMenu, onAction: parentOnAction, children, prefix, suffix, prerender = false, keepMounted = false, qa = 'Tabs', renderPanel, panelCacheKeys, isReorderable = false, keyOrder, onReorder, showTabPicker = false, showScrollArrows = false, tabPickerPosition = 'suffix', scrollArrowsPosition = 'suffix', tabListPadding, tabListStyles, prefixStyles, suffixStyles, ...otherProps } = props;
    // Extract outer styles
    const baseStyles = extractStyles(otherProps, OUTER_STYLES);
    // Build TabList padding style (memoized)
    const tabListPaddingStyles = useMemo(() => (tabListPadding ? { padding: `0 ${tabListPadding}` } : undefined), [tabListPadding]);
    // Merge tabListPaddingStyles with tabListStyles (memoized)
    const mergedTabListStyles = useMemo(() => mergeStyles(tabListPaddingStyles, tabListStyles), [tabListPaddingStyles, tabListStyles]);
    // Merge all sub-element styles into baseStyles
    const combinedStyles = useMergeStyles(baseStyles, {
        TabList: mergedTabListStyles,
        Prefix: prefixStyles,
        Suffix: suffixStyles,
    });
    // DOM element refs
    const listRef = useRef(null);
    const scrollRef = useRef(null);
    // Track visited tabs for keepMounted functionality
    const visitedKeysRef = useRef(new Set());
    // =========================================================================
    // Tab Title Editing Hook
    // =========================================================================
    const { editingKey, editValue, setEditValue, startEditing, cancelEditing, submitEditing, } = useTabEditing({ onChange, onTitleChange });
    // =========================================================================
    // Parse children to extract tabs and explicit panels
    // =========================================================================
    const { parsedTabs, explicitPanels, hasAnyContent } = useMemo(() => {
        const childArray = Children.toArray(children);
        const tabs = [];
        const panels = new Map();
        let hasExplicitList = false;
        let tabChildren = [];
        // Check for explicit Tabs.List / Tabs.Panel structure
        for (const child of childArray) {
            if (isTabListElement(child)) {
                hasExplicitList = true;
                tabChildren = Children.toArray(child.props.children);
            }
            else if (isTabPanelElement(child)) {
                const key = getRawKey(child);
                if (key != null) {
                    panels.set(key, {
                        key,
                        content: child.props.children,
                        prerender: child.props.prerender,
                        keepMounted: child.props.keepMounted,
                        styles: child.props.styles,
                        qa: child.props.qa,
                        qaVal: child.props.qaVal,
                    });
                }
            }
        }
        // If no explicit list, use direct Tab children
        if (!hasExplicitList) {
            tabChildren = childArray;
        }
        // Parse Tab elements
        let hasContent = panels.size > 0;
        for (const child of tabChildren) {
            if (isTabElement(child)) {
                const key = getRawKey(child) ?? child.props.id;
                if (key != null) {
                    const { id: _id, children: tabContent, ...tabProps } = child.props;
                    tabs.push({
                        ...tabProps,
                        key,
                        content: tabContent,
                    });
                    if (tabContent != null) {
                        hasContent = true;
                    }
                }
            }
        }
        return {
            parsedTabs: tabs,
            explicitPanels: panels,
            hasAnyContent: hasContent,
        };
    }, [children]);
    // Clean up visitedKeys when tabs are removed
    const currentTabKeysSet = useMemo(() => new Set(parsedTabs.map((t) => t.key)), [parsedTabs]);
    useEffect(() => {
        for (const key of visitedKeysRef.current) {
            if (!currentTabKeysSet.has(key)) {
                visitedKeysRef.current.delete(key);
            }
        }
    }, [currentTabKeysSet]);
    // =========================================================================
    // Order tabs according to keyOrder
    // =========================================================================
    const orderedParsedTabs = useMemo(() => {
        if (!keyOrder || keyOrder.length === 0) {
            return parsedTabs;
        }
        const tabMap = new Map();
        for (const tab of parsedTabs) {
            tabMap.set(tab.key, tab);
        }
        const ordered = [];
        for (const key of keyOrder) {
            const tab = tabMap.get(String(key));
            if (tab) {
                ordered.push(tab);
                tabMap.delete(String(key));
            }
        }
        // Append any tabs not in keyOrder
        for (const tab of parsedTabs) {
            if (tabMap.has(tab.key)) {
                ordered.push(tab);
            }
        }
        return ordered;
    }, [parsedTabs, keyOrder]);
    // Create collection items for React Stately
    const collectionItems = useMemo(() => {
        return orderedParsedTabs.map((tab) => (_jsx(CollectionItem, { textValue: typeof tab.title === 'string' ? tab.title : String(tab.key), children: tab.title }, tab.key)));
    }, [orderedParsedTabs]);
    // Determine disabled keys
    const disabledKeys = useMemo(() => {
        return new Set(parsedTabs.filter((tab) => tab.isDisabled).map((tab) => tab.key));
    }, [parsedTabs]);
    // Handle selection change (converts React Aria's Key to string for our API)
    const handleSelectionChange = useEvent((key) => {
        const keyStr = String(key);
        visitedKeysRef.current.add(keyStr);
        onChange?.(keyStr);
    });
    // Convert keys to strings for React Aria compatibility
    const selectedKey = activeKey != null ? String(activeKey) : undefined;
    const defaultSelectedKey = defaultActiveKey != null ? String(defaultActiveKey) : undefined;
    // Create aria props for useTabListState
    const ariaProps = useMemo(() => ({
        selectedKey,
        defaultSelectedKey,
        onSelectionChange: handleSelectionChange,
        disabledKeys,
        children: collectionItems,
        'aria-label': label,
    }), [
        selectedKey,
        defaultSelectedKey,
        handleSelectionChange,
        disabledKeys,
        collectionItems,
        label,
    ]);
    // Create state using useTabListState
    const state = useTabListState(ariaProps);
    // Track initial selected key for visited tabs
    useEffect(() => {
        if (state.selectedKey != null) {
            visitedKeysRef.current.add(String(state.selectedKey));
        }
    }, [state.selectedKey]);
    // Get tablist props from react-aria
    const { tabListProps } = useTabList(ariaProps, state, listRef);
    // Handle selection from TabPicker (needs to update internal state in uncontrolled mode)
    const handleTabPickerSelect = useEvent((key) => {
        // Update internal state (for uncontrolled mode)
        state.setSelectedKey(key);
        // Also call the external onChange handler
        handleSelectionChange(key);
    });
    // =========================================================================
    // Tab Indicator (for default type)
    // =========================================================================
    // Create order token that changes when tab order changes (for indicator recalculation)
    const orderToken = useMemo(() => orderedParsedTabs.map((t) => t.key).join(','), [orderedParsedTabs]);
    const indicatorStyle = useTabIndicator(listRef, state.selectedKey, type === 'default' || type === 'narrow', orderToken);
    // =========================================================================
    // Tiny Scrollbar (not for radio type)
    // =========================================================================
    const isTinyScrollbar = type !== 'radio';
    const { handleHStyle, hasOverflowX, isScrolling, isAtStartX, isAtEndX } = useTinyScrollbar(scrollRef, isTinyScrollbar);
    const hasPanels = hasAnyContent || !!renderPanel;
    // =========================================================================
    // Tab Picker visibility
    // =========================================================================
    useWarn(showTabPicker && type === 'radio', {
        key: ['tabs-tabpicker-radio-unsupported'],
        args: [
            'Tabs:',
            '`showTabPicker` is not supported when `type="radio"`. The TabPicker will not be rendered.',
        ],
    });
    const shouldShowTabPicker = type !== 'radio' &&
        (showTabPicker === true || (showTabPicker === 'auto' && hasOverflowX));
    // =========================================================================
    // Scroll Arrows visibility and handlers
    // =========================================================================
    useWarn(showScrollArrows && type === 'radio', {
        key: ['tabs-scrollarrows-radio-unsupported'],
        args: [
            'Tabs:',
            '`showScrollArrows` is not supported when `type="radio"`. The scroll arrows will not be rendered.',
        ],
    });
    const shouldShowScrollArrows = type !== 'radio' &&
        (showScrollArrows === true ||
            (showScrollArrows === 'auto' && hasOverflowX));
    const handleScrollLeft = useEvent(() => {
        const el = scrollRef.current;
        if (el) {
            el.scrollTo({
                left: el.scrollLeft - el.clientWidth,
                behavior: 'smooth',
            });
        }
    });
    const handleScrollRight = useEvent(() => {
        const el = scrollRef.current;
        if (el) {
            el.scrollTo({
                left: el.scrollLeft + el.clientWidth,
                behavior: 'smooth',
            });
        }
    });
    // =========================================================================
    // Base Context Value (without drag/drop states)
    // =========================================================================
    const baseContextValue = useMemo(() => ({
        state,
        type,
        size,
        autoHideActions,
        isEditable: parentIsEditable,
        menu: parentMenu,
        menuTriggerProps: parentMenuTriggerProps,
        menuProps: parentMenuProps,
        contextMenu: parentContextMenu,
        onDelete,
        onAction: parentOnAction,
        editingKey,
        editValue,
        setEditValue,
        startEditing,
        submitEditing,
        cancelEditing,
    }), [
        state,
        type,
        size,
        autoHideActions,
        parentIsEditable,
        parentMenu,
        parentMenuTriggerProps,
        parentMenuProps,
        parentContextMenu,
        onDelete,
        parentOnAction,
        editingKey,
        editValue,
        setEditValue,
        startEditing,
        submitEditing,
        cancelEditing,
    ]);
    // Helper to create full context value with optional drag/drop states
    const createContextValue = (dragState, dropState) => ({
        ...baseContextValue,
        dragState,
        dropState,
    });
    // =========================================================================
    // Tab List Content Renderer
    // =========================================================================
    const renderTabListContent = (contextValue, collectionProps = {}) => (_jsxs("div", { ...mergeProps(tabListProps, collectionProps), ref: listRef, "data-element": "TabList", children: [_jsx(TabsProvider, { value: contextValue, children: orderedParsedTabs.map((tab, index) => {
                    const item = state.collection.getItem(tab.key);
                    if (!item)
                        return null;
                    return (_jsx(TabButton, { item: item, tabData: tab, isLastTab: index === orderedParsedTabs.length - 1 }, item.key));
                }) }), indicatorStyle && (_jsx(TabIndicatorElement, { style: {
                    left: indicatorStyle.left,
                    width: indicatorStyle.width,
                } }))] }));
    // =========================================================================
    // Mods for styling
    // =========================================================================
    const mods = useMemo(() => ({
        type,
        size,
        deletable: !!onDelete,
        scrolling: isScrolling,
        'fade-left': !isAtStartX,
        'fade-right': !isAtEndX,
        'has-panels': hasPanels,
    }), [type, size, onDelete, isScrolling, isAtStartX, isAtEndX, hasPanels]);
    // =========================================================================
    // Action Elements (TabPicker and Scroll Arrows)
    // =========================================================================
    const scrollArrowsElement = shouldShowScrollArrows ? (_jsxs(_Fragment, { children: [_jsx(TabsAction, { icon: _jsx(DirectionIcon, { to: "left" }), "aria-label": "Scroll tabs left", isDisabled: isAtStartX, onPress: handleScrollLeft }), _jsx(TabsAction, { icon: _jsx(DirectionIcon, { to: "right" }), "aria-label": "Scroll tabs right", isDisabled: isAtEndX, onPress: handleScrollRight })] })) : null;
    const tabPickerElement = shouldShowTabPicker ? (_jsx(TabPicker, { tabs: orderedParsedTabs, selectedKey: state.selectedKey, size: size, type: type, onSelect: handleTabPickerSelect, onDelete: onDelete })) : null;
    // Determine which actions go in prefix/suffix
    // In prefix: TabPicker first (left), then scroll arrows (right)
    // In suffix: scroll arrows first (left), then TabPicker (right)
    const prefixHasActions = (shouldShowTabPicker && tabPickerPosition === 'prefix') ||
        (shouldShowScrollArrows && scrollArrowsPosition === 'prefix');
    const suffixHasActions = (shouldShowTabPicker && tabPickerPosition === 'suffix') ||
        (shouldShowScrollArrows && scrollArrowsPosition === 'suffix');
    // Wrap with TabsProvider so prefix/suffix can access context (size, type)
    // The inner TabsProvider in renderTabListContent will override for tab buttons
    return (_jsxs(TabsProvider, { value: baseContextValue, children: [_jsxs(TabsElement, { ref: ref, qa: qa, mods: mods, styles: combinedStyles, style: handleHStyle, "data-size": size, children: [prefix || prefixHasActions ? (_jsxs("div", { "data-element": "Prefix", children: [tabPickerPosition === 'prefix' && tabPickerElement, scrollArrowsPosition === 'prefix' && scrollArrowsElement, prefix] })) : null, _jsxs("div", { "data-element": "ScrollWrapper", children: [_jsx("div", { ref: scrollRef, "data-element": "Scroll", children: isReorderable ? (_jsx(DraggableTabList, { state: state, listRef: listRef, orderedKeys: orderedParsedTabs.map((t) => t.key), onReorder: onReorder, children: (dragState, dropState, collectionProps) => renderTabListContent(createContextValue(dragState, dropState), collectionProps) })) : (renderTabListContent(createContextValue())) }), isTinyScrollbar && hasOverflowX && _jsx("div", { "data-element": "ScrollbarH" })] }), suffix || suffixHasActions ? (_jsxs("div", { "data-element": "Suffix", children: [scrollArrowsPosition === 'suffix' && scrollArrowsElement, tabPickerPosition === 'suffix' && tabPickerElement, suffix] })) : null] }), renderPanel && (_jsx(CachedPanelRenderer, { parsedTabs: parsedTabs, explicitPanels: explicitPanels, state: state, renderPanel: renderPanel, panelCacheKeys: panelCacheKeys, prerender: prerender, keepMounted: keepMounted, visitedKeys: visitedKeysRef.current })), !renderPanel &&
                hasAnyContent &&
                parsedTabs.map((tab) => {
                    const explicitPanel = explicitPanels.get(tab.key);
                    const content = explicitPanel?.content ?? tab.content;
                    if (content == null)
                        return null;
                    return (_jsx(TabPanelRenderer, { tabKey: tab.key, state: state, content: content, prerender: prerender, keepMounted: keepMounted, tabPrerender: explicitPanel?.prerender ?? tab.prerender, tabKeepMounted: explicitPanel?.keepMounted ?? tab.keepMounted, visitedKeys: visitedKeysRef.current, panelStyles: explicitPanel?.styles, qa: explicitPanel?.qa, qaVal: explicitPanel?.qaVal }, tab.key));
                })] }));
}
// =============================================================================
// Exports
// =============================================================================
const _Tabs = forwardRef(TabsComponent);
/**
 * Tabs component for organizing content into multiple panels.
 * Built with React Aria for full accessibility support.
 *
 * **Note:** Tab keys are internally converted to strings for React Aria compatibility.
 *
 * @example
 * // Simple usage
 * <Tabs defaultActiveKey="tab1">
 *   <Tab key="tab1" title="Tab 1">Content 1</Tab>
 *   <Tab key="tab2" title="Tab 2">Content 2</Tab>
 * </Tabs>
 *
 * @example
 * // Tabs-only (no panels)
 * <Tabs activeKey={activeTab} onChange={setActiveTab}>
 *   <Tab key="overview" title="Overview" />
 *   <Tab key="settings" title="Settings" />
 * </Tabs>
 *
 * @example
 * // Explicit panels
 * <Tabs defaultActiveKey="tab1">
 *   <Tabs.List>
 *     <Tab key="tab1" title="Tab 1" />
 *     <Tab key="tab2" title="Tab 2" />
 *   </Tabs.List>
 *   <Tabs.Panel key="tab1">Content 1</Tabs.Panel>
 *   <Tabs.Panel key="tab2">Content 2</Tabs.Panel>
 * </Tabs>
 */
export const Tabs = Object.assign(_Tabs, {
    Tab,
    List: TabList,
    Panel: TabPanel,
    Action: TabsAction,
});
export { Tab, TabList, TabPanel, TabsAction };

        