"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.initialiseExchangeDataManagerForCurrentDataOwner = initialiseExchangeDataManagerForCurrentDataOwner;
const utils_1 = require("./utils");
const utils_2 = require("../utils");
const CryptoActorStub_1 = require("../../icc-api/model/CryptoActorStub");
const RSA_1 = require("./RSA");
const mutex_1 = require("../utils/mutex");
const simple_lru_cache_1 = require("../utils/simple-lru-cache");
/**
 * @internal this function is for internal use only and may be changed without notice.
 * Initialises and returns the exchange data manager which is most appropriate for the current data owner.
 */
function initialiseExchangeDataManagerForCurrentDataOwner(base_1, encryptionKeys_1, accessControlSecret_1, cryptoStrategies_1, dataOwnerApi_1, primitives_1, useParentKeys_1) {
    return __awaiter(this, arguments, void 0, function* (base, encryptionKeys, accessControlSecret, cryptoStrategies, dataOwnerApi, primitives, useParentKeys, optionalParameters = {}) {
        const currentOwner = CryptoActorStub_1.CryptoActorStubWithType.fromDataOwner(yield dataOwnerApi.getCurrentDataOwner());
        if (cryptoStrategies.dataOwnerRequiresAnonymousDelegation(currentOwner)) {
            const res = new FullyCachedExchangeDataManager(base, encryptionKeys, accessControlSecret, cryptoStrategies, dataOwnerApi, primitives, useParentKeys);
            yield res.clearOrRepopulateCache();
            return res;
        }
        else {
            return new LimitedLruCacheExchangeDataManager(base, encryptionKeys, accessControlSecret, cryptoStrategies, dataOwnerApi, primitives, useParentKeys, optionalParameters);
        }
    });
}
class AbstractExchangeDataManager {
    constructor(base, encryptionKeys, accessControlSecret, cryptoStrategies, dataOwnerApi, primitives, useParentKeys) {
        this.base = base;
        this.encryptionKeys = encryptionKeys;
        this.accessControlSecret = accessControlSecret;
        this.cryptoStrategies = cryptoStrategies;
        this.dataOwnerApi = dataOwnerApi;
        this.primitives = primitives;
        this.useParentKeys = useParentKeys;
    }
    decryptData(data) {
        return __awaiter(this, void 0, void 0, function* () {
            const decryptionKeys = this.encryptionKeys.getDecryptionKeys();
            const encryptionKeys = Object.fromEntries(this.encryptionKeys.getSelfVerifiedKeys().map((x) => [x.fingerprint, x.pair.privateKey]));
            const decryptedExchangeKey = (yield this.base.tryDecryptExchangeKeys([data], decryptionKeys)).successfulDecryptions[0];
            if (!decryptedExchangeKey)
                return undefined;
            const decryptedAccessControlSecret = (yield this.base.tryDecryptAccessControlSecret([data], decryptionKeys)).successfulDecryptions[0];
            if (!decryptedAccessControlSecret)
                throw new Error(`Decryption key could be decrypted but access control secret could not for data ${JSON.stringify(data)}`);
            const decryptedSharedSignatureKey = (yield this.base.tryDecryptSharedSignatureKeys([data], decryptionKeys)).successfulDecryptions[0];
            if (!decryptedSharedSignatureKey)
                throw new Error(`Decryption key could be decrypted but shared signature key could not for data ${JSON.stringify(data)}`);
            return {
                accessControlSecret: decryptedAccessControlSecret,
                exchangeKey: decryptedExchangeKey,
                sharedSignatureKey: decryptedSharedSignatureKey,
                verified: yield this.base.verifyExchangeData({
                    exchangeData: data,
                    decryptedAccessControlSecret,
                    decryptedExchangeKey,
                    decryptedSharedSignatureKey,
                }, encryptionKeys, true),
            };
        });
    }
    createNewExchangeData(delegateId, options) {
        return __awaiter(this, void 0, void 0, function* () {
            if (options.allowNoDelegateKeys && options.allowNoDelegatorKeys)
                throw new Error('Illegal arguments: allow no delegator and no delegate keys should never both be true');
            const encryptionKeys = {};
            const selfVerifiedKeys = this.encryptionKeys.getSelfVerifiedKeys();
            if (selfVerifiedKeys.length <= 0 && !options.allowNoDelegatorKeys)
                throw new Error('If the sdk is initialized in keyless mode you must create exchange data explicitly');
            selfVerifiedKeys.forEach(({ fingerprint, pair }) => {
                encryptionKeys[fingerprint] = pair.publicKey;
            });
            if (delegateId != (yield this.dataOwnerApi.getCurrentDataOwnerId())) {
                const delegate = yield this.dataOwnerApi.getCryptoActorStub(delegateId);
                const sha256KeysOfDelegate = (0, utils_1.hexPublicKeysWithSha256Of)(delegate.stub);
                const sha1KeysOfDelegate = (0, utils_1.hexPublicKeysWithSha1Of)(delegate.stub);
                let allVerifiedDelegateKeys;
                if (!sha256KeysOfDelegate.size && !sha1KeysOfDelegate.size) {
                    if (!options.allowNoDelegateKeys)
                        throw new Error(`Could not create exchange data to ${delegateId} as no public key for the delegate was found.`);
                    allVerifiedDelegateKeys = [];
                }
                else if (this.useParentKeys && (yield this.dataOwnerApi.getCurrentDataOwnerHierarchyIds()).includes(delegateId)) {
                    allVerifiedDelegateKeys = yield this.encryptionKeys.getVerifiedPublicKeysFor(delegate.stub);
                }
                else {
                    allVerifiedDelegateKeys = yield this.cryptoStrategies.verifyDelegatePublicKeys(delegate, [...Array.from(sha256KeysOfDelegate), ...Array.from(sha1KeysOfDelegate)], this.primitives);
                }
                if (!allVerifiedDelegateKeys.length && !options.allowNoDelegateKeys)
                    throw new Error(`Could not create exchange data to ${delegateId} as no public key for the delegate could be verified.`);
                for (const delegateKey of allVerifiedDelegateKeys) {
                    if (sha1KeysOfDelegate.has(delegateKey)) {
                        encryptionKeys[(0, utils_1.fingerprintV1)(delegateKey)] = yield this.primitives.RSA.importKey('spki', (0, utils_2.ua2ab)((0, utils_2.hex2ua)(delegateKey)), ['encrypt'], RSA_1.ShaVersion.Sha1);
                    }
                    else if (sha256KeysOfDelegate.has(delegateKey)) {
                        encryptionKeys[(0, utils_1.fingerprintV1)(delegateKey)] = yield this.primitives.RSA.importKey('spki', (0, utils_2.ua2ab)((0, utils_2.hex2ua)(delegateKey)), ['encrypt'], RSA_1.ShaVersion.Sha256);
                    }
                    else
                        throw new Error('Illegal state: verified keys should contain only keys for OAPE-SHA1 or OAPE-SHA256.');
                }
            }
            const newData = yield this.base.createExchangeData(delegateId, Object.fromEntries(selfVerifiedKeys.map((x) => [x.fingerprint, x.pair.privateKey])), encryptionKeys, options.newDataId ? { id: options.newDataId } : {});
            return {
                exchangeData: newData.exchangeData,
                accessControlSecret: newData.accessControlSecret,
                exchangeKey: newData.exchangeKey,
                sharedSignatureKey: newData.sharedSignatureKey,
            };
        });
    }
    giveAccessBackTo(otherDataOwner, newDataOwnerPublicKey) {
        return __awaiter(this, void 0, void 0, function* () {
            const self = yield this.dataOwnerApi.getCurrentDataOwnerId();
            const newKeyFp = (0, utils_1.fingerprintV1)(newDataOwnerPublicKey);
            const other = yield this.dataOwnerApi.getCryptoActorStub(otherDataOwner);
            const newKeyHashVersion = (0, utils_1.getShaVersionForKey)(other.stub, newDataOwnerPublicKey);
            if (!newKeyHashVersion)
                throw new Error(`Public key not found for data owner ${otherDataOwner}`);
            const importedNewKey = yield this.primitives.RSA.importKey('spki', (0, utils_2.ua2ab)((0, utils_2.hex2ua)(newDataOwnerPublicKey)), ['encrypt'], newKeyHashVersion);
            const decryptionKeys = this.encryptionKeys.getDecryptionKeys();
            const allExchangeDataToUpdate = self == otherDataOwner
                ? yield this.base.getExchangeDataByDelegatorDelegatePair(self, self)
                : [
                    ...(yield this.base.getExchangeDataByDelegatorDelegatePair(self, otherDataOwner)),
                    ...(yield this.base.getExchangeDataByDelegatorDelegatePair(otherDataOwner, self)),
                ];
            for (const dataToUpdate of allExchangeDataToUpdate) {
                if (!Object.keys(dataToUpdate.exchangeKey).find((fp) => fp == newKeyFp)) {
                    const updated = yield this.base.tryUpdateExchangeData(dataToUpdate, decryptionKeys, { [newKeyFp]: importedNewKey });
                    if (!updated) {
                        console.warn(`Failed to give access back to exchanged data ${JSON.stringify(dataToUpdate)}`);
                    }
                }
            }
        });
    }
    clearOrRepopulateCache() {
        throw new Error('Implemented by concrete class');
    }
    getOrCreateEncryptionDataTo(delegateId, options) {
        throw new Error('Implemented by concrete class');
    }
    getCachedDecryptionDataKeyByAccessControlHash(hashes) {
        throw new Error('Implemented by concrete class');
    }
    getDecryptionDataKeyByIds(ids, retrieveIfNotCached) {
        throw new Error('Implemented by concrete class');
    }
    getAccessControlKeysValue(entityType) {
        throw new Error('Implemented by concrete class');
    }
    getAllDelegationKeys(entityType) {
        throw new Error('Implemented by concrete class');
    }
    injectDecryptedExchangeData(exchangeDataDetails, reEncryptWithOwnKeys) {
        return __awaiter(this, void 0, void 0, function* () {
            const self = yield this.dataOwnerApi.getCurrentDataOwnerId();
            const retrievedExchangeData = yield this.base.getExchangeDataByIds(exchangeDataDetails.map((x) => x.exchangeDataId));
            if (retrievedExchangeData.some((x) => x.delegator != self && x.delegate != self))
                throw new Error('Should only inject exchange date from/to the current user');
            const exchangeDataById = {};
            retrievedExchangeData.forEach((x) => (exchangeDataById[x.id] = x));
            if (reEncryptWithOwnKeys) {
                const selfVerifiedKeys = this.encryptionKeys.getSelfVerifiedKeys();
                if (selfVerifiedKeys.length <= 0)
                    throw new Error("Can't re-encrypt injected exchange data with own keys if in keyless mode");
                const encryptionKeys = {};
                selfVerifiedKeys.forEach(({ fingerprint, pair }) => {
                    encryptionKeys[fingerprint] = pair.publicKey;
                });
                const signatureKeys = Object.fromEntries(selfVerifiedKeys.map((x) => [x.fingerprint, x.pair.privateKey]));
                for (const details of exchangeDataDetails) {
                    const exchangeData = exchangeDataById[details.exchangeDataId];
                    if (exchangeData != null)
                        yield this.base.updateExchangeDataWithRawDecryptedContent({
                            exchangeData,
                            newEncryptionKeys: encryptionKeys,
                            newDelegatorSignatureKeys: exchangeData.delegator == self && details.verified ? signatureKeys : {},
                            rawExchangeKey: details.exchangeKey,
                            rawAccessControlSecret: details.accessControlSecret,
                            rawSharedSignatureKey: details.sharedSignatureKey,
                        });
                }
            }
            const importedDetails = [];
            for (const details of exchangeDataDetails) {
                const exchangeData = exchangeDataById[details.exchangeDataId];
                if (exchangeData != undefined)
                    importedDetails.push({
                        exchangeData: exchangeData,
                        accessControlSecret: yield this.base.importAccessControlSecret(details.accessControlSecret),
                        exchangeKey: yield this.base.importExchangeKey(details.exchangeKey),
                        sharedSignatureKey: yield this.base.importSharedSignatureKey(details.sharedSignatureKey),
                        verified: details.verified,
                    });
            }
            yield this.cacheInjectedExchangeData(importedDetails);
        });
    }
}
class FullyCachedExchangeDataManager extends AbstractExchangeDataManager {
    constructor() {
        super(...arguments);
        this.caches = Promise.resolve({ dataById: {}, hashToId: {}, delegateToVerifiedEncryptionDataId: {}, entityTypeToAccessControlKeysValue: {} });
        this.createExchangeDataMutex = new mutex_1.Mutex();
    }
    clearOrRepopulateCache() {
        return __awaiter(this, void 0, void 0, function* () {
            this.caches = this.doRepopulateCache();
            yield this.caches;
        });
    }
    getCachedDecryptionDataKeyByAccessControlHash(hashes) {
        return __awaiter(this, void 0, void 0, function* () {
            function retrieveByHashesFromCaches(caches) {
                return hashes.reduce((res, hash) => {
                    const id = caches.hashToId[hash];
                    if (id) {
                        const cached = caches.dataById[id];
                        if (cached === null || cached === void 0 ? void 0 : cached.decrypted) {
                            res[hash] = {
                                exchangeData: cached.exchangeData,
                                exchangeKey: cached.decrypted.exchangeKey,
                                accessControlSecret: cached.decrypted.accessControlSecret,
                            };
                        }
                    }
                    return res;
                }, {});
            }
            return retrieveByHashesFromCaches(yield this.caches);
        });
    }
    getCachedEncryptionDataTo(delegateId) {
        return __awaiter(this, void 0, void 0, function* () {
            const caches = yield this.caches;
            const dataId = caches.delegateToVerifiedEncryptionDataId[delegateId];
            const cached = dataId ? caches.dataById[dataId] : undefined;
            if (cached && (cached === null || cached === void 0 ? void 0 : cached.decrypted)) {
                return {
                    exchangeData: cached.exchangeData,
                    accessControlSecret: cached.decrypted.accessControlSecret,
                    exchangeKey: cached.decrypted.exchangeKey,
                    sharedSignatureKey: cached.decrypted.sharedSignatureKey,
                };
            }
            else {
                return undefined;
            }
        });
    }
    getOrCreateEncryptionDataTo(delegateId, options) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a, _b;
            const initialCached = yield this.getCachedEncryptionDataTo(delegateId);
            if (initialCached)
                return initialCached;
            const release = yield this.createExchangeDataMutex.acquire();
            try {
                const cachedAfterMutex = yield this.getCachedEncryptionDataTo(delegateId);
                if (cachedAfterMutex)
                    return cachedAfterMutex;
                const created = yield this.createNewExchangeData(delegateId, {
                    allowNoDelegateKeys: (_a = options === null || options === void 0 ? void 0 : options.allowCreationWithoutDelegateKey) !== null && _a !== void 0 ? _a : false,
                    allowNoDelegatorKeys: (_b = options === null || options === void 0 ? void 0 : options.allowCreationWithoutDelegatorKey) !== null && _b !== void 0 ? _b : false,
                });
                this.cacheData(created.exchangeData, true, {
                    accessControlSecret: created.accessControlSecret,
                    exchangeKey: created.exchangeKey,
                    sharedSignatureKey: created.sharedSignatureKey,
                    verified: true,
                });
                return created;
            }
            finally {
                release();
            }
        });
    }
    getDecryptionDataKeyByIds(ids, retrieveIfNotCached) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a, _b;
            const caches = yield this.caches;
            const res = {};
            for (const id of ids) {
                const data = caches.dataById[id];
                if (data) {
                    res[id] = {
                        exchangeData: data.exchangeData,
                        exchangeKey: (_a = data.decrypted) === null || _a === void 0 ? void 0 : _a.exchangeKey,
                        accessControlSecret: (_b = data.decrypted) === null || _b === void 0 ? void 0 : _b.accessControlSecret,
                    };
                }
            }
            return res;
        });
    }
    cacheData(exchangeData, isNewData, decrypted) {
        this.caches = this.caches.then((caches) => __awaiter(this, void 0, void 0, function* () {
            caches.dataById[exchangeData.id] = { exchangeData, decrypted };
            if (decrypted) {
                const hashes = yield this.accessControlSecret.allSecureDelegationKeysFor(decrypted.accessControlSecret);
                hashes.forEach((hash) => {
                    caches.hashToId[hash] = exchangeData.id;
                });
                if (decrypted.verified) {
                    caches.delegateToVerifiedEncryptionDataId[exchangeData.delegate] = exchangeData.id;
                }
            }
            if (isNewData)
                caches.entityTypeToAccessControlKeysValue = {};
            return caches;
        }));
    }
    doRepopulateCache() {
        return __awaiter(this, void 0, void 0, function* () {
            const allData = yield this.base.getAllExchangeDataForCurrentDataOwnerIfAllowed();
            if (!allData)
                throw new Error('Impossible to use fully cached exchange data manager for current data owner.');
            const dataById = {};
            const hashToId = {};
            const delegateToVerifiedEncryptionDataId = {};
            for (const currData of allData) {
                const currDecrypted = yield this.decryptData(currData);
                dataById[currData.id] = { exchangeData: currData, decrypted: currDecrypted };
                if (currDecrypted === null || currDecrypted === void 0 ? void 0 : currDecrypted.verified) {
                    delegateToVerifiedEncryptionDataId[currData.delegate] = currData.id;
                }
                if (currDecrypted === null || currDecrypted === void 0 ? void 0 : currDecrypted.accessControlSecret) {
                    for (const h of yield this.accessControlSecret.allSecureDelegationKeysFor(currDecrypted.accessControlSecret)) {
                        hashToId[h] = currData.id;
                    }
                }
            }
            const entityTypeToAccessControlKeysValue = {};
            return { dataById, hashToId, delegateToVerifiedEncryptionDataId, entityTypeToAccessControlKeysValue };
        });
    }
    getAccessControlKeysValue(entityType) {
        return __awaiter(this, void 0, void 0, function* () {
            const caches = yield this.caches;
            const cached = caches.entityTypeToAccessControlKeysValue[entityType];
            if (cached)
                return cached;
            const accessControlSecrets = Object.values(caches.dataById).flatMap((x) => (x.decrypted ? [x.decrypted.accessControlSecret] : []));
            const fullData = yield this.accessControlSecret.getEncodedAccessControlKeys(accessControlSecrets, entityType);
            caches.entityTypeToAccessControlKeysValue[entityType] = fullData;
            return fullData;
        });
    }
    getAllDelegationKeys(entityType) {
        return __awaiter(this, void 0, void 0, function* () {
            const caches = yield this.caches;
            const accessControlSecrets = Object.values(caches.dataById).flatMap((x) => (x.decrypted ? [x.decrypted.accessControlSecret] : []));
            const res = [];
            for (const accessControlSecret of accessControlSecrets) {
                // Usage of sfks in secure delegation key should be configurable: it is not necessary for all users and it has some performance impact
                res.push(yield this.accessControlSecret.secureDelegationKeyFor(accessControlSecret, entityType));
            }
            return res;
        });
    }
    cacheInjectedExchangeData(exchangeDataDetails) {
        return __awaiter(this, void 0, void 0, function* () {
            for (const details of exchangeDataDetails) {
                this.cacheData(details.exchangeData, true, {
                    accessControlSecret: details.accessControlSecret,
                    exchangeKey: details.exchangeKey,
                    sharedSignatureKey: details.sharedSignatureKey,
                    verified: details.verified,
                });
            }
        });
    }
}
class LimitedLruCacheExchangeDataManager extends AbstractExchangeDataManager {
    constructor(base, encryptionKeys, accessControlSecret, cryptoStrategies, dataOwnerApi, primitives, useParentKeys, optionalParameters) {
        var _a;
        super(base, encryptionKeys, accessControlSecret, cryptoStrategies, dataOwnerApi, primitives, useParentKeys);
        this.idToData = new simple_lru_cache_1.SimpleLruCache();
        this.hashToId = new Map();
        this.delegateToVerifiedEncryptionDataId = new Map();
        this.cacheMutex = new mutex_1.Mutex();
        this.maxCachedExchangeData = (_a = optionalParameters.lruCacheSize) !== null && _a !== void 0 ? _a : 2000;
    }
    clearOrRepopulateCache() {
        return __awaiter(this, void 0, void 0, function* () {
            this.idToData.clear();
            this.hashToId.clear();
            this.delegateToVerifiedEncryptionDataId.clear();
        });
    }
    getCachedDecryptionDataKeyByAccessControlHash(hashes) {
        return __awaiter(this, void 0, void 0, function* () {
            const release = yield this.cacheMutex.acquire();
            try {
                const res = {};
                for (const hash of hashes) {
                    const dataId = this.hashToId.get(hash);
                    if (dataId) {
                        const retrieved = this.idToData.getCached(dataId);
                        if (!retrieved)
                            throw new Error(`Data with id ${dataId} should have been already cached.`);
                        if (retrieved.decrypted) {
                            res[hash] = {
                                exchangeData: retrieved.exchangeData,
                                exchangeKey: retrieved.decrypted.exchangeKey,
                                accessControlSecret: retrieved.decrypted.accessControlSecret,
                            };
                        }
                    }
                }
                return res;
            }
            finally {
                release();
            }
        });
    }
    getDecryptionDataKeyByIds(ids, retrieveIfNotCached) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a, _b;
            const release = yield this.cacheMutex.acquire();
            try {
                const res = {};
                const uncached = [];
                for (const id of ids) {
                    const cached = this.idToData.getCached(id);
                    if (cached) {
                        res[id] = {
                            exchangeKey: (_a = cached.decrypted) === null || _a === void 0 ? void 0 : _a.exchangeKey,
                            accessControlSecret: (_b = cached.decrypted) === null || _b === void 0 ? void 0 : _b.accessControlSecret,
                            exchangeData: cached.exchangeData,
                        };
                    }
                    else if (retrieveIfNotCached) {
                        uncached.push(id);
                    }
                }
                if (uncached.length > 0) {
                    const retrieved = yield this.base.getExchangeDataByIds(uncached);
                    for (const data of retrieved) {
                        const decrypted = yield this.decryptData(data);
                        if (decrypted) {
                            const hashes = yield this.accessControlSecret.allSecureDelegationKeysFor(decrypted.accessControlSecret);
                            this.addToCache({ exchangeData: data, hashes, decrypted });
                        }
                        else {
                            this.addToCache({ exchangeData: data, hashes: [] });
                        }
                        res[data.id] = {
                            exchangeKey: decrypted === null || decrypted === void 0 ? void 0 : decrypted.exchangeKey,
                            accessControlSecret: decrypted === null || decrypted === void 0 ? void 0 : decrypted.accessControlSecret,
                            exchangeData: data,
                        };
                    }
                }
                return res;
            }
            finally {
                release();
            }
        });
    }
    getOrCreateEncryptionDataTo(delegateId, options) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a, _b;
            const release = yield this.cacheMutex.acquire();
            try {
                let existingId = this.delegateToVerifiedEncryptionDataId.get(delegateId);
                if (!existingId) {
                    yield this.populateCacheToDelegate(delegateId);
                    existingId = this.delegateToVerifiedEncryptionDataId.get(delegateId);
                }
                if (existingId) {
                    const cached = this.idToData.getCached(existingId);
                    if (!cached)
                        throw new Error(`Illegal state: data with id ${existingId} should have been in cache`);
                    if (cached.decrypted) {
                        return {
                            exchangeData: cached.exchangeData,
                            exchangeKey: cached.decrypted.exchangeKey,
                            accessControlSecret: cached.decrypted.accessControlSecret,
                            sharedSignatureKey: cached.decrypted.sharedSignatureKey,
                        };
                    }
                    else
                        throw new Error(`Illegal state: cached verified data should be decrypted.`);
                }
                else {
                    const created = yield this.createNewExchangeData(delegateId, {
                        allowNoDelegateKeys: (_a = options === null || options === void 0 ? void 0 : options.allowCreationWithoutDelegateKey) !== null && _a !== void 0 ? _a : false,
                        allowNoDelegatorKeys: (_b = options === null || options === void 0 ? void 0 : options.allowCreationWithoutDelegatorKey) !== null && _b !== void 0 ? _b : false,
                    });
                    const hashes = yield this.accessControlSecret.allSecureDelegationKeysFor(created.accessControlSecret);
                    const data = {
                        exchangeData: created.exchangeData,
                        decrypted: {
                            accessControlSecret: created.accessControlSecret,
                            exchangeKey: created.exchangeKey,
                            sharedSignatureKey: created.sharedSignatureKey,
                            verified: true,
                        },
                        hashes,
                    };
                    this.addToCache(data);
                    return {
                        exchangeData: data.exchangeData,
                        exchangeKey: data.decrypted.exchangeKey,
                        accessControlSecret: data.decrypted.accessControlSecret,
                        sharedSignatureKey: data.decrypted.sharedSignatureKey,
                    };
                }
            }
            finally {
                release();
            }
        });
    }
    // Loads and adds to the cache all exchange data from the current data owner to the given delegate. Allows to check if there is already data from
    // the current data owner to the delegate which is good for encryption.
    populateCacheToDelegate(delegateId) {
        return __awaiter(this, void 0, void 0, function* () {
            const dataToDelegate = yield this.base.getExchangeDataByDelegatorDelegatePair(yield this.dataOwnerApi.getCurrentDataOwnerId(), delegateId);
            for (const data of dataToDelegate) {
                const decrypted = yield this.decryptData(data);
                if (decrypted) {
                    const hashes = yield this.accessControlSecret.allSecureDelegationKeysFor(decrypted.accessControlSecret);
                    this.addToCache({ exchangeData: data, hashes, decrypted });
                }
                else {
                    this.addToCache({ exchangeData: data, hashes: [] });
                }
            }
        });
    }
    addToCache(data) {
        var _a;
        if (this.idToData.size >= this.maxCachedExchangeData) {
            this.evictOneEntry();
        }
        data.hashes.forEach((hash) => this.hashToId.set(hash, data.exchangeData.id));
        if (((_a = data.decrypted) === null || _a === void 0 ? void 0 : _a.verified) && !this.delegateToVerifiedEncryptionDataId.has(data.exchangeData.delegate)) {
            this.delegateToVerifiedEncryptionDataId.set(data.exchangeData.delegate, data.exchangeData.id);
        }
        this.idToData.set(data.exchangeData.id, data);
    }
    evictOneEntry() {
        const evicted = this.idToData.evictLeastRecentlyUsed();
        evicted.hashes.forEach((hash) => this.hashToId.delete(hash));
        if (this.delegateToVerifiedEncryptionDataId.get(evicted.exchangeData.delegate) === evicted.exchangeData.id) {
            this.delegateToVerifiedEncryptionDataId.delete(evicted.exchangeData.delegate);
        }
    }
    getAccessControlKeysValue() {
        return Promise.resolve(undefined);
    }
    getAllDelegationKeys() {
        return Promise.resolve(undefined);
    }
    cacheInjectedExchangeData(exchangeDataDetails) {
        return __awaiter(this, void 0, void 0, function* () {
            for (const details of exchangeDataDetails) {
                const hashes = yield this.accessControlSecret.allSecureDelegationKeysFor(details.accessControlSecret);
                const data = {
                    exchangeData: details.exchangeData,
                    decrypted: {
                        accessControlSecret: details.accessControlSecret,
                        exchangeKey: details.exchangeKey,
                        sharedSignatureKey: details.sharedSignatureKey,
                        verified: details.verified,
                    },
                    hashes,
                };
                this.addToCache(data);
            }
        });
    }
}
//# sourceMappingURL=ExchangeDataManager.js.map