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

import { createElement, forwardRef, useMemo, } from 'react';
import { isValidElementType } from 'react-is';
import { useStyles } from './hooks/useStyles.js';
import { BASE_STYLES } from './styles/list.js';
import { getDisplayName } from './utils/get-display-name.js';
import { mergeStyles } from './utils/merge-styles.js';
import { modAttrs } from './utils/mod-attrs.js';
import { processTokens, stringifyTokens } from './utils/process-tokens.js';
/**
 * Mapping of is* properties to their corresponding HTML attributes
 */
const IS_PROPERTIES_MAP = {
    isDisabled: 'disabled',
    isHidden: 'hidden',
    isChecked: 'checked',
};
/**
 * Precalculated entries for performance optimization
 */
const IS_PROPERTIES_ENTRIES = Object.entries(IS_PROPERTIES_MAP);
/**
 * Helper function to handle is* properties consistently
 * Transforms is* props to HTML attributes and adds corresponding data-* attributes
 */
function handleIsProperties(props) {
    for (const [isProperty, targetAttribute] of IS_PROPERTIES_ENTRIES) {
        if (isProperty in props) {
            props[targetAttribute] = props[isProperty];
            delete props[isProperty];
        }
        // Add data-* attribute if target attribute is truthy and doesn't already exist
        const dataAttribute = `data-${targetAttribute}`;
        if (!(dataAttribute in props) && props[targetAttribute]) {
            props[dataAttribute] = '';
        }
    }
}
/**
 * Creates a sub-element component for compound component patterns.
 * Sub-elements are lightweight components with data-element attribute for CSS targeting.
 */
