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

/**
 * Style rendering that works with structured style objects.
 * Eliminates CSS string parsing for better performance.
 *
 * Key optimizations:
 * - Early exit checks (hasSameAttributeConflicts) handle common cases without overhead
 * - Logical rule caching (logicalRulesCache) memoizes expensive style processing
 * - Conflict detection prevents invalid CSS selector combinations
 * - Not-selector optimization reduces CSS size
 * - Priority-based filtering handles mod precedence correctly
 * - Consolidated mod processing eliminates code duplication
 */
import { Lru } from '../parser/lru';
import { createStyle, STYLE_HANDLER_MAP } from '../styles';
import { getModCombinationsIterative } from './getModCombinations';
import { normalizeStyleZones, pointsToZones, } from './responsive';
import { computeState, getModSelector, stringifyStyles, styleStateMapToStyleStateDataList, } from './styles';
// Detect if a value is a state map whose entries contain responsive arrays
function stateMapHasResponsiveArrays(value) {
    if (!value || typeof value !== 'object' || Array.isArray(value))
        return false;
    return Object.values(value).some((v) => Array.isArray(v));
}
// Cache logical rules per styles+breakpoints to avoid recomputation across identical calls
const logicalRulesCache = new Lru(5000);
// Normalize selector suffixes coming from `$` in style handler results.
// Some legacy handlers return suffixes starting with `&` (e.g. '& > *').
// The renderer expects suffixes without the ampersand because it adds
// the parent selector during materialization.
function normalizeDollarSelectorSuffix(suffix) {
    if (!suffix)
        return '';
    return suffix.startsWith('&') ? suffix.slice(1) : suffix;
}
/**
 * Check if a key represents a CSS selector
 */
export function isSelector(key) {
    return (key.startsWith('&') || key.startsWith('.') || key.match(/^[A-Z]/) !== null);
}
/**
 * Transform selector affix by converting capitalized words to sub-element selectors.
 * Returns a selector prefix with leading and trailing spaces.
 *
 * Examples:
 *   '> Body > Row'     -> ' > [data-element="Body"] > [data-element="Row"] '
 *   '> Body > Row >'   -> ' > [data-element="Body"] > [data-element="Row"] > '
 *   '>'                -> ' > '
 */
function transformSelectorAffix(affix) {
    const trimmed = affix.trim();
    if (!trimmed)
        return ' ';
    // Validate that combinators have spaces around them
    // Check for capitalized words adjacent to combinators without spaces
    const invalidPattern = /[A-Z][a-z]*[>+~]|[>+~][A-Z][a-z]*/;
    if (invalidPattern.test(trimmed)) {
        console.error(`[Tasty] Invalid selector affix ($) syntax: "${affix}"\n` +
            `Combinators (>, +, ~) must have spaces around them when used with element names.\n` +
            `Example: Use "$: '> Body > Row'" instead of "$: '>Body>Row'"\n` +
            `This is a design choice: the parser uses simple whitespace splitting for performance.`);
    }
    const tokens = trimmed.split(/\s+/);
    const transformed = tokens.map((token) => /^[A-Z]/.test(token) ? `[data-element="${token}"]` : token);
    return ` ${transformed.join(' ')} `;
}
/**
 * Get the selector suffix for a key
 */
function getSelector(key, styles) {
    if (key.startsWith('&')) {
        return key.slice(1);
    }
    if (key.startsWith('.')) {
        return ` ${key}`;
    }
    if (key.match(/^[A-Z]/)) {
        const affix = styles?.$;
        if (affix !== undefined) {
            const prefix = transformSelectorAffix(String(affix));
            return `${prefix}[data-element="${key}"]`;
        }
        return ` [data-element="${key}"]`;
    }
    return null;
}
/**
 * Parse attribute selector into its components.
 * Simple regex parsing - JS engines optimize repeated patterns internally.
 */
function parseAttributeSelector(selector) {
    // Match patterns like [data-size="medium"] or [data-selected]
    const match = selector.match(/^\[([^=\]]+)(?:="([^"]+)")?\]$/);
    return match
        ? {
            attribute: match[1],
            value: match[2] || 'true', // Handle boolean attributes
            fullSelector: selector,
        }
        : null;
}
function hasConflictingAttributeSelectors(mods, parsedMods) {
    const attributeValues = new Map();
    const attributeBooleans = new Set();
    for (const mod of mods) {
        const parsed = parsedMods?.get(mod) ?? parseAttributeSelector(mod);
        if (!parsed)
            continue;
        if (parsed.value === 'true') {
            // Boolean attribute
            attributeBooleans.add(parsed.attribute);
        }
        else {
            // Value attribute
            if (!attributeValues.has(parsed.attribute)) {
                attributeValues.set(parsed.attribute, []);
            }
            attributeValues.get(parsed.attribute).push(parsed.value);
        }
    }
    // Check for multiple different values for the same attribute
    for (const values of attributeValues.values()) {
        if (values.length > 1)
            return true;
    }
    return false;
}
/**
 * Create a conflict checker function that uses precomputed attribute maps
 * for efficient conflict detection during combination generation
 */
