"use strict";
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
    var useValue = arguments.length > 2;
    for (var i = 0; i < initializers.length; i++) {
        value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
    }
    return useValue ? value : void 0;
};
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
    function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
    var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
    var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
    var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
    var _, done = false;
    for (var i = decorators.length - 1; i >= 0; i--) {
        var context = {};
        for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
        for (var p in contextIn.access) context.access[p] = contextIn.access[p];
        context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
        var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
        if (kind === "accessor") {
            if (result === void 0) continue;
            if (result === null || typeof result !== "object") throw new TypeError("Object expected");
            if (_ = accept(result.get)) descriptor.get = _;
            if (_ = accept(result.set)) descriptor.set = _;
            if (_ = accept(result.init)) initializers.unshift(_);
        }
        else if (_ = accept(result)) {
            if (kind === "field") initializers.unshift(_);
            else descriptor[key] = _;
        }
    }
    if (target) Object.defineProperty(target, contextIn.name, descriptor);
    done = true;
};
var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
    if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
    return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.KeyVault = exports.ClientEncryption = void 0;
const decorators_1 = require("./decorators");
const cursor_1 = __importDefault(require("./cursor"));
const helpers_1 = require("./helpers");
const enums_1 = require("./enums");
const mongodb_redact_1 = require("mongodb-redact");
const errors_1 = require("@mongosh/errors");
const isDataKeyEncryptionKeyOptions = (options) => {
    return (!Array.isArray(options) &&
        typeof options === 'object' &&
        ('masterKey' in options ||
            'keyAltNames' in options ||
            'keyMaterial' in options));
};
const isMasterKey = (options) => {
    return (!Array.isArray(options) &&
        typeof options === 'object' &&
        !isDataKeyEncryptionKeyOptions(options));
};
let ClientEncryption = (() => {
    let _classDecorators = [decorators_1.shellApiClassDefault, (0, decorators_1.classPlatforms)(['CLI'])];
    let _classDescriptor;
    let _classExtraInitializers = [];
    let _classThis;
    let _classSuper = decorators_1.ShellApiWithMongoClass;
    let _instanceExtraInitializers = [];
    let _encrypt_decorators;
    let _decrypt_decorators;
    let _encryptExpression_decorators;
    let _createEncryptedCollection_decorators;
    var ClientEncryption = _classThis = class extends _classSuper {
        constructor(mongo) {
            super();
            this._mongo = __runInitializers(this, _instanceExtraInitializers);
            this._mongo = mongo;
            const fleOptions = { ...this._mongo._fleOptions };
            delete fleOptions.schemaMap;
            delete fleOptions.encryptedFieldsMap;
            if (!mongo._serviceProvider.createClientEncryption) {
                throw new errors_1.MongoshRuntimeError('FLE API is not available');
            }
            this._libmongocrypt = mongo._serviceProvider.createClientEncryption(fleOptions);
        }
        [enums_1.asPrintable]() {
            return `ClientEncryption class for ${(0, mongodb_redact_1.redactConnectionString)(this._mongo._uri)}`;
        }
        async encrypt(keyId, value, algorithmOrEncryptionOptions) {
            let encryptionOptions;
            if (typeof algorithmOrEncryptionOptions === 'object') {
                encryptionOptions = {
                    keyId,
                    ...algorithmOrEncryptionOptions,
                };
            }
            else {
                encryptionOptions = {
                    keyId,
                    algorithm: algorithmOrEncryptionOptions,
                };
            }
            (0, helpers_1.assertArgsDefinedType)([keyId, value, encryptionOptions], [true, true, true], 'ClientEncryption.encrypt');
            return await this._libmongocrypt.encrypt(value, encryptionOptions);
        }
        async decrypt(encryptedValue) {
            (0, helpers_1.assertArgsDefinedType)([encryptedValue], [true], 'ClientEncryption.decrypt');
            return await this._libmongocrypt.decrypt(encryptedValue);
        }
        async encryptExpression(keyId, value, options) {
            (0, helpers_1.assertArgsDefinedType)([keyId, value, options], [true, true, true], 'ClientEncryption.encryptExpression');
            return await this._libmongocrypt.encryptExpression(value, {
                keyId,
                ...options,
            });
        }
        async createEncryptedCollection(dbName, collName, options) {
            (0, helpers_1.assertArgsDefinedType)([dbName], ['string'], 'ClientEncryption.createEncryptedCollection');
            (0, helpers_1.assertArgsDefinedType)([collName], ['string'], 'ClientEncryption.createEncryptedCollection');
            (0, helpers_1.assertArgsDefinedType)([options], ['object'], 'ClientEncryption.createEncryptedCollection');
            (0, helpers_1.assertKeysDefined)(options, ['provider', 'createCollectionOptions']);
            if (!this._mongo._serviceProvider.createEncryptedCollection) {
                throw new errors_1.MongoshRuntimeError('Runtime does not support createEncryptedCollection yet');
            }
            const { encryptedFields } = await this._mongo._serviceProvider.createEncryptedCollection(dbName, collName, options, this._libmongocrypt);
            return {
                collection: this._mongo.getDB(dbName).getCollection(collName),
                encryptedFields,
            };
        }
    };
    __setFunctionName(_classThis, "ClientEncryption");
    (() => {
        const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
        _encrypt_decorators = [decorators_1.returnsPromise];
        _decrypt_decorators = [decorators_1.returnsPromise];
        _encryptExpression_decorators = [decorators_1.returnsPromise];
        _createEncryptedCollection_decorators = [decorators_1.returnsPromise, (0, decorators_1.apiVersions)([1])];
        __esDecorate(_classThis, null, _encrypt_decorators, { kind: "method", name: "encrypt", static: false, private: false, access: { has: obj => "encrypt" in obj, get: obj => obj.encrypt }, metadata: _metadata }, null, _instanceExtraInitializers);
        __esDecorate(_classThis, null, _decrypt_decorators, { kind: "method", name: "decrypt", static: false, private: false, access: { has: obj => "decrypt" in obj, get: obj => obj.decrypt }, metadata: _metadata }, null, _instanceExtraInitializers);
        __esDecorate(_classThis, null, _encryptExpression_decorators, { kind: "method", name: "encryptExpression", static: false, private: false, access: { has: obj => "encryptExpression" in obj, get: obj => obj.encryptExpression }, metadata: _metadata }, null, _instanceExtraInitializers);
        __esDecorate(_classThis, null, _createEncryptedCollection_decorators, { kind: "method", name: "createEncryptedCollection", static: false, private: false, access: { has: obj => "createEncryptedCollection" in obj, get: obj => obj.createEncryptedCollection }, metadata: _metadata }, null, _instanceExtraInitializers);
        __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
        ClientEncryption = _classThis = _classDescriptor.value;
        if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
        __runInitializers(_classThis, _classExtraInitializers);
    })();
    return ClientEncryption = _classThis;
})();
exports.ClientEncryption = ClientEncryption;
let KeyVault = (() => {
    let _classDecorators = [decorators_1.shellApiClassDefault, (0, decorators_1.classPlatforms)(['CLI'])];
    let _classDescriptor;
    let _classExtraInitializers = [];
    let _classThis;
    let _classSuper = decorators_1.ShellApiWithMongoClass;
    let _instanceExtraInitializers = [];
    let _createKey_decorators;
    let _getKey_decorators;
    let _getKeyByAltName_decorators;
    let _getKeys_decorators;
    let _deleteKey_decorators;
    let _addKeyAlternateName_decorators;
    let _removeKeyAlternateName_decorators;
    let _rewrapManyDataKey_decorators;
    let _createDataKey_decorators;
    let _removeKeyAltName_decorators;
    let _addKeyAltName_decorators;
    var KeyVault = _classThis = class extends _classSuper {
        constructor(clientEncryption) {
            super();
            this._mongo = __runInitializers(this, _instanceExtraInitializers);
            this._mongo = clientEncryption._mongo;
            this._clientEncryption = clientEncryption;
            const keyVaultNamespace = this._mongo?._fleOptions?.keyVaultNamespace;
            if (!keyVaultNamespace) {
                throw new errors_1.MongoshInvalidInputError('FLE options must be passed to the Mongo object');
            }
            const parsedNamespace = keyVaultNamespace.match(/^(?<db>[^.]+)\.(?<coll>.+)$/)?.groups;
            if (!parsedNamespace) {
                throw new errors_1.MongoshInvalidInputError(`Invalid keyVaultNamespace '${keyVaultNamespace}'`);
            }
            const { db, coll } = parsedNamespace;
            this._keyColl = this._mongo.getDB(db).getCollection(coll);
        }
        async _init() {
            try {
                const existingIndexKeys = await this._keyColl.getIndexKeys();
                if (existingIndexKeys.some((key) => key.keyAltNames)) {
                    return;
                }
            }
            catch {
            }
            try {
                await this._keyColl.updateMany({ keyAltNames: { $size: 0 } }, { $unset: { keyAltNames: '' }, $currentDate: { updateDate: true } });
                await this._keyColl.createIndex({ keyAltNames: 1 }, {
                    unique: true,
                    partialFilterExpression: { keyAltNames: { $exists: true } },
                });
            }
            catch (err) {
                await this._instanceState.printWarning(`Creating 'keyAltNames' index on '${this._keyColl.getFullName()}' failed: ${err.message}`);
            }
        }
        [enums_1.asPrintable]() {
            return `KeyVault class for ${(0, mongodb_redact_1.redactConnectionString)(this._mongo._uri)}`;
        }
        async createKey(kms, masterKeyOrAltNamesOrDataKeyOptions, legacyKeyAltNames) {
            let masterKey;
            let keyAltNames;
            let keyMaterial;
            if (isDataKeyEncryptionKeyOptions(masterKeyOrAltNamesOrDataKeyOptions)) {
                masterKey = masterKeyOrAltNamesOrDataKeyOptions?.masterKey;
                keyAltNames = masterKeyOrAltNamesOrDataKeyOptions?.keyAltNames;
                keyMaterial = masterKeyOrAltNamesOrDataKeyOptions?.keyMaterial;
            }
            else if (isMasterKey(masterKeyOrAltNamesOrDataKeyOptions)) {
                masterKey = masterKeyOrAltNamesOrDataKeyOptions;
            }
            if (legacyKeyAltNames) {
                keyAltNames = legacyKeyAltNames;
            }
            (0, helpers_1.assertArgsDefinedType)([kms], [true], 'KeyVault.createKey');
            if (typeof masterKeyOrAltNamesOrDataKeyOptions === 'string') {
                if (kms === 'local' && masterKeyOrAltNamesOrDataKeyOptions === '') {
                    masterKey = undefined;
                }
                else {
                    throw new errors_1.MongoshInvalidInputError('KeyVault.createKey does not support providing masterKey as string anymore. For AWS please use createKey("aws", { region: ..., key: ... })', errors_1.CommonErrors.Deprecated);
                }
            }
            else if (Array.isArray(masterKeyOrAltNamesOrDataKeyOptions)) {
                if (kms !== 'local') {
                    throw new errors_1.MongoshInvalidInputError('KeyVault.createKey requires masterKey to be given as second argument if KMS is not local', errors_1.CommonErrors.InvalidArgument);
                }
                else {
                    if (keyAltNames) {
                        throw new errors_1.MongoshInvalidInputError('KeyVault.createKey was supplied with an array for the masterKey and keyAltNames - either specify keyAltNames as second argument or set undefined for masterKey', errors_1.CommonErrors.InvalidArgument);
                    }
                    keyAltNames = masterKeyOrAltNamesOrDataKeyOptions;
                    masterKey = undefined;
                }
            }
            let options;
            if (masterKey) {
                options = { ...(options ?? {}), masterKey };
            }
            if (keyAltNames) {
                options = { ...(options ?? {}), keyAltNames };
            }
            if (keyMaterial) {
                options = { ...(options ?? {}), keyMaterial };
            }
            return await this._clientEncryption._libmongocrypt.createDataKey(kms, options);
        }
        async getKey(keyId) {
            (0, helpers_1.assertArgsDefinedType)([keyId], [true], 'KeyVault.getKey');
            const cursor = await this._keyColl.find({ _id: keyId });
            return await cursor.limit(1).next();
        }
        async getKeyByAltName(keyAltName) {
            (0, helpers_1.assertArgsDefinedType)([keyAltName], ['string'], 'KeyVault.getKeyByAltName');
            const cursor = await this._keyColl.find({ keyAltNames: keyAltName });
            return await cursor.limit(1).next();
        }
        async getKeys() {
            return new cursor_1.default(this._mongo, this._clientEncryption._libmongocrypt.getKeys());
        }
        async deleteKey(keyId) {
            (0, helpers_1.assertArgsDefinedType)([keyId], [true], 'KeyVault.deleteKey');
            return await this._clientEncryption._libmongocrypt.deleteKey(keyId);
        }
        async addKeyAlternateName(keyId, keyAltName) {
            (0, helpers_1.assertArgsDefinedType)([keyId, keyAltName], [true, 'string'], 'KeyVault.addKeyAlternateName');
            return await this._clientEncryption._libmongocrypt.addKeyAltName(keyId, keyAltName);
        }
        async removeKeyAlternateName(keyId, keyAltName) {
            (0, helpers_1.assertArgsDefinedType)([keyId, keyAltName], [true, 'string'], 'KeyVault.removeKeyAlternateName');
            return await this._clientEncryption._libmongocrypt.removeKeyAltName(keyId, keyAltName);
        }
        async rewrapManyDataKey(filter, options) {
            return await this._clientEncryption._libmongocrypt.rewrapManyDataKey(filter, options);
        }
        async createDataKey(...args) {
            return await this.createKey(...args);
        }
        async removeKeyAltName(...args) {
            return await this.removeKeyAlternateName(...args);
        }
        async addKeyAltName(...args) {
            return await this.addKeyAlternateName(...args);
        }
    };
    __setFunctionName(_classThis, "KeyVault");
    (() => {
        const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
        _createKey_decorators = [decorators_1.returnsPromise, (0, decorators_1.apiVersions)([1])];
        _getKey_decorators = [(0, decorators_1.apiVersions)([1]), decorators_1.returnsPromise];
        _getKeyByAltName_decorators = [(0, decorators_1.apiVersions)([1]), decorators_1.returnsPromise];
        _getKeys_decorators = [(0, decorators_1.returnType)('Cursor'), (0, decorators_1.apiVersions)([1]), decorators_1.returnsPromise];
        _deleteKey_decorators = [decorators_1.returnsPromise, (0, decorators_1.apiVersions)([1])];
        _addKeyAlternateName_decorators = [decorators_1.returnsPromise, (0, decorators_1.apiVersions)([1])];
        _removeKeyAlternateName_decorators = [decorators_1.returnsPromise, (0, decorators_1.apiVersions)([1])];
        _rewrapManyDataKey_decorators = [decorators_1.returnsPromise, (0, decorators_1.apiVersions)([1])];
        _createDataKey_decorators = [decorators_1.returnsPromise, (0, decorators_1.apiVersions)([1])];
        _removeKeyAltName_decorators = [decorators_1.returnsPromise, (0, decorators_1.apiVersions)([1])];
        _addKeyAltName_decorators = [decorators_1.returnsPromise, (0, decorators_1.apiVersions)([1])];
        __esDecorate(_classThis, null, _createKey_decorators, { kind: "method", name: "createKey", static: false, private: false, access: { has: obj => "createKey" in obj, get: obj => obj.createKey }, metadata: _metadata }, null, _instanceExtraInitializers);
        __esDecorate(_classThis, null, _getKey_decorators, { kind: "method", name: "getKey", static: false, private: false, access: { has: obj => "getKey" in obj, get: obj => obj.getKey }, metadata: _metadata }, null, _instanceExtraInitializers);
        __esDecorate(_classThis, null, _getKeyByAltName_decorators, { kind: "method", name: "getKeyByAltName", static: false, private: false, access: { has: obj => "getKeyByAltName" in obj, get: obj => obj.getKeyByAltName }, metadata: _metadata }, null, _instanceExtraInitializers);
        __esDecorate(_classThis, null, _getKeys_decorators, { kind: "method", name: "getKeys", static: false, private: false, access: { has: obj => "getKeys" in obj, get: obj => obj.getKeys }, metadata: _metadata }, null, _instanceExtraInitializers);
        __esDecorate(_classThis, null, _deleteKey_decorators, { kind: "method", name: "deleteKey", static: false, private: false, access: { has: obj => "deleteKey" in obj, get: obj => obj.deleteKey }, metadata: _metadata }, null, _instanceExtraInitializers);
        __esDecorate(_classThis, null, _addKeyAlternateName_decorators, { kind: "method", name: "addKeyAlternateName", static: false, private: false, access: { has: obj => "addKeyAlternateName" in obj, get: obj => obj.addKeyAlternateName }, metadata: _metadata }, null, _instanceExtraInitializers);
        __esDecorate(_classThis, null, _removeKeyAlternateName_decorators, { kind: "method", name: "removeKeyAlternateName", static: false, private: false, access: { has: obj => "removeKeyAlternateName" in obj, get: obj => obj.removeKeyAlternateName }, metadata: _metadata }, null, _instanceExtraInitializers);
        __esDecorate(_classThis, null, _rewrapManyDataKey_decorators, { kind: "method", name: "rewrapManyDataKey", static: false, private: false, access: { has: obj => "rewrapManyDataKey" in obj, get: obj => obj.rewrapManyDataKey }, metadata: _metadata }, null, _instanceExtraInitializers);
        __esDecorate(_classThis, null, _createDataKey_decorators, { kind: "method", name: "createDataKey", static: false, private: false, access: { has: obj => "createDataKey" in obj, get: obj => obj.createDataKey }, metadata: _metadata }, null, _instanceExtraInitializers);
        __esDecorate(_classThis, null, _removeKeyAltName_decorators, { kind: "method", name: "removeKeyAltName", static: false, private: false, access: { has: obj => "removeKeyAltName" in obj, get: obj => obj.removeKeyAltName }, metadata: _metadata }, null, _instanceExtraInitializers);
        __esDecorate(_classThis, null, _addKeyAltName_decorators, { kind: "method", name: "addKeyAltName", static: false, private: false, access: { has: obj => "addKeyAltName" in obj, get: obj => obj.addKeyAltName }, metadata: _metadata }, null, _instanceExtraInitializers);
        __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
        KeyVault = _classThis = _classDescriptor.value;
        if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
        __runInitializers(_classThis, _classExtraInitializers);
    })();
    return KeyVault = _classThis;
})();
exports.KeyVault = KeyVault;
//# sourceMappingURL=field-level-encryption.js.map