"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;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDefinition = exports.getAll = exports.get = exports.hooks = exports.diagnostics = void 0;
const feature_1 = require("./feature");
const STSymbol = __importStar(require("./st-symbol"));
const STImport = __importStar(require("./st-import"));
const CSSCustomProperty = __importStar(require("./css-custom-property"));
const diagnostics_1 = require("../diagnostics");
const plugable_record_1 = require("../helpers/plugable-record");
const namespace_1 = require("../helpers/namespace");
const global_1 = require("../helpers/global");
const postcss_value_parser_1 = __importDefault(require("postcss-value-parser"));
exports.diagnostics = {
    UNEXPECTED_DECL_VALUE: (0, diagnostics_1.createDiagnosticReporter)('20001', 'error', (value) => `unexpected value: ${value}`),
    UNKNOWN_DECL_TYPE: (0, diagnostics_1.createDiagnosticReporter)('20002', 'error', (value) => `unknown container type: ${value}`),
    MISSING_DECL_TYPE: (0, diagnostics_1.createDiagnosticReporter)('20003', 'error', () => `missing container shorthand type`),
    INVALID_CONTAINER_NAME: (0, diagnostics_1.createDiagnosticReporter)('20004', 'error', (value) => `invalid container name: ${value}`),
    UNRESOLVED_CONTAINER_NAME: (0, diagnostics_1.createDiagnosticReporter)('20005', 'error', (value) => `unresolved container name: ${value}`),
    UNKNOWN_IMPORTED_CONTAINER: (0, diagnostics_1.createDiagnosticReporter)('20006', 'error', (name, path) => `cannot resolve imported container name "${name}" from stylesheet "${path}"`),
    MISSING_CONTAINER_NAME_INSIDE_GLOBAL: (0, diagnostics_1.createDiagnosticReporter)('20007', 'warning', () => `Missing container name inside "${global_1.GLOBAL_FUNC}()"`),
    UNEXPECTED_DEFINITION: (0, diagnostics_1.createDiagnosticReporter)('20008', 'error', (def) => `Unexpected value in container definition: "${def}""`),
};
const dataKey = plugable_record_1.plugableRecord.key('container');
// HOOKS
STImport.ImportTypeHook.set(`container`, (context, localName, importName, importDef) => {
    addContainer({
        context,
        name: localName,
        importName,
        ast: importDef.rule,
        global: false,
        importDef,
        forceDefinition: true,
    });
});
exports.hooks = (0, feature_1.createFeature)({
    metaInit({ meta }) {
        plugable_record_1.plugableRecord.set(meta.data, dataKey, {
            'container-name': {},
            container: {},
            definitions: {},
        });
    },
    analyzeDeclaration({ context, decl }) {
        const prop = decl.prop.toLowerCase();
        if (prop !== 'container-name' && prop !== 'container') {
            return;
        }
        const analyzed = plugable_record_1.plugableRecord.getUnsafe(context.meta.data, dataKey);
        const bucket = analyzed[prop];
        const value = decl.value;
        if (bucket[value]) {
            return;
        }
        const parsed = (bucket[value] = parseContainerDecl(decl, context));
        for (const { name, global } of parsed.containers) {
            addContainer({
                context,
                ast: decl,
                name,
                importName: name,
                global,
                forceDefinition: false,
            });
        }
    },
    analyzeAtRule({ context, atRule }) {
        const ast = (0, postcss_value_parser_1.default)(atRule.params).nodes;
        let name = '';
        let global = false;
        let searchForContainerName = true;
        let searchForLogicalOp = false;
        for (const node of ast) {
            if (node.type === 'comment' || node.type === 'space') {
                // do nothing
                continue;
            }
            else if (searchForContainerName && node.type === 'word') {
                searchForContainerName = false;
                searchForLogicalOp = true;
                name = node.value;
            }
            else if (searchForContainerName &&
                node.type === 'function' &&
                node.value === global_1.GLOBAL_FUNC) {
                searchForContainerName = false;
                searchForLogicalOp = true;
                name = (0, global_1.globalValueFromFunctionNode)(node) || '';
                global = true;
            }
            else if (node.type === 'function' && node.value === 'style') {
                searchForContainerName = false;
                searchForLogicalOp = true;
                // check for custom properties
                for (const queryNode of node.nodes) {
                    if (queryNode.type === 'word' && queryNode.value.startsWith('--')) {
                        CSSCustomProperty.addCSSProperty({
                            context,
                            node: atRule,
                            name: queryNode.value,
                            global: context.meta.type === 'css',
                            final: false,
                        });
                    }
                }
            }
            else if (node.type !== 'function' &&
                (!searchForLogicalOp || (node.type === 'word' && !logicalOpNames[node.value]))) {
                const def = postcss_value_parser_1.default.stringify(node);
                context.diagnostics.report(exports.diagnostics.UNEXPECTED_DEFINITION(def), {
                    node: atRule,
                    word: def,
                });
                break;
            }
        }
        if (name && !atRule.nodes) {
            // treat @container with no body as definition
            if (invalidContainerNames[name]) {
                context.diagnostics.report(exports.diagnostics.INVALID_CONTAINER_NAME(name), {
                    node: atRule,
                    word: name,
                });
            }
            addContainer({
                context,
                ast: atRule,
                name,
                importName: name,
                global,
                forceDefinition: true,
            });
        }
    },
    transformResolve({ context }) {
        const symbols = STSymbol.getAllByType(context.meta, `container`);
        const resolved = {
            record: {},
            locals: new Set(),
        };
        const resolvedSymbols = context.getResolvedSymbols(context.meta);
        for (const [name, symbol] of Object.entries(symbols)) {
            const res = resolvedSymbols.container[name];
            if (res) {
                resolved.record[name] = res;
                if (res.meta === context.meta) {
                    resolved.locals.add(name);
                }
            }
            else if (symbol.import) {
                context.diagnostics.report(exports.diagnostics.UNKNOWN_IMPORTED_CONTAINER(symbol.name, symbol.import.request), {
                    node: symbol.import.rule,
                    word: symbol.name,
                });
            }
        }
        return resolved;
    },
    transformDeclaration({ context, decl, resolved }) {
        const prop = decl.prop.toLowerCase();
        if (prop !== 'container-name' && prop !== 'container') {
            return;
        }
        const analyzed = plugable_record_1.plugableRecord.getUnsafe(context.meta.data, dataKey);
        const bucket = analyzed[prop];
        const value = decl.value;
        decl.value = bucket[value].transformNames((name) => {
            const resolve = resolved.record[name];
            return resolve ? getTransformedName(resolved.record[name]) : name;
        });
    },
    transformAtRuleNode({ context, atRule, resolved }) {
        if (!atRule.nodes) {
            // remove definition only @container
            atRule.remove();
            return;
        }
        const ast = (0, postcss_value_parser_1.default)(atRule.params).nodes;
        let changed = false;
        let searchForContainerName = true;
        search: for (const node of ast) {
            if (node.type === 'comment' || node.type === 'space') {
                // do nothing
            }
            else if (node.type === 'word' && searchForContainerName) {
                const resolve = resolved.record[node.value];
                if (resolve) {
                    node.value = getTransformedName(resolve);
                    changed = true;
                }
                else {
                    context.diagnostics.report(exports.diagnostics.UNRESOLVED_CONTAINER_NAME(node.value), {
                        node: atRule,
                        word: node.value,
                    });
                }
                break search;
            }
            else if (node.type === 'function' &&
                node.value === global_1.GLOBAL_FUNC &&
                searchForContainerName) {
                const globalName = (0, global_1.globalValueFromFunctionNode)(node) || '';
                if (globalName) {
                    changed = true;
                    const wordNode = node;
                    wordNode.type = 'word';
                    wordNode.value = globalName;
                }
            }
            else if (node.type === 'function' && node.value === 'style') {
                // check for custom properties
                searchForContainerName = false;
                for (const queryNode of node.nodes) {
                    if (queryNode.type === 'word' && queryNode.value.startsWith('--')) {
                        changed = true;
                        CSSCustomProperty.transformPropertyIdent(context.meta, queryNode, context.getResolvedSymbols);
                    }
                }
            }
        }
        if (changed) {
            atRule.params = postcss_value_parser_1.default.stringify(ast);
        }
        atRule.params = context.evaluator.evaluateValue(context, {
            value: atRule.params,
            meta: context.meta,
            node: atRule,
            initialNode: atRule,
        }).outputValue;
    },
    transformJSExports({ exports, resolved }) {
        for (const name of resolved.locals) {
            exports.containers[name] = getTransformedName(resolved.record[name]);
        }
    },
});
const invalidContainerNames = {
    none: true,
    and: true,
    not: true,
    or: true,
};
const logicalOpNames = {
    and: true,
    not: true,
    or: true,
};
function parseContainerDecl(decl, context) {
    const { prop, value } = decl;
    const containers = [];
    const namedNodeRefs = {};
    const ast = (0, postcss_value_parser_1.default)(value).nodes;
    let noneFound = false;
    const checkNextName = (node) => {
        const { type, value } = node;
        if (type === 'comment' || type === 'space') {
            // do nothing
        }
        else if (type === 'word' || (type === 'function' && value === global_1.GLOBAL_FUNC)) {
            const global = type === 'function';
            const name = global ? (0, global_1.globalValueFromFunctionNode)(node) || '' : node.value;
            if (global && !name) {
                context.diagnostics.report(exports.diagnostics.MISSING_CONTAINER_NAME_INSIDE_GLOBAL(), {
                    node: decl,
                });
            }
            if (name === 'none') {
                noneFound = true;
                return;
            }
            if (!global) {
                containers.push({ name, global });
                namedNodeRefs[name] ?? (namedNodeRefs[name] = []);
                namedNodeRefs[name].push(node);
            }
            else {
                // mutate to word - this is safe since this node is not exposed
                node.type = 'word';
                node.value = name;
            }
            if (invalidContainerNames[name]) {
                context.diagnostics.report(exports.diagnostics.INVALID_CONTAINER_NAME(name), {
                    node: decl,
                    word: name,
                });
            }
        }
        else {
            const word = postcss_value_parser_1.default.stringify(node);
            context.diagnostics.report(exports.diagnostics.UNEXPECTED_DECL_VALUE(word), {
                node: decl,
                word,
            });
            return false;
        }
        return true;
    };
    if (prop.toLowerCase() === 'container-name') {
        for (const node of ast) {
            const continueParse = checkNextName(node);
            if (!continueParse) {
                break;
            }
        }
    }
    else {
        let nextExpected = 'name';
        for (const node of ast) {
            const { type, value } = node;
            if (type === 'comment' || type === 'space') {
                // do nothing
            }
            else if (nextExpected === 'name') {
                if (type === 'div' && value === '/') {
                    nextExpected = 'type';
                }
                else {
                    const continueParse = checkNextName(node);
                    if (!continueParse) {
                        break;
                    }
                }
            }
            else if (type === 'word' && nextExpected === 'type') {
                if (value !== 'normal' && value !== 'size' && value !== 'inline-size') {
                    context.diagnostics.report(exports.diagnostics.UNKNOWN_DECL_TYPE(value), {
                        node: decl,
                        word: value,
                    });
                }
                nextExpected = '';
            }
            else {
                const word = postcss_value_parser_1.default.stringify(node);
                context.diagnostics.report(exports.diagnostics.UNEXPECTED_DECL_VALUE(word), {
                    node: decl,
                    word,
                });
            }
        }
        if (nextExpected === 'type') {
            context.diagnostics.report(exports.diagnostics.MISSING_DECL_TYPE(), {
                node: decl,
            });
        }
    }
    if (containers.length > 0 && noneFound) {
        context.diagnostics.report(exports.diagnostics.INVALID_CONTAINER_NAME('none'), {
            node: decl,
            word: 'none',
        });
    }
    return {
        containers,
        transformNames(getTransformedName) {
            for (const [name, nodes] of Object.entries(namedNodeRefs)) {
                const transformedName = getTransformedName(name);
                for (const modifiedNode of nodes) {
                    if (modifiedNode.type === 'function') {
                        // mutate to word - this is safe since this node is not exposed
                        modifiedNode.type = 'word';
                    }
                    modifiedNode.value = transformedName;
                }
            }
            return postcss_value_parser_1.default.stringify(ast);
        },
    };
}
// API
function get(meta, name) {
    return STSymbol.get(meta, name, `container`);
}
exports.get = get;
function getAll(meta) {
    return STSymbol.getAllByType(meta, `container`);
}
exports.getAll = getAll;
function getDefinition(meta, name) {
    const { definitions } = plugable_record_1.plugableRecord.getUnsafe(meta.data, dataKey);
    return definitions[name];
}
exports.getDefinition = getDefinition;
function getTransformedName({ symbol, meta }) {
    return symbol.global ? symbol.alias : (0, namespace_1.namespace)(symbol.alias, meta.namespace);
}
function addContainer({ context, name, importName, ast, global, importDef, forceDefinition, }) {
    const { definitions } = plugable_record_1.plugableRecord.getUnsafe(context.meta.data, dataKey);
    const definedSymbol = STSymbol.get(context.meta, name, 'container');
    const isFirst = !definedSymbol;
    if (forceDefinition || isFirst) {
        if (context.meta.type !== 'stylable') {
            global = true;
        }
        definitions[name] = ast;
        STSymbol.addSymbol({
            context,
            node: ast,
            localName: name,
            symbol: {
                _kind: 'container',
                name: importName,
                alias: name,
                global,
                import: importDef,
            },
            safeRedeclare: false,
        });
    }
}
//# sourceMappingURL=css-contains.js.map