"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.transformCustomSelectorInline = exports.getCustomSelectorNames = exports.getCustomSelectorExpended = exports.getCustomSelectors = exports.getCustomSelector = exports.isScoped = exports.hooks = exports.CUSTOM_SELECTOR_RE = exports.diagnostics = void 0;
const plugable_record_1 = require("../helpers/plugable-record");
const feature_1 = require("./feature");
const custom_selector_1 = require("../helpers/custom-selector");
const selector_1 = require("../helpers/selector");
const postcss = __importStar(require("postcss"));
const css_selector_parser_1 = require("@tokey/css-selector-parser");
const diagnostics_1 = require("../diagnostics");
exports.diagnostics = {
    UNKNOWN_CUSTOM_SELECTOR: (0, diagnostics_1.createDiagnosticReporter)('18001', 'error', (selector) => `The selector '${selector}' is undefined`),
};
const dataKey = plugable_record_1.plugableRecord.key('st-custom-selector');
exports.CUSTOM_SELECTOR_RE = /:--[\w-]+/g;
// HOOKS
exports.hooks = (0, feature_1.createFeature)({
    metaInit({ meta }) {
        plugable_record_1.plugableRecord.set(meta.data, dataKey, {});
    },
    analyzeAtRule({ context, atRule, analyzeRule }) {
        const params = atRule.params.split(/\s/);
        const customSelector = params.shift();
        if (customSelector && customSelector.match(exports.CUSTOM_SELECTOR_RE)) {
            const selector = atRule.params.replace(customSelector, '').trim();
            const ast = (0, selector_1.parseSelectorWithCache)(selector, { clone: true });
            const isScoped = analyzeRule(postcss.rule({ selector, source: atRule.source }), {
                isScoped: false,
                originalNode: atRule,
            });
            const analyzed = plugable_record_1.plugableRecord.getUnsafe(context.meta.data, dataKey);
            const name = customSelector.slice(3);
            analyzed[name] = { selector, ast, isScoped, def: atRule };
        }
        else {
            // TODO: add warn there are two types one is not valid name and the other is empty name.
        }
    },
    analyzeDone(context) {
        const analyzed = plugable_record_1.plugableRecord.getUnsafe(context.meta.data, dataKey);
        const customSelectors = {};
        for (const [name, data] of Object.entries(analyzed)) {
            customSelectors[name] = data.ast;
        }
        const inlined = (0, custom_selector_1.transformInlineCustomSelectorMap)(customSelectors, (report) => {
            if (report.type === 'unknown' && analyzed[report.origin]) {
                const unknownSelector = `:--${report.unknown}`;
                context.diagnostics.report(exports.diagnostics.UNKNOWN_CUSTOM_SELECTOR(unknownSelector), {
                    node: analyzed[report.origin].def,
                    word: unknownSelector,
                });
            }
            else if (report.type === 'circular') {
                // ToDo: report error
            }
        });
        // cache inlined selector
        for (const [name, ast] of Object.entries(inlined)) {
            analyzed[name].ast = ast;
            analyzed[name].selector = (0, css_selector_parser_1.stringifySelectorAst)(ast);
        }
    },
    prepareAST({ context, node, toRemove }) {
        // called with experimentalSelectorInference=false
        // split selectors & remove definitions
        if (node.type === 'rule' && node.selector.match(exports.CUSTOM_SELECTOR_RE)) {
            node.selector = transformCustomSelectorInline(context.meta, node.selector, {
                diagnostics: context.diagnostics,
                node,
            });
        }
        else if (node.type === 'atrule' && node.name === 'custom-selector') {
            toRemove.push(node);
        }
    },
    transformSelectorNode({ context, selectorContext, node }) {
        const customSelector = node.value.startsWith('--') &&
            getCustomSelectorExpended(context.meta, node.value.slice(2));
        if (customSelector) {
            const mappedSelectorAst = (0, selector_1.parseSelectorWithCache)(customSelector, { clone: true });
            const mappedContext = selectorContext.createNestedContext(mappedSelectorAst);
            selectorContext.scopeSelectorAst(mappedContext);
            const inferredSelector = selectorContext.experimentalSelectorInference
                ? mappedContext.inferredMultipleSelectors
                : mappedContext.inferredSelector;
            selectorContext.setNextSelectorScope(inferredSelector, node); // doesn't add to the resolved elements
            if (selectorContext.transform) {
                selectorContext.transformIntoMultiSelector(node, mappedSelectorAst);
            }
        }
        return !!customSelector;
    },
    transformAtRuleNode({ atRule }) {
        if (atRule.name === 'custom-selector') {
            atRule.remove();
        }
    },
});
// API
function isScoped(meta, name) {
    const analyzed = plugable_record_1.plugableRecord.getUnsafe(meta.data, dataKey);
    return analyzed[name]?.isScoped;
}
exports.isScoped = isScoped;
function getCustomSelector(meta, name) {
    return plugable_record_1.plugableRecord.getUnsafe(meta.data, dataKey)[name]?.ast;
}
exports.getCustomSelector = getCustomSelector;
function getCustomSelectors(meta) {
    const analyzed = plugable_record_1.plugableRecord.getUnsafe(meta.data, dataKey);
    return Object.entries(analyzed).reduce((acc, [name, { ast }]) => {
        acc[name] = ast;
        return acc;
    }, {});
}
exports.getCustomSelectors = getCustomSelectors;
function getCustomSelectorExpended(meta, name) {
    const analyzed = plugable_record_1.plugableRecord.getUnsafe(meta.data, dataKey);
    return analyzed[name]?.selector;
}
exports.getCustomSelectorExpended = getCustomSelectorExpended;
function getCustomSelectorNames(meta) {
    const analyzed = plugable_record_1.plugableRecord.getUnsafe(meta.data, dataKey);
    return Object.keys(analyzed).map((name) => `:--${name}`);
}
exports.getCustomSelectorNames = getCustomSelectorNames;
function transformCustomSelectorInline(meta, selector, options = {}) {
    const ast = (0, selector_1.parseSelectorWithCache)(selector, { clone: true });
    const analyzed = plugable_record_1.plugableRecord.getUnsafe(meta.data, dataKey);
    const inlined = (0, custom_selector_1.transformInlineCustomSelectors)(ast, (name) => analyzed[name]?.ast, (report) => {
        if (options.diagnostics && options.node) {
            const unknownSelector = `:--${report.unknown}`;
            options.diagnostics.report(exports.diagnostics.UNKNOWN_CUSTOM_SELECTOR(unknownSelector), {
                node: options.node,
                word: unknownSelector,
            });
        }
    });
    return (0, css_selector_parser_1.stringifySelectorAst)(inlined);
}
exports.transformCustomSelectorInline = transformCustomSelectorInline;
//# sourceMappingURL=st-custom-selector.js.map