function createAttributeConflictChecker(parsedMods) {
    return (combination) => hasConflictingAttributeSelectors(combination, parsedMods);
}
// Build precomputed attribute maps for efficient not selector optimization
function buildAttributeMaps(currentMods, allMods, parsedModsCache) {
    const allAttributes = new Map();
    const currentAttributes = new Map();
    const parsedMods = parsedModsCache || new Map();
    // Parse all mods once and cache results (only if cache not provided)
    if (!parsedModsCache) {
        const allModsSet = new Set([...currentMods, ...allMods]);
        for (const mod of allModsSet) {
            if (!parsedMods.has(mod)) {
                parsedMods.set(mod, parseAttributeSelector(mod));
            }
        }
    }
    // Build map of all possible values for each attribute
    for (const mod of allMods) {
        const parsed = parsedMods.get(mod);
        if (parsed && parsed.value !== 'true') {
            if (!allAttributes.has(parsed.attribute)) {
                allAttributes.set(parsed.attribute, new Set());
            }
            allAttributes.get(parsed.attribute).add(parsed.value);
        }
    }
    // Build map of current mod attribute values
    for (const mod of currentMods) {
        const parsed = parsedMods.get(mod);
        if (parsed && parsed.value !== 'true') {
            currentAttributes.set(parsed.attribute, parsed.value);
        }
    }
    return { allAttributes, currentAttributes, parsedMods };
}
/**
 * Check if a combination of positive and negative selectors creates a contradiction
 * Returns true if the combination is INVALID and should be pruned
 */
function hasContradiction(currentMods, notMods, parsedMods) {
    // Build maps of positive selector states
    const positiveAttributes = new Map();
    const positiveBooleans = new Set();
    for (const mod of currentMods) {
        const parsed = parsedMods.get(mod);
        if (parsed) {
            if (parsed.value === 'true') {
                // Boolean attribute (e.g., [data-theme])
                positiveBooleans.add(parsed.attribute);
            }
            else {
                // Value attribute (e.g., [data-theme="danger"])
                if (!positiveAttributes.has(parsed.attribute)) {
                    positiveAttributes.set(parsed.attribute, []);
                }
                positiveAttributes.get(parsed.attribute).push(parsed.value);
            }
        }
    }
    // Check negative selectors for contradictions
    for (const mod of notMods) {
        const parsed = parsedMods.get(mod);
        if (parsed) {
            if (parsed.value === 'true') {
                // Negative boolean: !([data-theme])
                // Case 6: Value positive + attribute negative = CONTRADICTION
                if (positiveAttributes.has(parsed.attribute) ||
                    positiveBooleans.has(parsed.attribute)) {
                    return true; // INVALID: can't have value without attribute
                }
            }
            else {
                // Negative value: !([data-theme="danger"])
                // No contradiction - this is valid
            }
        }
    }
    return false;
}
function optimizeNotSelectors(currentMods, allMods, precomputedMaps) {
    const maps = precomputedMaps || buildAttributeMaps(currentMods, allMods);
    const notMods = allMods.filter((mod) => !currentMods.includes(mod));
    const optimizedNotMods = [];
    // Precompute presence of negative boolean attributes to avoid repeated scans
    const negativeBooleanByAttr = new Set();
    for (const mod of notMods) {
        const p = maps.parsedMods.get(mod);
        if (p && p.value === 'true') {
            negativeBooleanByAttr.add(p.attribute);
        }
    }
    // Build maps of positive selector states for subsumption optimization
    const positiveAttributes = new Map();
    const positiveBooleans = new Set();
    for (const mod of currentMods) {
        const parsed = maps.parsedMods.get(mod);
        if (parsed) {
            if (parsed.value === 'true') {
                positiveBooleans.add(parsed.attribute);
            }
            else {
                if (!positiveAttributes.has(parsed.attribute)) {
                    positiveAttributes.set(parsed.attribute, []);
                }
                positiveAttributes.get(parsed.attribute).push(parsed.value);
            }
        }
    }
    for (const mod of notMods) {
        const parsed = maps.parsedMods.get(mod);
        if (parsed && parsed.value !== 'true') {
            // Negative value selector
            // If we already have a value for this attribute, skip this not selector
            // because it's already mutually exclusive (optimization)
            if (maps.currentAttributes.has(parsed.attribute)) {
                continue;
            }
        }
        // If we have a positive value for this attribute, skip the negative boolean
        // This avoids producing selectors like [data-attr="x"]:not([data-attr])
        if (parsed && parsed.value === 'true') {
            if (maps.currentAttributes.has(parsed.attribute)) {
                continue;
            }
        }
        // Case 4 subsumption: If we have a value positive and boolean positive for same attribute
        // The value implies the boolean, so we can skip the boolean from positive mods
        // (This is handled elsewhere - the value selector is more specific)
        // Case 7 subsumption: If we have a value negative and boolean negative for same attribute
        // The boolean negative implies value negative, so skip the value negative
        if (parsed && parsed.value !== 'true') {
            // If we also have the boolean attribute in negative mods, skip the value negative
            if (negativeBooleanByAttr.has(parsed.attribute)) {
                continue;
            }
        }
        optimizedNotMods.push(mod);
    }
    return optimizedNotMods;
}
/**
 * Quick check if there are any same-attribute conflicts (boolean vs value for same attribute).
 * Returns true if conflicts exist, false if filtering can be skipped.
 * This early exit optimization handles the common case efficiently.
 */