function createSubElement(elementName, definition) {
    // Normalize definition to object form
    const config = typeof definition === 'string'
        ? { as: definition }
        : definition;
    const tag = config.as ?? 'div';
    const defaultQa = config.qa;
    const defaultQaVal = config.qaVal;
    const SubElement = forwardRef((props, ref) => {
        const { qa, qaVal, mods, tokens, isDisabled, isHidden, isChecked, className, style, ...htmlProps } = props;
        // Build mod attributes
        let modProps;
        if (mods) {
            modProps = modAttrs(mods);
        }
        // Process tokens into inline style properties
        const tokenStyle = tokens
            ? processTokens(tokens)
            : undefined;
        // Merge token styles with explicit style prop (style has priority)
        let mergedStyle;
        if (tokenStyle || style) {
            mergedStyle =
                tokenStyle && style
                    ? { ...tokenStyle, ...style }
                    : (tokenStyle ?? style);
        }
        const elementProps = {
            'data-element': elementName,
            'data-qa': qa ?? defaultQa,
            'data-qaval': qaVal ?? defaultQaVal,
            ...(modProps || {}),
            ...htmlProps,
            className,
            style: mergedStyle,
            isDisabled,
            isHidden,
            isChecked,
            ref,
        };
        // Handle is* properties (isDisabled -> disabled + data-disabled, etc.)
        handleIsProperties(elementProps);
        // Clean up undefined data attributes
        if (elementProps['data-qa'] === undefined)
            delete elementProps['data-qa'];
        if (elementProps['data-qaval'] === undefined)
            delete elementProps['data-qaval'];
        return createElement(tag, elementProps);
    });
    SubElement.displayName = `SubElement(${elementName})`;
    return SubElement;
}
// Implementation
export function tasty(Component, options) {
    if (isValidElementType(Component)) {
        return tastyWrap(Component, options);
    }
    return tastyElement(Component);
}
function tastyWrap(Component, options) {
    let { as: extendTag, element: extendElement, ...defaultProps } = (options ?? {});
    let propsWithStyles = ['styles'].concat(Object.keys(defaultProps).filter((prop) => prop.endsWith('Styles')));
    const _WrappedComponent = forwardRef((props, ref) => {
        const { as, element, ...restProps } = props;
        const propsWithStylesValues = propsWithStyles.map((prop) => props[prop]);
        const mergedStylesMap = useMemo(() => {
            return propsWithStyles.reduce((map, prop) => {
                const restValue = restProps[prop];
                const defaultValue = defaultProps[prop];
                if (restValue != null && defaultValue != null) {
                    map[prop] = mergeStyles(defaultValue, restValue);
                }
                else {
                    map[prop] = restValue ?? defaultValue;
                }
                return map;
            }, {});
        }, [propsWithStylesValues]);
        const elementProps = {
            ...defaultProps,
            ...restProps,
            ...mergedStylesMap,
            as: as ?? extendTag,
            element: element || extendElement,
            ref,
        };
        return createElement(Component, elementProps);
    });
    _WrappedComponent.displayName = `TastyWrappedComponent(${getDisplayName(Component, defaultProps.qa ?? extendTag ?? 'Anonymous')})`;
    return _WrappedComponent;
}
function tastyElement(tastyOptions) {
    let { as: originalAs = 'div', element: defaultElement, styles: defaultStyles, styleProps, variants, tokens: defaultTokens, elements, ...defaultProps } = tastyOptions;
    // Pre-compute merged styles for each variant (if variants are defined)
    // This avoids creating separate component instances per variant
    let variantStylesMap;
    if (variants) {
        const variantEntries = Object.entries(variants);
        variantStylesMap = variantEntries.reduce((map, [variant, variantStyles]) => {
            map[variant] = mergeStyles(defaultStyles, variantStyles);
            return map;
        }, {});
        // Ensure 'default' variant always exists
        if (!variantStylesMap['default']) {
            variantStylesMap['default'] = defaultStyles;
        }
    }
    let { qa: defaultQa, qaVal: defaultQaVal, ...otherDefaultProps } = defaultProps ?? {};
    const _TastyComponent = forwardRef((allProps, ref) => {
        let { as, styles, variant, mods, element, qa, qaVal, className: userClassName, tokens, style, ...otherProps } = allProps;
        // Optimize propStyles extraction - avoid creating empty objects
        let propStyles = null;
        const propsToCheck = styleProps
            ? styleProps.concat(BASE_STYLES)
            : BASE_STYLES;
        for (const prop of propsToCheck) {
            const key = prop;
            if (!propStyles)
                propStyles = {};
            if (key in otherProps) {
                propStyles[key] = otherProps[key];
                delete otherProps[key];
            }
        }
        if (!styles ||
            (styles && Object.keys(styles).length === 0)) {
            styles = undefined;
        }
        // Determine base styles: use variant styles if available, otherwise default styles
        const baseStyles = variantStylesMap
            ? variantStylesMap[variant || 'default'] ??
                variantStylesMap['default']
            : defaultStyles;
        // Merge base styles with instance styles and prop styles
        const allStyles = useMemo(() => {
            const hasStyles = styles && Object.keys(styles).length > 0;
            const hasPropStyles = propStyles && Object.keys(propStyles).length > 0;
            if (!hasStyles && !hasPropStyles) {
                return baseStyles;
            }
            return mergeStyles(baseStyles, styles, propStyles);
        }, [baseStyles, styles, propStyles]);
        // Use the useStyles hook for style generation and injection
        const { className: stylesClassName } = useStyles(allStyles);
        // Merge default tokens with instance tokens (instance overrides defaults)
        const tokensKey = stringifyTokens(tokens);
        const mergedTokens = useMemo(() => {
            if (!defaultTokens && !tokens)
                return undefined;
            if (!defaultTokens)
                return tokens;
            if (!tokens)
                return defaultTokens;
            return { ...defaultTokens, ...tokens };
        }, [tokensKey]);
        // Process merged tokens into inline style properties
        const processedTokenStyle = useMemo(() => {
            return processTokens(mergedTokens);
        }, [mergedTokens]);
        // Merge processed tokens with explicit style prop (style has priority)
        const mergedStyle = useMemo(() => {
            if (!processedTokenStyle && !style)
                return undefined;
            if (!processedTokenStyle)
                return style;
            if (!style)
                return processedTokenStyle;
            return { ...processedTokenStyle, ...style };
        }, [processedTokenStyle, style]);
        let modProps;
        if (mods) {
            const modsObject = mods;
            modProps = modAttrs(modsObject);
        }
        // Merge user className with generated className
        const finalClassName = [userClassName || '', stylesClassName]
            .filter(Boolean)
            .join(' ');
        const elementProps = {
            'data-element': element || defaultElement,
            'data-qa': qa || defaultQa,
            'data-qaval': qaVal || defaultQaVal,
            ...otherDefaultProps,
            ...(modProps || {}),
            ...otherProps,
            className: finalClassName,
            style: mergedStyle,
            ref,
        };
        // Apply the helper to handle is* properties
        handleIsProperties(elementProps);
        const renderedElement = createElement(as ?? originalAs, elementProps);
        return renderedElement;
    });
    _TastyComponent.displayName = `TastyComponent(${defaultProps.qa || originalAs})`;
    // Attach sub-element components if elements are defined
    if (elements) {
        const subElements = Object.entries(elements).reduce((acc, [name, definition]) => {
            acc[name] = createSubElement(name, definition);
            return acc;
        }, {});
        return Object.assign(_TastyComponent, subElements);
    }
    return _TastyComponent;
}
export const Element = tasty({});

        