"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.setMetaNamespace = exports.defaultProcessNamespace = exports.parseNamespace = exports.hooks = exports.diagnostics = void 0;
const path_1 = __importDefault(require("path"));
const feature_1 = require("./feature");
const plugable_record_1 = require("../helpers/plugable-record");
const string_1 = require("../helpers/string");
const string_2 = require("../helpers/string");
const postcss_value_parser_1 = __importDefault(require("postcss-value-parser"));
const murmurhash_1 = require("../murmurhash");
const diagnostics_1 = require("../diagnostics");
exports.diagnostics = {
    INVALID_NAMESPACE_DEF: (0, diagnostics_1.createDiagnosticReporter)('11007', 'error', () => 'invalid @st-namespace'),
    EMPTY_NAMESPACE_DEF: (0, diagnostics_1.createDiagnosticReporter)('11008', 'error', () => '@st-namespace must contain at least one character or digit'),
    EXTRA_DEFINITION: (0, diagnostics_1.createDiagnosticReporter)('11012', 'error', () => '@st-namespace must contain a single string definition'),
    INVALID_NAMESPACE_VALUE: (0, diagnostics_1.createDiagnosticReporter)('11013', 'error', () => '@st-namespace must contain only letters, numbers or dashes'),
    INVALID_NAMESPACE_REFERENCE: (0, diagnostics_1.createDiagnosticReporter)('11010', 'error', () => 'st-namespace-reference dose not have any value'),
    NATIVE_OVERRIDE_DEPRECATION: (0, diagnostics_1.createDiagnosticReporter)('11014', 'info', () => '@namespace will stop working in version 6, use @st-namespace instead'),
};
const dataKey = plugable_record_1.plugableRecord.key('namespace');
// HOOKS
exports.hooks = (0, feature_1.createFeature)({
    metaInit({ meta }) {
        plugable_record_1.plugableRecord.set(meta.data, dataKey, {
            namespaces: [],
            usedNativeNamespace: [],
            usedNativeNamespaceNodes: [],
            foundStNamespace: false,
        });
    },
    analyzeAtRule({ context, atRule }) {
        const isSTNamespace = atRule.name === 'st-namespace';
        const isNamespace = atRule.name === 'namespace';
        if (!isSTNamespace && !isNamespace) {
            return;
        }
        const data = plugable_record_1.plugableRecord.getUnsafe(context.meta.data, dataKey);
        if (data.foundStNamespace && isNamespace) {
            // ignore @namespace once @st-namespace was found
            return;
        }
        const diag = isSTNamespace ? context.diagnostics : undefined;
        const match = parseNamespace(atRule, diag);
        if (match) {
            data.namespaces.push(match);
            if (isNamespace) {
                data.usedNativeNamespace.push(atRule.params);
                data.usedNativeNamespaceNodes.push(atRule);
            }
            else {
                // clear @namespace matches once @st-namespace if found
                data.usedNativeNamespace.length = 0;
                data.usedNativeNamespaceNodes.length = 0;
                // mark to prevent any further @namespace collection
                data.foundStNamespace = true;
            }
        }
    },
    analyzeDone(context) {
        const { usedNativeNamespaceNodes } = plugable_record_1.plugableRecord.getUnsafe(context.meta.data, dataKey);
        for (const node of usedNativeNamespaceNodes) {
            context.diagnostics.report(exports.diagnostics.NATIVE_OVERRIDE_DEPRECATION(), {
                node,
            });
        }
    },
    prepareAST({ context, node, toRemove }) {
        // remove @st-namespace or @namespace that was used as @st-namespace
        const { usedNativeNamespace } = plugable_record_1.plugableRecord.getUnsafe(context.meta.data, dataKey);
        if (node.type === 'atrule' &&
            (node.name === 'st-namespace' ||
                (node.name === 'namespace' && usedNativeNamespace.includes(node.params)))) {
            toRemove.push(node);
        }
    },
});
// API
function parseNamespace(node, diag) {
    const { nodes } = (0, postcss_value_parser_1.default)(node.params);
    if (!nodes.length) {
        // empty params (not even empty quotes)
        diag?.report(exports.diagnostics.EMPTY_NAMESPACE_DEF(), { node });
        return;
    }
    let isInvalid = false;
    let namespace = undefined;
    for (const valueNode of nodes) {
        switch (valueNode.type) {
            case 'string': {
                if (namespace === undefined) {
                    // first namespace
                    if (!isInvalid) {
                        namespace = (0, string_2.stripQuotation)(valueNode.value);
                    }
                }
                else {
                    // extra definition - mark as invalid and clear namespace
                    diag?.report(exports.diagnostics.EXTRA_DEFINITION(), {
                        node,
                        word: postcss_value_parser_1.default.stringify(valueNode),
                    });
                    isInvalid = true;
                    namespace = undefined;
                }
                break;
            }
            case 'comment':
            case 'space':
                // do nothing
                break;
            default: {
                // invalid definition - mark as invalid and clear namespace
                diag?.report(exports.diagnostics.EXTRA_DEFINITION(), {
                    node,
                    word: postcss_value_parser_1.default.stringify(valueNode),
                });
                isInvalid = true;
                namespace = undefined;
            }
        }
    }
    if (namespace === undefined) {
        // no namespace found
        diag?.report(exports.diagnostics.INVALID_NAMESPACE_DEF(), {
            node,
        });
        return;
    }
    if (namespace !== undefined && namespace.trim() === '') {
        // empty namespace found
        diag?.report(exports.diagnostics.EMPTY_NAMESPACE_DEF(), {
            node,
        });
        return;
    }
    // check namespace is a valid ident start with no conflicts with stylable namespacing
    const transformedNamespace = (0, string_1.string2varname)(namespace);
    if (namespace !== transformedNamespace) {
        // invalid namespace found
        diag?.report(exports.diagnostics.INVALID_NAMESPACE_VALUE(), {
            node,
            word: namespace,
        });
        return;
    }
    return namespace;
}
exports.parseNamespace = parseNamespace;
function defaultProcessNamespace(namespace, origin, _source) {
    return namespace + (0, murmurhash_1.murmurhash3_32_gc)(origin); // .toString(36);
}
exports.defaultProcessNamespace = defaultProcessNamespace;
function setMetaNamespace(context, resolveNamespace) {
    const meta = context.meta;
    // resolve namespace
    const { namespaces } = plugable_record_1.plugableRecord.getUnsafe(meta.data, dataKey);
    const namespace = namespaces[namespaces.length - 1] || (0, string_1.filename2varname)(path_1.default.basename(meta.source)) || 's';
    // resolve path origin
    let pathToSource;
    let length = meta.sourceAst.nodes.length;
    while (length--) {
        const node = meta.sourceAst.nodes[length];
        if (node.type === 'comment' && node.text.includes('st-namespace-reference')) {
            const i = node.text.indexOf('=');
            if (i === -1) {
                context.diagnostics.report(exports.diagnostics.INVALID_NAMESPACE_REFERENCE(), {
                    node,
                });
            }
            else {
                pathToSource = (0, string_2.stripQuotation)(node.text.slice(i + 1));
            }
            break;
        }
    }
    // generate final namespace
    meta.namespace = resolveNamespace(namespace, pathToSource ? path_1.default.resolve(path_1.default.dirname(meta.source), pathToSource) : meta.source, meta.source);
}
exports.setMetaNamespace = setMetaNamespace;
//# sourceMappingURL=st-namespace.js.map