function hasSameAttributeConflicts(allMods, parsedModsCache) {
    const attributeCounts = new Map();
    for (const mod of allMods) {
        const parsed = parsedModsCache.get(mod);
        if (!parsed)
            continue;
        const isBool = parsed.value === 'true';
        const existing = attributeCounts.get(parsed.attribute);
        if (existing) {
            // Already have both types for this attribute - conflict exists!
            if ((isBool && existing.hasValue) || (!isBool && existing.hasBool)) {
                return true;
            }
            // Update the types we've seen
            if (isBool)
                existing.hasBool = true;
            else
                existing.hasValue = true;
        }
        else {
            attributeCounts.set(parsed.attribute, {
                hasBool: isBool,
                hasValue: !isBool,
            });
        }
    }
    return false;
}
/**
 * Filter mods based on priority order for same-attribute conflicts.
 * If a boolean selector has higher priority than value selectors for the same attribute,
 * remove the value selectors (they would be shadowed by the boolean).
 *
 * Priority is determined by order in the styleStates (after reversal - earlier = higher priority)
 */
function filterModsByPriority(allMods, styleStates, lookupStyles, parsedModsCache) {
    // Early exit: if no same-attribute conflicts exist, skip all the expensive work
    // This handles the common case efficiently without any cache overhead
    if (!hasSameAttributeConflicts(allMods, parsedModsCache)) {
        return allMods;
    }
    // Build priority map: for each mod, find its earliest appearance in any state list
    const modPriorities = new Map();
    for (const style of lookupStyles) {
        const states = styleStates[style];
        if (!states || states.length === 0)
            continue; // Skip empty states
        // states are already reversed (higher priority = lower index)
        for (let index = 0; index < states.length; index++) {
            const state = states[index];
            if (!state.mods || state.mods.length === 0)
                continue; // Skip empty mods
            for (const mod of state.mods) {
                const currentPriority = modPriorities.get(mod);
                if (currentPriority === undefined || index < currentPriority) {
                    modPriorities.set(mod, index);
                }
            }
        }
    }
    // Group mods by attribute
    const attributeGroups = new Map();
    for (const mod of allMods) {
        const parsed = parsedModsCache.get(mod);
        if (!parsed)
            continue;
        const priority = modPriorities.get(mod);
        if (priority === undefined)
            continue;
        const isBool = parsed.value === 'true';
        let group = attributeGroups.get(parsed.attribute);
        if (!group) {
            group = [];
            attributeGroups.set(parsed.attribute, group);
        }
        group.push({
            mod,
            isBool,
            priority,
        });
    }
    // Filter: for each attribute, if boolean has higher priority than any value, remove values
    const modsToRemove = new Set();
    for (const [attribute, group] of attributeGroups.entries()) {
        // Only process attributes with more than one mod
        if (group.length <= 1)
            continue;
        const boolMods = group.filter((m) => m.isBool);
        const valueMods = group.filter((m) => !m.isBool);
        // Only check if we have both types
        if (boolMods.length === 0 || valueMods.length === 0)
            continue;
        // Check if any boolean has higher priority (lower index) than all values
        for (const boolMod of boolMods) {
            const hasHigherPriorityThanAllValues = valueMods.every((valueMod) => boolMod.priority < valueMod.priority);
            if (hasHigherPriorityThanAllValues) {
                // This boolean shadows all value mods for this attribute
                valueMods.forEach((valueMod) => modsToRemove.add(valueMod.mod));
            }
        }
    }
    return allMods.filter((mod) => !modsToRemove.has(mod));
}
/**
 * Explode a style handler result into logical rules with proper mapping
 * Phase 1: Handler fan-out ($ selectors, arrays)
 * Phase 2: Responsive fan-out (breakpoint arrays)
 * Phase 3: Rule materialization
 */
