// If `opacity` exists, we apply a `color-mix` formula to blend colors
// If `multiple` exists, we apply the `calc()` function to multiply the reference value
// Otherwise, we simply replace the reference with its corresponding variable
function processTokenValue(value, refs, multiple, opacity) {
    // Remove `mapping` from token.name
    const cleanName = refs.name.replace(/mapping-?/g, '');
    if (opacity) {
        return `color-mix(in srgb, var(--${cleanName}), transparent calc(100% - ${opacity}))`;
    }
    else if (multiple) {
        return value.replace(refs.value, `calc(var(--${cleanName}) * ${multiple})`);
    }
    else {
        return value.replace(refs.value, `var(--${cleanName})`);
    }
}
// Helper function to resolve references for both `multiple` and `opacity` and return the updated value
function resolveTokenReferences(token, unfilteredTokens, usesReferences, getReferences) {
    let multiple = token.original?.attributes?.multiple;
    let opacity = token.original?.attributes?.opacity;
    // Resolve references in `multiple` and `opacity`
    multiple = resolveAndReplaceReferences(multiple, unfilteredTokens, usesReferences, getReferences);
    opacity = resolveAndReplaceReferences(opacity, unfilteredTokens, usesReferences, getReferences);
    return { multiple, opacity };
}
// Helper function to resolve references and return the token name
function resolveAndReplaceReferences(attribute, unfilteredTokens, usesReferences, getReferences) {
    if (attribute && usesReferences(attribute, unfilteredTokens)) {
        const refs = getReferences(attribute, unfilteredTokens);
        refs.forEach(ref => {
            // Replace the reference with the resolved token name
            attribute = `var(--${ref.name})`;
        });
    }
    return attribute;
}
/**
 * @param selector The raw CSS selector string
 * @returns A cleaned string of class names
 */
