"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MATCH_COMPLETIONS = exports.BASE_COMPLETIONS = void 0;
exports.completer = completer;
exports.initNewAutocompleter = initNewAutocompleter;
const shell_api_1 = require("@mongosh/shell-api");
const semver_1 = __importDefault(require("semver"));
const mongodb_constants_1 = require("@mongodb-js/mongodb-constants");
const direct_command_completer_1 = require("./direct-command-completer");
exports.BASE_COMPLETIONS = [].concat(mongodb_constants_1.EXPRESSION_OPERATORS, mongodb_constants_1.CONVERSION_OPERATORS, mongodb_constants_1.BSON_TYPES, mongodb_constants_1.STAGE_OPERATORS);
exports.MATCH_COMPLETIONS = [].concat(mongodb_constants_1.QUERY_OPERATORS, mongodb_constants_1.BSON_TYPES);
const DB_AGGREGATE_COMPLETIONS = mongodb_constants_1.STAGE_OPERATORS.filter(({ namespaces }) => {
    return namespaces.length === 1 && namespaces[0] === mongodb_constants_1.DATABASE;
});
const PROJECT = '$project';
const GROUP = '$group';
async function completer(params, line) {
    const SHELL_COMPLETIONS = shell_api_1.signatures.ShellApi
        .attributes;
    const COLL_COMPLETIONS = shell_api_1.signatures.Collection
        .attributes;
    const DB_COMPLETIONS = shell_api_1.signatures.Database
        .attributes;
    const AGG_CURSOR_COMPLETIONS = shell_api_1.signatures.AggregationCursor
        .attributes;
    const COLL_CURSOR_COMPLETIONS = shell_api_1.signatures.Cursor
        .attributes;
    const RS_COMPLETIONS = shell_api_1.signatures.ReplicaSet
        .attributes;
    const CONFIG_COMPLETIONS = shell_api_1.signatures.ShellConfig
        .attributes;
    const SHARD_COMPLETE = shell_api_1.signatures.Shard
        .attributes;
    const SP_COMPLETIONS = shell_api_1.signatures.Streams
        .attributes;
    const SP_INSTANCE_COMPLETIONS = shell_api_1.signatures.StreamProcessor
        .attributes;
    const splitLineWhitespace = line.split(/(?<!\S)(?=\S)/);
    const command = splitLineWhitespace[0].trim();
    if (SHELL_COMPLETIONS[command]?.isDirectShellCommand) {
        const completer = SHELL_COMPLETIONS[command].shellCommandCompleter;
        if (completer) {
            if (splitLineWhitespace.length === 1) {
                if (splitLineWhitespace[0].trimEnd() === splitLineWhitespace[0]) {
                    splitLineWhitespace[0] += ' ';
                }
                splitLineWhitespace.push('');
            }
            const hits = (await completer(params, splitLineWhitespace.map((item) => item.trim()))) || [];
            const fullLineHits = hits.map((hit) => [...splitLineWhitespace.slice(0, -1), hit].join(''));
            return [fullLineHits, line, 'exclusive'];
        }
        return [[line], line, 'exclusive'];
    }
    const splitLine = line.split('.');
    const firstLineEl = splitLine[0];
    const elToComplete = splitLine[splitLine.length - 1];
    if (splitLine.length <= 1) {
        const hits = filterShellAPI(params, SHELL_COMPLETIONS, elToComplete);
        return [hits.length ? hits : [], line];
    }
    else if (/\bdb\b/.exec(firstLineEl) && splitLine.length === 2) {
        if (/aggregate\s*\(\s*\[\s*\{\s*/.exec(elToComplete)) {
            const splitQuery = line.split('{');
            const prefix = splitQuery.pop()?.trim() || '';
            const command = prefix
                ? line.split(prefix).shift()
                : line;
            const suggestFirstStage = splitQuery.length <= 2;
            const expressions = suggestFirstStage
                ?
                    DB_AGGREGATE_COMPLETIONS
                : [...exports.BASE_COMPLETIONS, ...getStageAccumulators(params, elToComplete)];
            const hits = filterQueries(params, expressions, prefix, command);
            return [hits.length ? hits : [], line];
        }
        const hits = filterShellAPI(params, DB_COMPLETIONS, elToComplete, splitLine);
        const colls = await params.getCollectionCompletionsForCurrentDb(elToComplete.trim());
        hits.push(...colls.map((coll) => `${splitLine[0]}.${coll}`));
        return [hits.length ? hits : [], line];
    }
    else if (/\bdb\b/.exec(firstLineEl) && splitLine.length > 2) {
        if (!/^\s*\w+\s*$/.exec(splitLine[1]) &&
            !/\bgetCollection\b/.exec(splitLine[1])) {
            return [[], line];
        }
        if (splitLine.length > 3) {
            if (/\baggregate\b/.exec(splitLine[2])) {
                const hits = filterShellAPI(params, AGG_CURSOR_COMPLETIONS, elToComplete, splitLine);
                return [hits.length ? hits : [], line];
            }
            else if (/\bfind\b/.exec(splitLine[2])) {
                const hits = filterShellAPI(params, COLL_CURSOR_COMPLETIONS, elToComplete, splitLine);
                return [hits.length ? hits : [], line];
            }
            return [[], line];
        }
        if (splitLine[2].includes('([') || splitLine[2].includes('({')) {
            let expressions;
            if (/\baggregate\b/.exec(splitLine[2])) {
                expressions = [
                    ...exports.BASE_COMPLETIONS,
                    ...getStageAccumulators(params, elToComplete),
                ];
            }
            else {
                expressions = exports.MATCH_COMPLETIONS;
            }
            const splitQuery = line.split('{');
            const prefix = splitQuery.pop()?.trim();
            const command = prefix
                ? line.split(prefix).shift()
                : line;
            const hits = filterQueries(params, expressions, prefix || '', command);
            return [hits.length ? hits : [], line];
        }
        const hits = filterShellAPI(params, COLL_COMPLETIONS, elToComplete, splitLine);
        return [hits.length ? hits : [], line];
    }
    else if (/\bsh\b/.exec(firstLineEl) && splitLine.length === 2) {
        const hits = filterShellAPI(params, SHARD_COMPLETE, elToComplete, splitLine);
        return [hits.length ? hits : [], line];
    }
    else if (/\brs\b/.exec(firstLineEl) && splitLine.length === 2) {
        const hits = filterShellAPI(params, RS_COMPLETIONS, elToComplete, splitLine);
        return [hits.length ? hits : [], line];
    }
    else if (/\bconfig\b/.exec(firstLineEl) && splitLine.length === 2) {
        const hits = filterShellAPI(params, CONFIG_COMPLETIONS, elToComplete, splitLine);
        return [hits.length ? hits : [], line];
    }
    else if (/\bsp\b/.exec(firstLineEl)) {
        let expressions;
        if (splitLine.length === 2) {
            expressions = SP_COMPLETIONS;
        }
        else if (splitLine.length === 3) {
            expressions = SP_INSTANCE_COMPLETIONS;
        }
        const hits = expressions &&
            filterShellAPI(params, expressions, elToComplete, splitLine);
        return [hits?.length ? hits : [], line];
    }
    return [[], line];
}
function satisfiesVersion(v1, v2) {
    const isGTECheck = /^\d+?\.\d+?\.\d+?$/.test(v2);
    return semver_1.default.satisfies(v1, isGTECheck ? `>=${v2}` : v2, {
        includePrerelease: true,
    });
}
function isAcceptable(params, entry, versionKey) {
    const connectionInfo = params.connectionInfo();
    const apiVersionInfo = params.apiVersionInfo();
    let isAcceptableVersion;
    if (apiVersionInfo?.strict && entry.apiVersions) {
        isAcceptableVersion = entry.apiVersions.includes(+apiVersionInfo.version);
    }
    else {
        isAcceptableVersion =
            !entry[versionKey] ||
                !connectionInfo?.server_version ||
                satisfiesVersion(connectionInfo.server_version, entry[versionKey]);
    }
    const isAcceptableEnvironment = !entry.env ||
        !connectionInfo ||
        (connectionInfo.is_data_federation
            ? entry.env.includes(mongodb_constants_1.ADL)
            : connectionInfo.is_atlas || connectionInfo.is_local_atlas
                ? entry.env.includes(mongodb_constants_1.ATLAS)
                : entry.env.includes(mongodb_constants_1.ON_PREM));
    return isAcceptableVersion && isAcceptableEnvironment;
}
function getStageAccumulators(params, stage) {
    if (stage.includes(PROJECT)) {
        return mongodb_constants_1.ACCUMULATORS.filter((acc) => {
            return isAcceptable(params, acc, 'projectVersion');
        });
    }
    else if (stage.includes(GROUP)) {
        return mongodb_constants_1.ACCUMULATORS;
    }
    return [];
}
function filterQueries(params, completions, prefix, split) {
    const hits = completions.filter((e) => {
        return (e.name && e.name.startsWith(prefix) && isAcceptable(params, e, 'version'));
    });
    return hits.map((h) => `${split}${h.name}`);
}
function filterShellAPI(params, completions, prefix, split) {
    const hits = Object.keys(completions).filter((c) => {
        if (!c.toLowerCase().startsWith(prefix.toLowerCase()))
            return false;
        if (completions[c].deprecated)
            return false;
        const apiVersionInfo = params.apiVersionInfo();
        let isAcceptableVersion;
        let acceptableApiVersions;
        if (apiVersionInfo?.strict &&
            (acceptableApiVersions = completions[c].apiVersions)) {
            isAcceptableVersion =
                +apiVersionInfo.version >= acceptableApiVersions[0] &&
                    +apiVersionInfo.version <= acceptableApiVersions[1];
        }
        else {
            const serverVersion = params.connectionInfo()?.server_version;
            const acceptableVersions = completions[c].serverVersions;
            isAcceptableVersion =
                !acceptableVersions ||
                    !serverVersion ||
                    (semver_1.default.gte(serverVersion, acceptableVersions[0]) &&
                        semver_1.default.lte(serverVersion, acceptableVersions[1]));
        }
        const acceptableTopologies = completions[c].topologies;
        const topology = params.topology();
        const isAcceptableTopology = !acceptableTopologies ||
            !topology ||
            acceptableTopologies.includes(topology);
        return isAcceptableVersion && isAcceptableTopology;
    });
    if (split) {
        return hits.map((h) => `${split.slice(0, -1).join('.')}.${h}`);
    }
    return hits;
}
function transformAutocompleteResults(line, results) {
    return [results.map((result) => result.result), line];
}
async function initNewAutocompleter(instanceState) {
    const { MongoDBAutocompleter } = await import('@mongodb-js/mongodb-ts-autocomplete');
    const autocompletionContext = instanceState.getAutocompletionContext();
    const mongoDBCompleter = new MongoDBAutocompleter({
        context: autocompletionContext,
    });
    return async (text) => {
        const directResults = await (0, direct_command_completer_1.directCommandCompleter)(autocompletionContext, text);
        if (directResults.length) {
            return [directResults, text, 'exclusive'];
        }
        const results = await mongoDBCompleter.autocomplete(text);
        const transformed = transformAutocompleteResults(text, results);
        return transformed;
    };
}
//# sourceMappingURL=index.js.map