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

import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
import { isValidElement, useCallback, useLayoutEffect, useRef, useState, } from 'react';
import { tasty } from '../../../tasty/index.js';
import { DisplayTransition } from '../DisplayTransition/DisplayTransition.js';
function isNullishContent(node) {
    // React treats `null`, `undefined`, and `false` as "render nothing"
    return node == null || node === false;
}
const typeIds = new WeakMap();
let lastTypeId = 0;
function getTypeId(type) {
    const existing = typeIds.get(type);
    if (existing != null)
        return existing;
    const next = (lastTypeId += 1);
    typeIds.set(type, next);
    return next;
}
/**
 * Best-effort key derivation for icon switching when `contentKey` is not provided.
 * The goal is to:
 * - Transition when icon component type changes
 * - Not transition when only props change for the same icon type (self-animating icons)
 * - Transition when toggling to/from nullish content
 */
function deriveContentKey(children) {
    if (isNullishContent(children))
        return 'nullish';
    if (!isValidElement(children))
        return `non-element:${typeof children}`;
    const { type, key } = children;
    const keyPart = key == null ? '' : `:${String(key)}`;
    if (typeof type === 'string')
        return `host:${type}${keyPart}`;
    if (typeof type === 'function')
        return `fn:${getTypeId(type)}${keyPart}`;
    if (typeof type === 'object' && type != null)
        return `obj:${getTypeId(type)}${keyPart}`;
    if (typeof type === 'symbol')
        return `sym:${String(type)}${keyPart}`;
    return `unknown${keyPart}`;
}
const IconSwitchElement = tasty({
    styles: {
        display: 'grid',
        placeItems: 'center',
    },
});
const IconSlotElement = tasty({
    styles: {
        gridArea: '1 / 1',
        display: 'grid',
        placeItems: 'center',
        opacity: {
            '': 0,
            entered: 1,
        },
        transition: {
            '': 'theme $transition ease-out',
            'exit | entered': 'theme $transition ease-in',
        },
    },
});
/**
 * A component that cross-fades between icons when children change.
 * Useful for animated icon transitions in buttons, items, etc.
 *
 * When `contentKey` changes, a transition is triggered.
 * When only `children` changes (same `contentKey`), content is updated in-place
 * without transition, allowing self-animating icons (like DirectionIcon) to work.
 */
export function IconSwitch(props) {
    const { children, contentKey, noWrapper, ...rest } = props;
    const keyCounterRef = useRef(0);
    const effectiveKey = contentKey ?? deriveContentKey(children);
    const prevEffectiveKeyRef = useRef(effectiveKey);
    const prevChildrenRef = useRef(children);
    const [icons, setIcons] = useState(() => [
        { key: keyCounterRef.current, content: children },
    ]);
    useLayoutEffect(() => {
        const prevKey = prevEffectiveKeyRef.current;
        const prevChildren = prevChildrenRef.current;
        const hasKeyChanged = effectiveKey !== prevKey;
        const hasNullishToggled = isNullishContent(children) !== isNullishContent(prevChildren);
        // Transition rules:
        // - If key changed -> transition (add new icon entry)
        // - If we toggled nullish <-> non-nullish -> transition (even if key did not change)
        // - Otherwise -> no state update (children reference changes are handled at render time)
        if (hasKeyChanged || hasNullishToggled) {
            keyCounterRef.current += 1;
            const newEntry = {
                key: keyCounterRef.current,
                content: children,
            };
            setIcons((prev) => [...prev, newEntry]);
        }
        prevEffectiveKeyRef.current = effectiveKey;
        prevChildrenRef.current = children;
    }, [children, effectiveKey]);
    const handleExitComplete = useCallback((exitedKey) => {
        setIcons((prev) => prev.filter((icon) => icon.key !== exitedKey));
    }, []);
    const latestKey = icons[icons.length - 1]?.key;
    // Detect if we're in an intermediate render (transition pending but effect hasn't added new entry yet).
    // In this state, we must use stored content to avoid flashing the new icon before the transition.
    const isKeyChangePending = effectiveKey !== prevEffectiveKeyRef.current;
    const isNullishChangePending = isNullishContent(children) !== isNullishContent(prevChildrenRef.current);
    const isTransitionPending = isKeyChangePending || isNullishChangePending;
    const content = icons.map((icon) => {
        const isActive = icon.key === latestKey;
        // Use current children for active icon ONLY when no transition is pending.
        // During intermediate renders (before effect adds new entry), use stored content.
        // For exiting icons, always use stored content to preserve appearance during fade-out.
        const displayContent = isActive && !isTransitionPending ? children : icon.content;
        return (_jsx(DisplayTransition, { isShown: isActive, animateOnMount: icon.key !== 0, onRest: (transition) => {
                if (transition === 'exit') {
                    handleExitComplete(icon.key);
                }
            }, children: ({ isShown, phase, ref }) => (_jsx(IconSlotElement, { ref: ref, mods: {
                    entered: isShown,
                    phase,
                }, children: displayContent })) }, icon.key));
    });
    if (noWrapper) {
        return _jsx(_Fragment, { children: content });
    }
    return _jsx(IconSwitchElement, { ...rest, children: content });
}

        