const cleanSelector = (selector) => {
    return selector?.replace(/^\.wm-app\s*/, '') // Remove .wm-app prefix if present
        .replace(/^\./, '') // Remove leading dot
        .replace(/\./g, ' ') || ''; // Replace remaining dots with spaces
};
const cssVarCalcMixFormatter = (usesReferences, getReferences, fs) => ({
    name: 'wm-calc-colormix-format',
    format: function ({ dictionary, options }) {
        const { selector, tokenReadmePath } = options;
        // Prepare Markdown table only if enabled
        const tableRows = ['| Token | Description |', '|-------|------------|'];
        // Generate CSS variable definitions
        const variables = dictionary.allTokens
            .map((token) => {
            // Remove `mapping` from token.name
            const cleanName = token.name.replace(/mapping-?/g, '');
            let value = getProcessedTokenValue(token, dictionary, usesReferences, getReferences);
            // Add to table if token has a description and table generation is enabled
            if (tokenReadmePath && token?.attributes?.description) {
                const pathName = token.path
                    .slice(1) // ignore first key (btn, data-table etc.,)
                    .filter(key => key !== 'mapping') // remove 'mapping'
                    .join('.');
                // Keep full description but handle <br> tags properly for table
                let safeDescription = token?.attributes?.description
                    .replace(/\|/g, '\\|') // escape pipe characters
                    .replace(/<br\s*\/?>/gi, '<br>') // keep <br> tags as HTML for proper rendering
                    .trim();
                tableRows.push(`| ${pathName} | ${safeDescription} |`);
            }
            return `--${cleanName}: ${value};`;
        })
            .join(`\n`);
        // Write the table if tokens has description
        if (tokenReadmePath && tableRows.length > 2 && fs) {
            tableRows.unshift(`<!-- AUTO-GENERATED FILE. Do not edit manually. -->`, ``, `# Token Reference Table`, ``, `This table lists all tokens for this component:`, ``);
            fs.writeFileSync(tokenReadmePath, tableRows.join('\n'), 'utf8');
            console.log(`Token description table generated: ${tokenReadmePath}`);
        }
        // Return correctly formatted output based on selector
        return selector && selector.trim()
            ? `${selector} {\n${variables}\n}\n` // Wrap in selector
            : `${variables}\n`; // No selector, no closing bracket
    },
});
function getProcessedTokenValue(token, dictionary, usesReferences, getReferences) {
    let value = String(token.value);
    // Checking if the token has references
    if (usesReferences(token.original.value, dictionary.unfilteredTokens)) {
        const refs = getReferences(token.original.value, dictionary.unfilteredTokens);
        refs.forEach((ref) => {
            // Resolve references for `multiple` and `opacity`
            const { multiple, opacity } = resolveTokenReferences(token, dictionary.unfilteredTokens, usesReferences, getReferences);
            // Process the value based on resolved references
            value = processTokenValue(value, ref, multiple, opacity);
        });
    }
    else {
        value = token.original.value || token.value;
    }
    return value;
}
const componentVariantsFormatter = (usesReferences, getReferences, platform, options = {}) => ({
    name: 'css/component-variants',
    format: function ({ dictionary, file, ...rest }) {
        const componentRootKey = Object.keys(dictionary.tokens)[0];
        if (!componentRootKey) {
            console.warn(`[Style Dictionary] Could not derive component name from source ${file.destination}`);
            return '';
        }
        const groupedVariants = groupTokensByVariantType(dictionary, componentRootKey, platform);
        const cssBlocks = Object.values(groupedVariants).map(group => generateCssBlockFromGroup(group, dictionary, usesReferences, getReferences, platform));
        // --- README GENERATION ---
        const generateReadmeFn = options.generateComponentReadme;
        if (generateReadmeFn) {
            generateReadmeFn(componentRootKey, groupedVariants, platform, dictionary, { ...rest.options });
        }
        // --- END README GENERATION ---
        return cssBlocks.join('\n\n');
    }
});
const groupTokensByVariantType = (dictionary, componentRootKey, platform) => {
    const groups = {};
    const isMobile = platform === 'mobile';
    const mappingStore = {};
    dictionary.allTokens.forEach((token) => {
        const path = token.path;
        const appearanceIndex = path.indexOf('appearances');
        const variantGroupIndex = path.indexOf('variantGroups');
        const stateIndex = path.indexOf('states');
        const isState = stateIndex !== -1;
        const state = isState ? path[stateIndex + 1] : null;
        const appearance = appearanceIndex !== -1 ? path[appearanceIndex + 1] : null;
        const groupType = variantGroupIndex !== -1 ? path[variantGroupIndex + 1] : null;
        const variant = variantGroupIndex !== -1 ? path[variantGroupIndex + 2] : null;
        const isVariantToken = appearance && groupType && variant;
        const isTopLevelMapping = appearanceIndex === -1 && path[1] === 'mapping';
        const isAppearanceMapping = appearanceIndex !== -1 && path[appearanceIndex + 2] === 'mapping';
        let key, type, group;
        if (isVariantToken) {
            key = `${componentRootKey}-${appearance}-${groupType}-${variant}${isState ? `-${state}` : ''}`;
            type = isState ? 'state-variant' : 'variant';
            group = {
                type,
                component: componentRootKey,
                appearance,
                groupType,
                variant,
                state,
            };
            if (!groups[key]) {
                groups[key] = { ...group, tokens: [] };
                if (isMobile && mappingStore[appearance]) {
                    groups[key].tokens.push(...mappingStore[appearance]);
                }
            }
        }
        else if (isTopLevelMapping) {
            key = `${componentRootKey}-mapping${isState ? `-${state}` : ''}`;
            type = isState ? 'state-mapping' : 'mapping';
            group = {
                type,
                component: componentRootKey,
                appearance: 'mapping',
                state,
                isRootMapping: true,
            };
        }
        else if (isAppearanceMapping) {
            const appearanceDef = dictionary.tokens?.[componentRootKey]?.appearances?.[appearance];
            const hasVariants = appearanceDef?.variantGroups && Object.keys(appearanceDef.variantGroups).length > 0;
            // If mobile + variants exist, skip creating this class — mapping will be merged
            if (isMobile && hasVariants && !isState) {
                mappingStore[appearance] ?? (mappingStore[appearance] = []);
                mappingStore[appearance].push(token);
                return; // skip adding mapping like .btn-outlined
            }
            // Else proceed to generate a separate class for mapping
            key = `${componentRootKey}-${appearance}-mapping${isState ? `-${state}` : ''}`;
            type = isState ? 'state-mapping' : 'mapping';
            group = {
                type,
                component: componentRootKey,
                appearance,
                state,
            };
            if (isMobile && !isState) {
                mappingStore[appearance] ?? (mappingStore[appearance] = []);
                mappingStore[appearance].push(token);
            }
        }
        if (key) {
            groups[key] ?? (groups[key] = { ...group, tokens: [] });
            groups[key].tokens.push(token);
        }
    });
    return groups;
};
// output: a selector string based on the group type and state
const getStateSelector = (dictionary, component, appearance, state, groupType, variant, type, platform, isRootMapping) => {
    const meta = dictionary.unfilteredTokens?.[component]?.meta;
    const appearanceMeta = meta?.appearances?.[appearance];
    const selectors = [];
    if (type === 'state-variant') {
        selectors.push(appearanceMeta?.variantGroups?.[groupType]?.[variant]?.states?.[state]?.selector?.[platform]);
    }
    if (type === 'state-mapping' || type === 'state-variant') {
        selectors.push(appearanceMeta?.mapping?.states?.[state]?.selector?.[platform], meta?.mapping?.states?.[state]?.selector?.[platform]);
    }
    const selector = (platform === "mobile" && (type === "state-variant" || (type == "state-mapping" && !isRootMapping))
        ? `${meta?.mapping?.selector?.[platform]}${selectors.find(Boolean)}`
        : selectors.find(Boolean)) || `:${state}`;
    // Return the first defined selector or fallback to pseudo-class
    return selector;
};
const getSelectorFromGroup = (group, dictionary, platform) => {
    const { component, appearance, type, groupType, variant, state, isRootMapping } = group;
    const meta = dictionary.unfilteredTokens?.[component]?.meta;
    const appearanceMeta = meta?.appearances?.[appearance];
    const defaultBase = `.${component}-${appearance}`;
    const rootMappingSelector = meta?.mapping?.selector?.[platform] || `.${component}`;
    const appearanceMappingSelector = appearanceMeta?.mapping?.selector?.[platform] || defaultBase;
    const getCombinedVariantSelector = () => {
        const selector = appearanceMeta?.variantGroups?.[groupType]?.[variant]?.selector?.[platform];
        if (platform === 'mobile') {
            return selector || `${[
                appearanceMappingSelector || [component, appearance].filter(Boolean).join('-'),
                variant
            ].filter(Boolean).join('-')}`;
        }
        return selector
            ? expandSelectors(platform, appearanceMappingSelector, selector)
            : expandSelectors(platform, appearanceMappingSelector, `.${component}-${variant}`);
    };
    const getStateSelectorWrapper = () => getStateSelector(dictionary, component, appearance, state, groupType, variant, type, platform, isRootMapping);
    switch (type) {
        case 'mapping':
            return isRootMapping ? rootMappingSelector : appearanceMappingSelector;
        case 'variant':
            return getCombinedVariantSelector();
        case 'state-mapping':
            return isRootMapping
                ? expandSelectors(platform, rootMappingSelector, getStateSelectorWrapper())
                : expandSelectors(platform, appearanceMappingSelector, getStateSelectorWrapper());
        case 'state-variant':
            return expandSelectors(platform, getCombinedVariantSelector(), getStateSelectorWrapper());
        default:
            return '';
    }
};
const expandSelectors = (platform, baseSelectors, stateSelectors = ['']) => {
    if (platform == "web") {
        const bases = baseSelectors.split(',').filter(Boolean);
        const states = stateSelectors.split(',').filter(Boolean);
        const combos = [];
        for (const base of bases) {
            for (const state of states) {
                combos.push(`${base}${state}`);
            }
        }
        return combos.join(',');
    }
    else {
        return `${baseSelectors}${stateSelectors}`;
    }
};
// takes a group object and a dictionary, and returns a CSS block
// Example output:
// .btn-filled-status-primary {
//   --wm-btn-filled-status-primary-color: #fff;
//}
const generateCssBlockFromGroup = (group, dictionary, usesReferences, getReferences, platform) => {
    const { tokens, component, appearance, groupType, variant } = group;
    let selector = getSelectorFromGroup(group, dictionary, platform);
    if (platform === 'web' && selector) {
        selector = scopeSelectorForWeb(selector);
    }
    if (!selector)
        return '';
    const cssProps = tokens
        .map((token) => {
        const value = getProcessedTokenValue(token, dictionary, usesReferences, getReferences);
        const baseProp = getBasePropertyName(token.path, component, appearance, groupType, variant);
        const cssVarName = `--wm-${component}${baseProp ? `-${baseProp}` : ''}`.replace(/-+$/, '');
        return `  ${cssVarName}: ${value};`;
    })
        .join('\n');
    if (platform === 'web') {
        return `${selector} {\n${cssProps}\n}`;
    }
    else {
        const selectors = selector.split(',').map(s => s.trim()).filter(Boolean);
        //if there are multiple selectors extract custom variant from the first selector and apply it to subsequent selectors
        if (selectors.length > 1) {
            const firstSelector = selectors[0];
            const hasCustomAppearance = /^\.[\w-]+\./.test(firstSelector);
            if (hasCustomAppearance) {
                // Extract base selector (e.g., ".btn-success" from ".btn-success.app-button-disabled")
                const baseMatch = firstSelector.match(/^(\.[\w-]+)\./);
                const baseSelector = baseMatch ? baseMatch[1] : '';
                // Apply base selector to all subsequent selectors
                selectors.forEach((selector, i) => {
                    if (baseSelector && !selector.startsWith(baseSelector)) {
                        selectors[i] = `${baseSelector}${selector}`;
                    }
                });
            }
        }
        return `${selectors.join(',\n')} {\n${cssProps}\n}`;
    }
};
/**
 * Derives a clean CSS variable suffix from the token path.
 * Removes known structural segments like component name, appearance, state, variant, etc.
 *
 * @param tokenPath - The full token path array
 * @param componentRootKey - e.g., "btn"
 * @param appearance - e.g., "filled"
 * @param groupType - e.g., "status" or "size"
 * @param variant - e.g., "primary", "lg"
 * @returns A kebab-case string like "color-background" to append to --wm-btn
 */
