"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.findRule = exports.isStMixinMarker = exports.stMixinMarker = exports.createSubsetAst = exports.isInConditionalGroup = exports.isChildOfAtRule = void 0;
const selector_1 = require("./selector");
const css_selector_parser_1 = require("@tokey/css-selector-parser");
const postcss = __importStar(require("postcss"));
const custom_selector_1 = require("./custom-selector");
function isChildOfAtRule(rule, atRuleName) {
    let currentParent = rule.parent;
    while (currentParent) {
        if (currentParent.type === 'atrule' &&
            currentParent.name === atRuleName) {
            return true;
        }
        currentParent = currentParent.parent;
    }
    return false;
}
exports.isChildOfAtRule = isChildOfAtRule;
function isInConditionalGroup(node, includeRoot = true) {
    // https://www.w3.org/TR/css-conditional-3/#contents-of
    const parent = node.parent;
    return (parent &&
        ((includeRoot && parent.type === `root`) ||
            (parent.type === `atrule` && (parent.name === `media` || parent.name === `supports`))));
}
exports.isInConditionalGroup = isInConditionalGroup;
function createSubsetAst(root, selectorPrefix, mixinTarget, isRoot = false, getCustomSelector, isNestedInMixin = false) {
    // keyframes on class mixin?
    const prefixSelectorList = (0, selector_1.parseSelectorWithCache)(selectorPrefix);
    const prefixType = prefixSelectorList[0].nodes[0];
    const containsPrefix = containsMatchInFirstChunk.bind(null, prefixType);
    const mixinRoot = mixinTarget ? mixinTarget : postcss.root();
    root.nodes?.forEach((node) => {
        if (node.type === 'decl') {
            mixinTarget?.append(node.clone());
        }
        else if (node.type === `rule` &&
            (node.selector === ':vars' || node.selector === ':import')) {
            // nodes that don't mix
            return;
        }
        else if (node.type === `rule`) {
            const selectorAst = (0, selector_1.parseSelectorWithCache)(node.selector, { clone: true });
            let ast = isRoot
                ? (0, selector_1.scopeNestedSelector)(prefixSelectorList, selectorAst, true).ast
                : selectorAst;
            if (getCustomSelector) {
                ast = (0, custom_selector_1.transformInlineCustomSelectors)(ast, getCustomSelector, () => {
                    /*don't report*/
                });
            }
            const matchesSelectors = isRoot || isNestedInMixin ? ast : ast.filter((node) => containsPrefix(node));
            if (matchesSelectors.length) {
                const selector = (0, selector_1.stringifySelector)(matchesSelectors.map((selectorNode) => {
                    if (!isRoot) {
                        selectorNode = fixChunkOrdering(selectorNode, prefixType);
                    }
                    replaceTargetWithMixinAnchor(selectorNode, prefixType);
                    return selectorNode;
                }));
                const clonedRule = createSubsetAst(node, selectorPrefix, node.clone({ selector, nodes: [] }), isRoot, getCustomSelector, true /*isNestedInMixin*/);
                mixinRoot.append(clonedRule);
            }
        }
        else if (node.type === `atrule`) {
            if (node.name === 'media' ||
                node.name === 'supports' ||
                node.name === 'st-scope' ||
                node.name === 'layer' ||
                node.name === 'container') {
                let scopeSelector = node.name === 'st-scope' ? node.params : '';
                let atruleHasMixin = isNestedInMixin || false;
                if (scopeSelector) {
                    const ast = (0, selector_1.parseSelectorWithCache)(scopeSelector, { clone: true });
                    const matchesSelectors = isRoot
                        ? ast
                        : ast.filter((node) => containsPrefix(node));
                    if (matchesSelectors.length) {
                        atruleHasMixin = true;
                        scopeSelector = (0, selector_1.stringifySelector)(matchesSelectors.map((selectorNode) => {
                            if (!isRoot) {
                                selectorNode = fixChunkOrdering(selectorNode, prefixType);
                            }
                            replaceTargetWithMixinAnchor(selectorNode, prefixType);
                            return selectorNode;
                        }));
                    }
                }
                const atRuleSubset = createSubsetAst(node, selectorPrefix, postcss.atRule({
                    params: scopeSelector || node.params,
                    name: node.name,
                }), isRoot, getCustomSelector, atruleHasMixin);
                if (atRuleSubset.nodes) {
                    mixinRoot.append(atRuleSubset);
                }
            }
            else if (isRoot) {
                mixinRoot.append(node.clone());
            }
        }
        else {
            // TODO: add warn?
        }
    });
    return mixinRoot;
}
exports.createSubsetAst = createSubsetAst;
exports.stMixinMarker = 'st-mixin-marker';
const isStMixinMarker = (node) => node.type === 'attribute' && node.value === exports.stMixinMarker;
exports.isStMixinMarker = isStMixinMarker;
function replaceTargetWithMixinAnchor(selectorNode, prefixType) {
    (0, selector_1.walkSelector)(selectorNode, (node) => {
        if ((0, selector_1.matchTypeAndValue)(node, prefixType)) {
            (0, selector_1.convertToSelector)(node).nodes = [
                {
                    type: `attribute`,
                    value: exports.stMixinMarker,
                    start: node.start,
                    end: node.end,
                },
            ];
        }
    });
}
function fixChunkOrdering(selectorNode, prefixType) {
    const compound = (0, css_selector_parser_1.groupCompoundSelectors)(selectorNode, {
        deep: true,
        splitPseudoElements: false,
    });
    (0, selector_1.walkSelector)(compound, (node) => {
        if (node.type === `compound_selector`) {
            const simpleNodes = node.nodes;
            for (let i = 1; i < simpleNodes.length; i++) {
                const childNode = simpleNodes[i];
                if ((0, selector_1.matchTypeAndValue)(childNode, prefixType)) {
                    const chunk = simpleNodes.splice(i, simpleNodes.length - i);
                    simpleNodes.unshift(...chunk);
                    break;
                }
            }
        }
    });
    return compound;
}
function containsMatchInFirstChunk(prefixType, selectorNode) {
    let isMatch = false;
    (0, selector_1.walkSelector)(selectorNode, (node) => {
        if (node.type === `combinator`) {
            return selector_1.walkSelector.stopAll;
        }
        else if (node.type === 'pseudo_class') {
            // handle nested match :is(.mix)
            if (node.nodes) {
                for (const innerSelectorNode of node.nodes) {
                    if (containsMatchInFirstChunk(prefixType, innerSelectorNode)) {
                        isMatch = true;
                    }
                }
            }
            return selector_1.walkSelector.skipNested;
        }
        else if ((0, selector_1.matchTypeAndValue)(node, prefixType)) {
            isMatch = true;
            return selector_1.walkSelector.stopAll;
        }
        return;
    });
    return isMatch;
}
/** @deprecated internal for transformer  */
function findRule(root, selector, test = (statement) => statement.prop === `-st-extends`) {
    let found = null;
    root.walkRules(selector, (rule) => {
        const declarationIndex = rule.nodes ? rule.nodes.findIndex(test) : -1;
        const isSimplePerSelector = (0, selector_1.isSimpleSelector)(rule.selector);
        // This will assume that a selector that contains .a, .b:hover is simple! (for backward compatibility)
        const isSimple = isSimplePerSelector.reduce((acc, { isSimple }) => {
            return !isSimple ? false : acc;
        }, true);
        if (isSimple && !!~declarationIndex) {
            found = rule.nodes[declarationIndex];
        }
    });
    return found;
}
exports.findRule = findRule;
//# sourceMappingURL=rule.js.map