function explodeHandlerResult(result, zones, selectorSuffix = '', forceBreakpointIdx, responsiveOrigin = false) {
    if (!result)
        return [];
    // Phase 1: Handler fan-out - normalize to array
    const resultArray = Array.isArray(result) ? result : [result];
    const logicalRules = [];
    for (const item of resultArray) {
        if (!item || typeof item !== 'object')
            continue;
        const { $, ...styleProps } = item;
        // Phase 2: Responsive fan-out - handle array values
        const breakpointGroups = new Map();
        if (forceBreakpointIdx !== undefined) {
            // When breakpoint is forced (from responsive processing), use all props for that breakpoint
            const group = {};
            for (const [prop, value] of Object.entries(styleProps)) {
                if (value != null && value !== '') {
                    group[prop] = String(value);
                }
            }
            if (Object.keys(group).length > 0) {
                breakpointGroups.set(forceBreakpointIdx, group);
            }
        }
        else {
            // Normal processing - handle responsive arrays
            const responsiveProps = [];
            for (const [prop, value] of Object.entries(styleProps)) {
                if (Array.isArray(value)) {
                    // Responsive array - create entry for each breakpoint
                    value.forEach((val, idx) => {
                        if (val != null && val !== '' && idx < zones.length) {
                            responsiveProps.push({ prop, value: val, breakpointIdx: idx });
                        }
                    });
                }
                else if (value != null && value !== '') {
                    // Single value - goes to base breakpoint
                    responsiveProps.push({ prop, value, breakpointIdx: 0 });
                }
            }
            // Group by breakpoint index
            for (const { prop, value, breakpointIdx } of responsiveProps) {
                const group = breakpointGroups.get(breakpointIdx) || {};
                group[prop] = String(value);
                breakpointGroups.set(breakpointIdx, group);
            }
        }
        // Phase 3: Selector fan-out - handle $ suffixes
        // IMPORTANT: If we are already in a pseudo-element context (contains '::'),
        // CSS does not allow further descendant/child selectors (e.g., '>*') after
        // a pseudo-element. In such cases we must ignore only the `$`-derived
        // selectors while still preserving base declarations for the current
        // selector. Previously this branch returned early and accidentally dropped
        // all declarations computed before, including valid base ones.
        const inPseudoElementContext = selectorSuffix.includes('::');
        if (inPseudoElementContext && $) {
            // Skip this item entirely to avoid producing invalid selectors like
            // `.t0::before>*`. Other items (without $) in the same handler result
            // will still be processed and preserved.
            continue;
        }
        const suffixes = $
            ? (Array.isArray($) ? $ : [$]).map((s) => selectorSuffix + normalizeDollarSelectorSuffix(String(s)))
            : [selectorSuffix];
        // Early identical-breakpoint coalescing: skip duplicate declarations
        const seenDeclarations = new Map();
        // Process breakpoints in order to prefer lower breakpoint indices
        const sortedBreakpoints = Array.from(breakpointGroups.entries()).sort(([a], [b]) => a - b);
        for (const [breakpointIdx, declarations] of sortedBreakpoints) {
            if (Object.keys(declarations).length === 0)
                continue;
            // Create a stable hash key for identical declarations
            const declarationKeys = Object.keys(declarations).sort();
            const declarationHash = declarationKeys
                .map((key) => `${key}:${declarations[key]}`)
                .join(';');
            const existingBreakpointIdx = seenDeclarations.get(declarationHash);
            if (existingBreakpointIdx !== undefined) {
                // Skip this breakpoint as it has identical declarations to a previous one
                // The CSS cascade will handle the responsive behavior correctly
                continue;
            }
            // Mark this declaration set as seen
            seenDeclarations.set(declarationHash, breakpointIdx);
            // Create logical rules for this unique declaration set
            for (const suffix of suffixes) {
                logicalRules.push({
                    selectorSuffix: suffix,
                    breakpointIdx,
                    declarations,
                    responsiveSource: responsiveOrigin || forceBreakpointIdx !== undefined,
                });
            }
        }
    }
    return logicalRules;
}
/**
 * Convert handler result (CSSMap) to CSS string for global injection
 */
function convertHandlerResultToCSS(result, selectorSuffix = '') {
    if (!result)
        return '';
    if (Array.isArray(result)) {
        const fragments = [];
        for (const item of result) {
            const itemCSS = convertHandlerResultToCSS(item, selectorSuffix);
            if (itemCSS) {
                fragments.push(itemCSS);
            }
        }
        return fragments.join('');
    }
    const { $, css, ...styleProps } = result;
    const styleFragments = [];
    // Process style properties using array accumulation
    for (const [styleName, value] of Object.entries(styleProps)) {
        if (Array.isArray(value)) {
            // Handle array values
            for (const val of value) {
                if (val) {
                    styleFragments.push(`${styleName}: ${val};\n`);
                }
            }
        }
        else if (value) {
            // Handle single values
            styleFragments.push(`${styleName}: ${value};\n`);
        }
    }
    let renderedStyles = styleFragments.join('');
    if (css) {
        renderedStyles = css + '\n' + renderedStyles;
    }
    if (!renderedStyles) {
        return '';
    }
    const finalSelectorSuffix = selectorSuffix || '';
    if (Array.isArray($)) {
        const ruleFragments = [];
        for (const suffix of $) {
            const normalized = suffix
                ? normalizeDollarSelectorSuffix(String(suffix))
                : '';
            ruleFragments.push(`&${finalSelectorSuffix}${normalized}{\n${renderedStyles}}\n`);
        }
        return ruleFragments.join('');
    }
    const normalizedSingle = $ ? normalizeDollarSelectorSuffix(String($)) : '';
    return `&${finalSelectorSuffix}${normalizedSingle}{\n${renderedStyles}}\n`;
}
/**
 * Process state maps with mod combinations and generate logical rules.
 * This consolidates the common logic for handling mod combinations, priority filtering,
 * contradiction checking, and selector optimization.
 */