const getBasePropertyName = (tokenPath, componentRootKey, appearance, groupType, variant) => {
    const stateIndex = tokenPath.indexOf('states');
    const stateName = stateIndex !== -1 ? tokenPath[stateIndex + 1] : null;
    // All known structural segments to exclude from final name
    const ignoreSegments = new Set([
        componentRootKey,
        'appearances',
        appearance,
        'mapping',
        'states',
        stateName,
        'variantGroups',
        groupType,
        variant
    ]);
    // Sanitize helper: removes illegal characters like @ and trims leading/trailing dashes
    const sanitize = (segment) => segment.replace(/[^a-zA-Z0-9-_]/g, '-').replace(/^-+|-+$/g, '').trim();
    // Build property name from remaining segments
    const propertyName = tokenPath
        .filter(segment => segment && !ignoreSegments.has(segment))
        .map(sanitize)
        .join('-');
    return propertyName;
};
/**
 * Prefixes `.wm-app` to each selector in a comma-separated string for web
 */
function scopeSelectorForWeb(selector) {
    if (!selector)
        return '';
    return selector
        .split(',')
        .map(s => s.trim())
        .map(s => (s.startsWith('.wm-app') ? s : `.wm-app ${s}`))
        .join(', ');
}
export { cssVarCalcMixFormatter, componentVariantsFormatter, getSelectorFromGroup, scopeSelectorForWeb, cleanSelector };
//# sourceMappingURL=style-dictionary-utils.js.map