"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ALL_CONSTANTS = void 0;
exports.createConstantFilter = createConstantFilter;
exports.wrapField = wrapField;
exports.getFilteredCompletions = getFilteredCompletions;
const semver_1 = require("semver");
const accumulators_1 = require("./accumulators");
const bson_type_aliases_1 = require("./bson-type-aliases");
const bson_types_1 = require("./bson-types");
const conversion_operators_1 = require("./conversion-operators");
const expression_operators_1 = require("./expression-operators");
const json_schema_1 = require("./json-schema");
const query_operators_1 = require("./query-operators");
const stage_operators_1 = require("./stage-operators");
const system_variables_1 = require("./system-variables");
exports.ALL_CONSTANTS = [
    ...accumulators_1.ACCUMULATORS,
    ...bson_types_1.BSON_TYPES,
    ...bson_type_aliases_1.BSON_TYPE_ALIASES,
    ...conversion_operators_1.CONVERSION_OPERATORS,
    ...expression_operators_1.EXPRESSION_OPERATORS,
    ...json_schema_1.JSON_SCHEMA,
    ...query_operators_1.QUERY_OPERATORS,
    ...stage_operators_1.STAGE_OPERATORS,
    ...system_variables_1.SYSTEM_VARIABLES,
];
const DEFAULT_SERVER_VERSION = '999.999.999';
function matchesMeta(filter, meta) {
    const metaParts = meta.split(':');
    return filter.some((metaFilter) => {
        const filterParts = metaFilter.split(':');
        return (filterParts.length === metaParts.length &&
            filterParts.every((part, index) => {
                return part === '*' || part === metaParts[index];
            }));
    });
}
function isIn(val, set) {
    if (typeof val === 'undefined' || typeof set === 'undefined') {
        return true;
    }
    val = Array.isArray(val) ? val : [val];
    return val.some((v) => set.includes(v));
}
function satisfiesVersion(v1, v2) {
    const isGTECheck = /^\d+\.\d+\.\d+$/.test(v2);
    return (0, semver_1.satisfies)(v1, isGTECheck ? `>=${v2}` : v2);
}
function createConstantFilter({ meta: filterMeta, serverVersion = DEFAULT_SERVER_VERSION, stage: filterStage = {}, } = {}) {
    const currentServerVersion = /^(?<version>\d+\.\d+\.\d+)/.exec(serverVersion)?.groups?.version ??
        DEFAULT_SERVER_VERSION;
    return ({ version: minServerVersion, meta, env, namespaces, apiVersions, }) => {
        return (satisfiesVersion(currentServerVersion, minServerVersion) &&
            isIn(filterStage.env, env) &&
            isIn(filterStage.namespace, namespaces) &&
            isIn(filterStage.apiVersion, apiVersions) &&
            (!filterMeta || matchesMeta(filterMeta, meta)));
    };
}
function isValidIdentifier(identifier) {
    if (/[.\s"'()[\];={}:]/.test(identifier)) {
        return false;
    }
    try {
        new Function(`"use strict";let _ = { ${identifier}: 0 };`);
        return true;
    }
    catch {
        return false;
    }
}
function wrapField(field, force = false) {
    return force || !isValidIdentifier(field)
        ? `"${field.replace(/["\\]/g, '\\$&')}"`
        : field;
}
function normalizeField(field) {
    return typeof field === 'string'
        ? { value: field }
        : {
            value: field.name,
            description: field.description,
        };
}
function getFilteredCompletions(options = {}, constants = exports.ALL_CONSTANTS) {
    const { serverVersion = DEFAULT_SERVER_VERSION, fields = [], meta, stage, } = options;
    const completionsFilter = createConstantFilter({
        serverVersion,
        meta,
        stage,
    });
    const completionsWithFields = constants.concat(fields.flatMap((field) => {
        const { value, description } = normalizeField(field);
        return [
            {
                value: value,
                meta: 'field:identifier',
                version: '0.0.0',
                description,
            },
            {
                value: `$${value}`,
                meta: 'field:reference',
                version: '0.0.0',
                description,
            },
        ];
    }));
    return completionsWithFields.filter((completion) => {
        return completionsFilter(completion);
    });
}
//# sourceMappingURL=filter.js.map