function processStateMapsWithModCombinations(styleStates, lookupStyles, zones, handler, parentSuffix, allLogicalRules, cachedNormalizeStyleZones, breakpointIdx, responsiveOrigin = false) {
    // Collect all mods from style states
    const allMods = [];
    const seenMods = new Set();
    for (const style of lookupStyles) {
        const states = styleStates[style];
        if (!states)
            continue;
        for (const state of states) {
            if (state.mods) {
                for (const mod of state.mods) {
                    if (!seenMods.has(mod)) {
                        seenMods.add(mod);
                        allMods.push(mod);
                    }
                }
            }
        }
    }
    if (allMods.length === 0) {
        // No mods - just call handler with default state values
        const stateProps = {};
        lookupStyles.forEach((style) => {
            const states = styleStates[style];
            if (states && states.length > 0) {
                stateProps[style] = states[0].value;
            }
        });
        const result = handler(stateProps);
        if (!result)
            return;
        const logicalRules = explodeHandlerResult(result, zones, parentSuffix, breakpointIdx, responsiveOrigin);
        allLogicalRules.push(...logicalRules);
        return;
    }
    // Parse all mods once and share the cache across all operations
    const parsedModsCache = new Map();
    for (const mod of allMods) {
        parsedModsCache.set(mod, parseAttributeSelector(getModSelector(mod)));
    }
    // Apply priority-based filtering for same-attribute boolean vs value conflicts
    const filteredMods = filterModsByPriority(allMods, styleStates, lookupStyles, parsedModsCache);
    // Precompute attribute maps once for all combinations
    const attributeMaps = buildAttributeMaps([], filteredMods, parsedModsCache);
    // Generate combinations with conflict-aware pruning
    const conflictChecker = createAttributeConflictChecker(attributeMaps.parsedMods);
    const combinations = getModCombinationsIterative(filteredMods, true, conflictChecker);
    // Process each mod combination
    combinations.forEach((modCombination) => {
        const stateProps = {};
        // Find matching state for each style
        lookupStyles.forEach((style) => {
            const states = styleStates[style];
            const matchingState = states.find((state) => computeState(state.model, (mod) => modCombination.includes(mod)));
            if (matchingState) {
                stateProps[style] = matchingState.value;
            }
        });
        // Use precomputed maps for efficient not selector optimization
        const currentMaps = buildAttributeMaps(modCombination, filteredMods, parsedModsCache);
        // Compute raw NOTs for contradiction check (before optimization)
        const rawNotMods = filteredMods.filter((mod) => !modCombination.includes(mod));
        // Check for contradictions between positive and negative selectors
        if (hasContradiction(modCombination, rawNotMods, currentMaps.parsedMods)) {
            return; // Skip this invalid combination
        }
        // Optimize NOT selectors afterwards (pure simplification)
        const optimizedNotMods = optimizeNotSelectors(modCombination, filteredMods, currentMaps);
        // Build the mod selector string
        const modsSelectors = `${modCombination
            .map(getModSelector)
            .join('')}${optimizedNotMods
            .map((mod) => {
            const sel = getModSelector(mod);
            return sel.startsWith(':not(') ? sel.slice(5, -1) : `:not(${sel})`;
        })
            .join('')}`;
        // Check if any state value is responsive (array)
        const hasResponsiveStateValues = lookupStyles.some((style) => Array.isArray(stateProps[style]));
        if (hasResponsiveStateValues) {
            // Fan out by breakpoint for responsive state values
            const propsByPoint = zones.map((_, i) => {
                const pointProps = {};
                lookupStyles.forEach((style) => {
                    const v = stateProps[style];
                    if (Array.isArray(v)) {
                        const arr = cachedNormalizeStyleZones(v, zones.length);
                        pointProps[style] = arr?.[i];
                    }
                    else {
                        pointProps[style] = v;
                    }
                });
                return pointProps;
            });
            propsByPoint.forEach((props, bpIdx) => {
                const res = handler(props);
                if (!res)
                    return;
                const logical = explodeHandlerResult(res, zones, `${modsSelectors}${parentSuffix}`, bpIdx, true);
                allLogicalRules.push(...logical);
            });
        }
        else {
            // Simple non-responsive state values
            const result = handler(stateProps);
            if (!result)
                return;
            const logical = explodeHandlerResult(result, zones, `${modsSelectors}${parentSuffix}`, breakpointIdx, responsiveOrigin);
            allLogicalRules.push(...logical);
        }
    });
}
/**
 * Convert logical rules to final StyleResult format
 */
function materializeRules(logicalRules, className, zones) {
    return logicalRules.map((rule) => {
        // Generate base selector
        let selector = `.${className}${rule.selectorSuffix}`;
        // Increase specificity for tasty class selectors by duplicating the class
        if (/^t\d+$/.test(className)) {
            selector = `.${className}${selector}`;
        }
        const declarations = Object.entries(rule.declarations)
            .map(([prop, value]) => `${prop}: ${value};`)
            .join(' ');
        const q = rule.breakpointIdx > 0
            ? zones[rule.breakpointIdx]?.mediaQuery
            : rule.responsiveSource
                ? zones[0]?.mediaQuery
                : undefined;
        const atRules = q ? [`@media ${q}`] : undefined;
        return {
            selector,
            declarations,
            atRules,
        };
    });
}
/**
 * Core style processing logic that generates logical rules
 * Used by the unified renderStyles function
 */
function generateLogicalRules(styles, responsive = [], parentSuffix = '') {
    const zones = pointsToZones(responsive || []);
    const allLogicalRules = [];
    // Cache for normalizeStyleZones results to avoid repeated computation
    // WeakMap allows automatic cleanup when arrays are garbage collected
    const normalizeCache = new WeakMap();
    // Helper function to get cached normalizeStyleZones result
    function cachedNormalizeStyleZones(value, zoneNumber) {
        // Only cache for arrays - other types are fast to process
        if (!Array.isArray(value)) {
            return normalizeStyleZones(value, zoneNumber);
        }
        // Check if we have a cache for this array reference
        let zoneCache = normalizeCache.get(value);
        if (!zoneCache) {
            zoneCache = new Map();
            normalizeCache.set(value, zoneCache);
        }
        // Check if we have a cached result for this zone count
        let result = zoneCache.get(zoneNumber);
        if (result === undefined) {
            result = normalizeStyleZones(value, zoneNumber);
            zoneCache.set(zoneNumber, result);
        }
        return result;
    }
    // Local versions of helpers that leverage cachedNormalizeStyleZones
    function stateMapToArrayOfStateMapsLocal(value, zoneNumber) {
        // Short-circuit for single zone - avoid array allocation
        if (zoneNumber === 1) {
            const singleMap = {};
            for (const [state, stateValue] of Object.entries(value)) {
                if (Array.isArray(stateValue)) {
                    // Take the first value from the array or null if empty
                    singleMap[state] = stateValue.length > 0 ? stateValue[0] : null;
                }
                else {
                    singleMap[state] = stateValue;
                }
            }
            return [singleMap];
        }
        const result = Array.from({ length: zoneNumber }, () => ({}));
        for (const [state, stateValue] of Object.entries(value)) {
            const perZone = Array.isArray(stateValue)
                ? cachedNormalizeStyleZones(stateValue, zoneNumber)
                : Array(zoneNumber).fill(stateValue);
            for (let i = 0; i < zoneNumber; i++) {
                const v = perZone[i];
                result[i][state] = v;
            }
        }
        return result;
    }
    function normalizeArrayWithStateMapsLocal(valueArray, zoneNumber) {
        // Short-circuit for single zone - avoid array propagation and mapping
        if (zoneNumber === 1) {
            const firstEntry = valueArray.length > 0 ? valueArray[0] : null;
            if (firstEntry &&
                typeof firstEntry === 'object' &&
                !Array.isArray(firstEntry)) {
                return [firstEntry];
            }
            return [{ '': firstEntry }];
        }
        const propagated = cachedNormalizeStyleZones(valueArray, zoneNumber);
        // Trim trailing null/undefined entries to reduce processing
        let lastNonNullIndex = propagated.length - 1;
        while (lastNonNullIndex >= 0 && propagated[lastNonNullIndex] == null) {
            lastNonNullIndex--;
        }
        // If all entries are null, return minimal array
        if (lastNonNullIndex < 0) {
            return Array.from({ length: zoneNumber }, () => ({ '': null }));
        }
        // Process only up to the last non-null entry, then fill the rest with the last value
        const result = [];
        let lastProcessedEntry = null;
        for (let i = 0; i <= lastNonNullIndex; i++) {
            const entry = propagated[i];
            let processedEntry;
            if (entry && typeof entry === 'object' && !Array.isArray(entry)) {
                processedEntry = entry;
            }
            else {
                processedEntry = { '': entry };
            }
            result.push(processedEntry);
            lastProcessedEntry = processedEntry;
        }
        // Fill remaining slots with the last processed entry (CSS cascade behavior)
        for (let i = lastNonNullIndex + 1; i < zoneNumber; i++) {
            result.push(lastProcessedEntry || { '': null });
        }
        return result;
    }
    // Process styles recursively, preserving mod selectors and combining with nested selector suffixes
    function processStyles(currentStyles, parentSuffix = '') {
        const keys = Object.keys(currentStyles || {});
        const selectorKeys = keys.filter((key) => isSelector(key));
        // Recurse into nested selectors first to compute proper suffix chaining
        for (const key of selectorKeys) {
            const suffix = getSelector(key, currentStyles[key]);
            if (suffix && currentStyles[key]) {
                // Remove $ key to prevent it from being processed as a style property
                const { $: _omit, ...cleanedStyles } = currentStyles[key];
                processStyles(cleanedStyles, `${parentSuffix}${suffix}`);
            }
        }
        // Build handler queue for style properties at this level
        const handlerQueue = [];
        const seenHandlers = new Set();
        keys.forEach((styleName) => {
            if (isSelector(styleName))
                return;
            let handlers = STYLE_HANDLER_MAP[styleName];
            if (!handlers) {
                handlers = STYLE_HANDLER_MAP[styleName] = [createStyle(styleName)];
            }
            handlers.forEach((handler) => {
                if (seenHandlers.has(handler)) {
                    return;
                }
                seenHandlers.add(handler);
                let isResponsive = false;
                const lookupStyles = handler.__lookupStyles;
                const filteredStyleMap = lookupStyles.reduce((map, name) => {
                    const value = currentStyles?.[name];
                    if (value !== undefined) {
                        // Case 1: state-map-of-arrays → array-of-state-maps
                        if (value &&
                            typeof value === 'object' &&
                            !Array.isArray(value) &&
                            stateMapHasResponsiveArrays(value)) {
                            map[name] = stateMapToArrayOfStateMapsLocal(value, zones.length);
                            isResponsive = true;
                        }
                        else if (Array.isArray(value)) {
                            // Case 2: array that may contain state maps → normalize to array-of-state-maps
                            if (value.length > 0) {
                                map[name] = normalizeArrayWithStateMapsLocal(value, zones.length);
                                isResponsive = true;
                            }
                        }
                        else {
                            map[name] = value;
                        }
                    }
                    return map;
                }, {});
                handlerQueue.push({
                    handler,
                    styleMap: filteredStyleMap,
                    isResponsive,
                });
            });
        });
        // Process handlers using the three-phase approach
        handlerQueue.forEach(({ handler, styleMap, isResponsive }) => {
            const lookupStyles = handler.__lookupStyles;
            if (isResponsive) {
                // For responsive styles, resolve arrays using normalizeStyleZones
                const valueMap = lookupStyles.reduce((map, style) => {
                    map[style] = cachedNormalizeStyleZones(styleMap[style], zones.length);
                    return map;
                }, {});
                // Create props for each breakpoint
                const propsByPoint = zones.map((zone, i) => {
                    const pointProps = {};
                    lookupStyles.forEach((style) => {
                        if (valueMap != null && valueMap[style] != null) {
                            pointProps[style] = valueMap[style][i];
                        }
                    });
                    return pointProps;
                });
                // Call handler for each breakpoint, with state map processing if needed
                propsByPoint.forEach((pointProps, breakpointIdx) => {
                    const hasStateMapsAtPoint = lookupStyles.some((style) => {
                        const v = pointProps[style];
                        return v && typeof v === 'object' && !Array.isArray(v);
                    });
                    if (hasStateMapsAtPoint) {
                        // Build styleStates from point props
                        const styleStates = {};
                        lookupStyles.forEach((style) => {
                            const v = pointProps[style];
                            if (v && typeof v === 'object' && !Array.isArray(v)) {
                                const { states } = styleStateMapToStyleStateDataList(v);
                                styleStates[style] = states;
                            }
                            else {
                                styleStates[style] = [{ mods: [], notMods: [], value: v }];
                            }
                        });
                        // Use the consolidated helper for mod combination processing
                        processStateMapsWithModCombinations(styleStates, lookupStyles, zones || [], handler, parentSuffix, allLogicalRules, cachedNormalizeStyleZones, breakpointIdx, true);
                    }
                    else {
                        const result = handler(pointProps);
                        if (!result)
                            return;
                        const logicalRules = explodeHandlerResult(result, zones || [], parentSuffix, breakpointIdx, true);
                        allLogicalRules.push(...logicalRules);
                    }
                });
            }
            else {
                // For non-responsive styles, check if any values have state maps
                const hasStateMaps = lookupStyles.some((style) => {
                    const value = styleMap[style];
                    return value && typeof value === 'object' && !Array.isArray(value);
                });
                if (hasStateMaps) {
                    // Build styleStates from styleMap
                    const styleStates = {};
                    lookupStyles.forEach((style) => {
                        const value = styleMap[style];
                        if (value && typeof value === 'object' && !Array.isArray(value)) {
                            const { states } = styleStateMapToStyleStateDataList(value);
                            styleStates[style] = states;
                        }
                        else {
                            // Simple value, create a single state
                            styleStates[style] = [{ mods: [], notMods: [], value }];
                        }
                    });
                    // Use the consolidated helper for mod combination processing
                    processStateMapsWithModCombinations(styleStates, lookupStyles, zones || [], handler, parentSuffix, allLogicalRules, cachedNormalizeStyleZones);
                }
                else {
                    // Simple case: no state maps, call handler directly
                    const result = handler(styleMap);
                    if (result) {
                        const logical = explodeHandlerResult(result, zones || [], parentSuffix);
                        allLogicalRules.push(...logical);
                    }
                }
            }
        });
    }
    // Kick off processing from the root styles with empty suffix
    processStyles(styles, parentSuffix);
    return allLogicalRules;
}
export function renderStyles(styles, responsive = [], classNameOrSelector) {
    const directSelector = !!classNameOrSelector;
    if (!styles) {
        return directSelector ? [] : { rules: [] };
    }
    // Generate logical rules using shared pipeline (memoized per styles+breakpoints)
    const stylesKey = stringifyStyles(styles);
    const bpKey = (responsive || []).join(',');
    const lrKey = `${stylesKey}#${bpKey}`;
    let allLogicalRules = logicalRulesCache.get(lrKey);
    if (!allLogicalRules) {
        allLogicalRules = generateLogicalRules(styles, responsive);
        logicalRulesCache.set(lrKey, allLogicalRules);
    }
    const zones = pointsToZones(responsive || []);
    if (directSelector) {
        // Direct selector mode: convert logical rules directly to StyleResult format
        if (!classNameOrSelector) {
            throw new Error('directSelector mode requires classNameOrSelector');
        }
        return allLogicalRules.map((rule) => {
            // Replace & with the actual selector or append suffix to selector
            let finalSelector = rule.selectorSuffix
                ? `${classNameOrSelector}${rule.selectorSuffix}`
                : classNameOrSelector;
            // Increase specificity for tasty class selectors by duplicating the class
            if (finalSelector.startsWith('.') && /^\.t\d+/.test(finalSelector)) {
                const classMatch = finalSelector.match(/^\.t\d+/);
                if (classMatch) {
                    const baseClass = classMatch[0];
                    finalSelector = baseClass + finalSelector;
                }
            }
            const declarations = Object.entries(rule.declarations)
                .map(([prop, value]) => `${prop}: ${value};`)
                .join(' ');
            const q = rule.breakpointIdx > 0
                ? zones[rule.breakpointIdx]?.mediaQuery
                : rule.responsiveSource
                    ? zones[0]?.mediaQuery
                    : undefined;
            const atRules = q ? [`@media ${q}`] : undefined;
            return {
                selector: finalSelector,
                declarations,
                atRules,
            };
        });
    }
    // Standard mode: use accumulation and materialization with className
    const accumulatedRules = new Map();
    for (const rule of allLogicalRules) {
        // Create a key based on breakpointIdx, selectorSuffix, and responsiveOrigin
        const ruleKey = `${rule.breakpointIdx}|${rule.selectorSuffix}|${rule.responsiveSource}`;
        const existing = accumulatedRules.get(ruleKey);
        if (existing) {
            // Merge declarations from this rule into the existing one
            Object.assign(existing.declarations, rule.declarations);
        }
        else {
            // Create a new accumulated rule
            accumulatedRules.set(ruleKey, {
                selectorSuffix: rule.selectorSuffix,
                breakpointIdx: rule.breakpointIdx,
                declarations: { ...rule.declarations },
                responsiveSource: rule.responsiveSource,
            });
        }
    }
    // If no className provided, return rules with needsClassName flag
    if (!classNameOrSelector) {
        const materializedRules = Array.from(accumulatedRules.values()).map((rule) => {
            const declarations = Object.entries(rule.declarations)
                .map(([prop, value]) => `${prop}: ${value};`)
                .join(' ');
            const q = rule.breakpointIdx > 0
                ? zones[rule.breakpointIdx]?.mediaQuery
                : rule.responsiveSource
                    ? zones[0]?.mediaQuery
                    : undefined;
            const atRules = q ? [`@media ${q}`] : undefined;
            return {
                selector: rule.selectorSuffix || '',
                declarations,
                atRules,
                needsClassName: true, // Flag for injector to prepend className
            };
        });
        return {
            rules: materializedRules,
        };
    }
    // Materialize the accumulated logical rules into final format
    const finalRulesRaw = materializeRules(Array.from(accumulatedRules.values()), classNameOrSelector, zones || []);
    // Simplified deduplication (should be much less work now)
    const seen = new Set();
    const finalRules = finalRulesRaw.filter((rule) => {
        const at = rule.atRules && rule.atRules.length ? `@${rule.atRules.join('|')}` : '';
        const key = `${rule.selector}|${rule.declarations}|${at}`;
        if (seen.has(key))
            return false;
        seen.add(key);
        return true;
    });
    return {
        rules: finalRules,
        className: classNameOrSelector,
    };
}

        