"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.IccHelementXApi = void 0;
const icc_api_1 = require("../icc-api");
const models = require("../icc-api/model/models");
const date_fns_1 = require("date-fns");
const collection_utils_1 = require("./utils/collection-utils");
const AuthenticationProvider_1 = require("./auth/AuthenticationProvider");
const SecureDelegation_1 = require("../icc-api/model/SecureDelegation");
var AccessLevelEnum = SecureDelegation_1.SecureDelegation.AccessLevelEnum;
const utils_1 = require("./utils");
const Connection_1 = require("../icc-api/model/Connection");
const SecretIdUseOption_1 = require("./crypto/SecretIdUseOption");
class IccHelementXApi extends icc_api_1.IccHelementApi {
    get headers() {
        return super.headers.then((h) => this.crypto.accessControlKeysHeaders.addAccessControlKeysHeaders(h, utils_1.EntityWithDelegationTypeName.HealthElement));
    }
    constructor(host, headers, crypto, dataOwnerApi, userApi, authApi, autofillAuthor, encryptedKeys = ['descr', 'note'], authenticationProvider = new AuthenticationProvider_1.NoAuthenticationProvider(), fetchImpl = typeof window !== 'undefined'
        ? window.fetch
        : typeof self !== 'undefined'
            ? self.fetch
            : fetch) {
        super(host, headers, authenticationProvider, fetchImpl);
        this.crypto = crypto;
        this.dataOwnerApi = dataOwnerApi;
        this.userApi = userApi;
        this.authApi = authApi;
        this.autofillAuthor = autofillAuthor;
        this.encryptedFields = (0, utils_1.parseEncryptedFields)(encryptedKeys, 'HealthElement.');
    }
    /**
     * Creates a new instance of health element with initialised encryption metadata (not in the database).
     * @param user the current user.
     * @param patient the patient this health element refers to.
     * @param h initialised data for the health element. Metadata such as id, creation data, etc. will be automatically initialised, but you can specify
     * other kinds of data or overwrite generated metadata with this. You can't specify encryption metadata.
     * @param options optional parameters:
     * - additionalDelegates: delegates which will have access to the entity in addition to the current data owner and delegates from the
     * auto-delegations. Must be an object which associates each data owner id with the access level to give to that data owner. May overlap with
     * auto-delegations, in such case the access level specified here will be used.
     * - sfkOption: specifies which sfk of the owning entity to use.
     * - ignoreAutoDelegations: if true the data won't be shared with the autodelegations of the user, but only with additional delegates
     * - alternateRootDelegation: by default a new entity is created with a root delegation from self to self. In keyless mode this is not possible,
     * and instead the root delegation will be from self to another. You have to specify which delegate will be part of the root delegation.
     * @return a new instance of health element.
     */
    newInstance(user_1, patient_1, h_1) {
        return __awaiter(this, arguments, void 0, function* (user, patient, h, options = {}) {
            var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
            const dataOwnerId = this.dataOwnerApi.getDataOwnerIdOf(user);
            const helement = Object.assign(Object.assign({}, (h !== null && h !== void 0 ? h : {})), { _type: 'org.taktik.icure.entities.HealthElement', id: (_a = h === null || h === void 0 ? void 0 : h.id) !== null && _a !== void 0 ? _a : this.crypto.primitives.randomUuid(), created: (_b = h === null || h === void 0 ? void 0 : h.created) !== null && _b !== void 0 ? _b : new Date().getTime(), modified: (_c = h === null || h === void 0 ? void 0 : h.modified) !== null && _c !== void 0 ? _c : new Date().getTime(), responsible: (_d = h === null || h === void 0 ? void 0 : h.responsible) !== null && _d !== void 0 ? _d : (this.autofillAuthor ? dataOwnerId : undefined), author: (_e = h === null || h === void 0 ? void 0 : h.author) !== null && _e !== void 0 ? _e : (this.autofillAuthor ? user.id : undefined), codes: (_f = h === null || h === void 0 ? void 0 : h.codes) !== null && _f !== void 0 ? _f : [], tags: (_g = h === null || h === void 0 ? void 0 : h.tags) !== null && _g !== void 0 ? _g : [], healthElementId: (_h = h === null || h === void 0 ? void 0 : h.healthElementId) !== null && _h !== void 0 ? _h : this.crypto.primitives.randomUuid(), openingDate: (_j = h === null || h === void 0 ? void 0 : h.openingDate) !== null && _j !== void 0 ? _j : parseInt((0, date_fns_1.format)(new Date(), 'yyyyMMddHHmmss')) });
            const ownerId = this.dataOwnerApi.getDataOwnerIdOf(user);
            if (ownerId !== (yield this.dataOwnerApi.getCurrentDataOwnerId()))
                throw new Error('Can only initialise entities as current data owner.');
            const sfk = yield this.crypto.xapi.resolveSecretIdUseOptions({ entity: patient, type: utils_1.EntityWithDelegationTypeName.Patient }, (_k = options.sfkOption) !== null && _k !== void 0 ? _k : SecretIdUseOption_1.SecretIdUseOption.UseAnySharedWithParent);
            const extraDelegations = Object.assign(Object.assign({}, (options.ignoreAutoDelegations == true
                ? {}
                : Object.fromEntries([...((_m = (_l = user.autoDelegations) === null || _l === void 0 ? void 0 : _l.all) !== null && _m !== void 0 ? _m : []), ...((_p = (_o = user.autoDelegations) === null || _o === void 0 ? void 0 : _o.medicalInformation) !== null && _p !== void 0 ? _p : [])].map((d) => [d, AccessLevelEnum.WRITE])))), ((_q = options === null || options === void 0 ? void 0 : options.additionalDelegates) !== null && _q !== void 0 ? _q : {}));
            const initialisationInfo = yield this.crypto.xapi.entityWithInitialisedEncryptedMetadata(helement, utils_1.EntityWithDelegationTypeName.HealthElement, patient.id, sfk, true, extraDelegations, options.alternateRootDelegation);
            return new models.HealthElement(initialisationInfo.updatedEntity);
        });
    }
    /**
     * @throws always. Use {@link createHealthElementWithUser} instead.
     */
    createHealthElement(body) {
        throw new Error('Cannot call a method that returns health elements without providing a user for de/encryption');
    }
    /**
     * Creates a health element after encrypting its content.
     * @param user the current user, used for encryption.
     * @param body the health element to create.
     * @return the created and decrypted health element.
     */
    createHealthElementWithUser(user, body) {
        return body
            ? this.encrypt(user, [(0, collection_utils_1.cloneDeep)(body)])
                .then((hes) => super.createHealthElement(hes[0]))
                .then((he) => this.decryptWithUser(user, [he]))
                .then((hes) => hes[0])
            : Promise.resolve(null);
    }
    /**
     * @throws always. Use {@link createHealthElementsWithUser} instead.
     */
    createHealthElements(body) {
        throw new Error('Cannot call a method that returns health elements without providing a user for de/encryption');
    }
    /**
     * Creates multiple health elements after encrypting their content.
     * @param user the current user, used for encryption.
     * @param bodies the health elements to create.
     * @return the created and decrypted health elements.
     */
    createHealthElementsWithUser(user, bodies) {
        return bodies
            ? this.encrypt(user, bodies.map((c) => (0, collection_utils_1.cloneDeep)(c)))
                .then((hes) => super.createHealthElements(hes))
                .then((hes) => this.decrypt(this.dataOwnerApi.getDataOwnerIdOf(user), hes))
            : Promise.resolve(null);
    }
    /**
     * @throws always. Use {@link getHealthElementWithUser} instead.
     */
    getHealthElement(healthElementId) {
        throw new Error('Cannot call a method that returns health element without providing a user for de/encryption');
    }
    /**
     * Retrieves a health element by id and decrypts it.
     * @param user the current user, used for decryption.
     * @param healthElementId the id of the health element to retrieve.
     * @return the decrypted health element.
     */
    getHealthElementWithUser(user, healthElementId) {
        return super
            .getHealthElement(healthElementId)
            .then((he) => this.decryptWithUser(user, [he]))
            .then((hes) => hes[0]);
    }
    /**
     * @throws always. Use {@link getHealthElementsWithUser} instead.
     */
    getHealthElements(body) {
        throw new Error('Cannot call a method that returns health elements without providing a user for de/encryption');
    }
    /**
     * Retrieves multiple health elements by their ids and decrypts them.
     * @param user the current user, used for decryption.
     * @param body the list of health element ids to retrieve.
     * @return the decrypted health elements.
     */
    getHealthElementsWithUser(user, body) {
        return super.getHealthElements(body).then((hes) => this.decrypt(this.dataOwnerApi.getDataOwnerIdOf(user), hes));
    }
    /**
     * @throws always.
     */
    newHealthElementDelegations(healthElementId, body) {
        throw new Error('Cannot call a method that returns health element without providing a user for de/encryption');
    }
    /**
     * @throws always. Use {@link findHealthElementsByHCPartyPatientForeignKeysWithUser} instead.
     */
    findHealthElementsByHCPartyPatientForeignKeys(hcPartyId, secretFKeys) {
        throw new Error('Cannot call a method that returns health element without providing a user for de/encryption');
    }
    /**
     * @deprecated use {@link findHealthElementIdsByDataOwnerPatientOpeningDate} instead.
     */
    findHealthElementsByHCPartyPatientForeignKeysWithUser(user, hcPartyId, secretFKeys) {
        return super.findHealthElementsByHCPartyPatientForeignKeys(hcPartyId, secretFKeys).then((hes) => this.decryptWithUser(user, hes));
    }
    /**
     * @deprecated use {@link findHealthElementIdsByDataOwnerPatientOpeningDate} instead.
     */
    findHealthElementsByHCPartyPatientForeignKeysArrayWithUser(user, hcPartyId, secretFKeys) {
        return super.findHealthElementsByHCPartyPatientForeignKeysUsingPost(hcPartyId, secretFKeys).then((hes) => this.decryptWithUser(user, hes));
    }
    /**
     * Finds all health elements for a given patient and healthcare party, and decrypts them.
     * @deprecated use {@link findHealthElementIdsByDataOwnerPatientOpeningDate} instead.
     * @param user the current user, used for decryption.
     * @param hcPartyId the id of the healthcare party.
     * @param patient the patient whose health elements to find.
     * @param usingPost if true, uses POST instead of GET for the request.
     * @return the decrypted health elements.
     */
    findHealthElementsByHCPartyAndPatientWithUser(user_1, hcPartyId_1, patient_1) {
        return __awaiter(this, arguments, void 0, function* (user, hcPartyId, patient, usingPost = false) {
            var _a;
            let keysAndHcPartyId = yield this.crypto.xapi.secretIdsForHcpHierarchyOf({ entity: patient, type: utils_1.EntityWithDelegationTypeName.Patient });
            const keys = (_a = keysAndHcPartyId.find((secretForeignKeys) => secretForeignKeys.ownerId == hcPartyId)) === null || _a === void 0 ? void 0 : _a.extracted;
            if (keys == undefined) {
                throw Error('No delegation for user');
            }
            return usingPost
                ? this.findHealthElementsByHCPartyPatientForeignKeysArrayWithUser(user, hcPartyId, keys)
                : this.findHealthElementsByHCPartyPatientForeignKeysWithUser(user, hcPartyId, keys.join(','));
        });
    }
    /**
     * @throws always. Use {@link modifyHealthElementWithUser} instead.
     */
    modifyHealthElement(body) {
        throw new Error('Cannot call a method that returns health element without providing a user for de/encryption');
    }
    /**
     * Modifies a health element after encrypting its content.
     * @param user the current user, used for encryption/decryption.
     * @param body the health element with updated fields.
     * @return the modified and decrypted health element, or null if body was not provided.
     */
    modifyHealthElementWithUser(user, body) {
        return body ? this.modifyHealthElementAs(this.dataOwnerApi.getDataOwnerIdOf(user), body) : Promise.resolve(null);
    }
    modifyHealthElementAs(dataOwner, body) {
        return this.encryptAs(dataOwner, [(0, collection_utils_1.cloneDeep)(body)])
            .then((hes) => super.modifyHealthElement(hes[0]))
            .then((he) => this.decrypt(dataOwner, [he]))
            .then((hes) => hes[0]);
    }
    /**
     * @throws always. Use {@link modifyHealthElementsWithUser} instead.
     */
    modifyHealthElements(body) {
        throw new Error('Cannot call a method that returns health elements without providing a user for de/encryption');
    }
    /**
     * Modifies multiple health elements after encrypting their content.
     * @param user the current user, used for encryption/decryption.
     * @param bodies the health elements with updated fields.
     * @return the modified and decrypted health elements, or null if bodies was not provided.
     */
    modifyHealthElementsWithUser(user, bodies) {
        return bodies
            ? this.encrypt(user, bodies.map((c) => (0, collection_utils_1.cloneDeep)(c)))
                .then((hes) => super.modifyHealthElements(hes))
                .then((hes) => this.decrypt(this.dataOwnerApi.getDataOwnerIdOf(user), hes))
            : Promise.resolve(null);
    }
    // noinspection JSUnusedGlobalSymbols
    /**
     * 1. Check whether there is a delegation with 'hcpartyId' or not.
     * 2. 'fetchHcParty[hcpartyId][1]': is encrypted AES exchange key by RSA public key of him.
     * 3. Obtain the AES exchange key, by decrypting the previous step value with hcparty private key
     *      3.1.  KeyPair should be fetch from cache (in jwk)
     *      3.2.  if it doesn't exist in the cache, it has to be loaded from Browser Local store, and then import it to WebCrypto
     * 4. Obtain the array of delegations which are delegated to his ID (hcpartyId) in this patient
     * 5. Decrypt and collect all keys (secretForeignKeys) within delegations of previous step (with obtained AES key of step 4)
     * 6. Do the REST call to get all helements with (allSecretForeignKeysDelimitedByComa, hcpartyId)
     *
     * After these painful steps, you have the helements of the patient.
     *
     * @deprecated use {@link findIdsBy}.
     * @param hcpartyId
     * @param patient (Promise)
     * @param keepObsoleteVersions
     * @param usingPost
     */
    findBy(hcpartyId, patient, keepObsoleteVersions = false, usingPost = false) {
        return this.crypto.xapi
            .secretIdsForHcpHierarchyOf({ entity: patient, type: utils_1.EntityWithDelegationTypeName.Patient })
            .then((secretForeignKeys) => secretForeignKeys && secretForeignKeys.length > 0
            ? Promise.all(secretForeignKeys
                .reduce((acc, level) => {
                return acc.concat([
                    {
                        hcpartyId: level.ownerId,
                        extractedKeys: level.extracted.filter((key) => !acc.some((previousLevel) => previousLevel.extractedKeys.includes(key))),
                    },
                ]);
            }, [])
                .filter((l) => l.extractedKeys.length > 0)
                .map(({ hcpartyId, extractedKeys }) => usingPost
                ? this.findByHCPartyPatientSecretFKeysArray(hcpartyId, [...new Set(extractedKeys)])
                : this.findByHCPartyPatientSecretFKeys(hcpartyId, [...new Set(extractedKeys)].join(',')))).then((results) => (0, collection_utils_1.uniqBy)(results.flat(), (x) => x.id))
            : Promise.resolve([]))
            .then((decryptedHelements) => {
            const byIds = {};
            if (keepObsoleteVersions) {
                return decryptedHelements;
            }
            else {
                decryptedHelements.forEach((he) => {
                    if (he.healthElementId) {
                        const phe = byIds[he.healthElementId];
                        if (!phe || !phe.modified || (he.modified && phe.modified < he.modified)) {
                            byIds[he.healthElementId] = he;
                        }
                    }
                });
                return Object.values(byIds).filter((s) => !s.endOfLife);
            }
        });
    }
    /**
     * Same as {@link findBy} but it will only return the ids of the health elements. It can also filter the health elements where HealthElement.openingDate is between
     * startDate and endDate in ascending or descending order by that field. (default: ascending).
     */
    findIdsBy(hcpartyId, patient, startDate, endDate, descending) {
        return __awaiter(this, void 0, void 0, function* () {
            return this.crypto.xapi.secretIdsForHcpHierarchyOf({ entity: patient, type: utils_1.EntityWithDelegationTypeName.Patient }).then((secretForeignKeys) => secretForeignKeys && secretForeignKeys.length > 0
                ? Promise.all(secretForeignKeys
                    .reduce((acc, level) => {
                    return acc.concat([
                        {
                            hcpartyId: level.ownerId,
                            extractedKeys: level.extracted.filter((key) => !acc.some((previousLevel) => previousLevel.extractedKeys.includes(key))),
                        },
                    ]);
                }, [])
                    .filter((l) => l.extractedKeys.length > 0)
                    .map(({ hcpartyId, extractedKeys }) => this.findHealthElementIdsByDataOwnerPatientOpeningDate(hcpartyId, extractedKeys, startDate, endDate, descending))).then((results) => [...new Set(results.flat())])
                : Promise.resolve([]));
        });
    }
    /**
     * @deprecated use {@link findHealthElementIdsByDataOwnerPatientOpeningDate} instead.
     */
    findByHCPartyPatientSecretFKeys(hcPartyId, secretFKeys) {
        return super.findHealthElementsByHCPartyPatientForeignKeys(hcPartyId, secretFKeys).then((helements) => this.decrypt(hcPartyId, helements));
    }
    /**
     * @deprecated use {@link findHealthElementIdsByDataOwnerPatientOpeningDate} instead.
     */
    findByHCPartyPatientSecretFKeysArray(hcPartyId, secretFKeys) {
        return super
            .findHealthElementsByHCPartyPatientForeignKeysUsingPost(hcPartyId, secretFKeys)
            .then((helements) => this.decrypt(hcPartyId, helements));
    }
    /**
     * Encrypts the encrypted fields of a list of health elements.
     * @param user the current user, used to determine the data owner for encryption.
     * @param healthElements the health elements to encrypt.
     * @return the encrypted health elements.
     */
    encrypt(user, healthElements) {
        return this.encryptAs(this.dataOwnerApi.getDataOwnerIdOf(user), healthElements);
    }
    encryptAs(owner, healthElements) {
        return this.crypto.xapi.tryEncryptEntities(healthElements, utils_1.EntityWithDelegationTypeName.HealthElement, this.encryptedFields, false, false, (x) => new models.HealthElement(x));
    }
    /**
     * Decrypts a list of health elements using the given user's data owner keys.
     * @param user the current user.
     * @param hes the health elements to decrypt.
     * @return the decrypted health elements.
     */
    decryptWithUser(user, hes) {
        return this.decrypt(this.dataOwnerApi.getDataOwnerIdOf(user), hes);
    }
    /**
     * Decrypts a list of health elements using the current data owner's keys.
     * @param dataOwnerId the id of the data owner performing the decryption.
     * @param hes the health elements to decrypt.
     * @return the decrypted health elements.
     */
    decrypt(dataOwnerId, hes) {
        return __awaiter(this, void 0, void 0, function* () {
            return (yield this.crypto.xapi.tryDecryptEntities(hes, utils_1.EntityWithDelegationTypeName.HealthElement, (x) => new models.HealthElement(x))).map(({ entity }) => entity);
        });
    }
    /**
     * @throws always. Use {@link filterByWithUser} instead.
     */
    filterHealthElementsBy(startDocumentId, limit, body) {
        throw new Error('Cannot call a method that returns health elements without providing a user for de/encryption');
    }
    filterByWithUser(user, startDocumentId, limit, body, collectTiming = false) {
        return super
            .filterHealthElementsBy(startDocumentId, limit, body, collectTiming)
            .then((pl) => this.decryptWithUser(user, pl.rows).then((dr) => Object.assign(pl, { rows: dr })));
    }
    /**
     * Converts a service into a new health element linked to the given patient.
     * @param user the current user.
     * @param patient the patient to link the health element to.
     * @param heSvc the service to convert.
     * @param descr the description for the new health element.
     * @return the created health element.
     */
    // noinspection JSUnusedGlobalSymbols
    serviceToHealthElement(user, patient, heSvc, descr) {
        return this.newInstance(user, patient, {
            idService: heSvc.id,
            author: heSvc.author,
            responsible: heSvc.responsible,
            openingDate: heSvc.valueDate || heSvc.openingDate,
            descr: descr,
            idOpeningContact: heSvc.contactId,
            modified: heSvc.modified,
            created: heSvc.created,
            codes: heSvc.codes,
            tags: heSvc.tags,
        }).then((he) => {
            return this.createHealthElement(he);
        });
    }
    /**
     * Parses a pipe-delimited code string into a Code object.
     * @param code the code string in format "type|code|version".
     * @return a Code object with the parsed fields.
     */
    // noinspection JSUnusedGlobalSymbols, JSMethodCanBeStatic
    stringToCode(code) {
        const c = code.split('|');
        return new models.Code({
            type: c[0],
            code: c[1],
            version: c[2],
            id: code,
        });
    }
    /**
     * @param healthElement a health element
     * @return the id of the patient that the health element refers to, retrieved from the encrypted metadata. Normally there should only be one element
     * in the returned array, but in case of entity merges there could be multiple values.
     */
    decryptPatientIdOf(healthElement) {
        return __awaiter(this, void 0, void 0, function* () {
            return this.crypto.xapi.owningEntityIdsOf({ entity: healthElement, type: utils_1.EntityWithDelegationTypeName.HealthElement }, undefined);
        });
    }
    /**
     * @return if the logged data owner has write access to the content of the given health element
     */
    hasWriteAccess(healthElement) {
        return __awaiter(this, void 0, void 0, function* () {
            return this.crypto.xapi.hasWriteAccess({ entity: healthElement, type: utils_1.EntityWithDelegationTypeName.HealthElement });
        });
    }
    /**
     * Share an existing health element with other data owners, allowing them to access the non-encrypted data of the health element and optionally also
     * the encrypted content, with read-only or read-write permissions.
     * @param delegateId the id of the data owner which will be granted access to the health element.
     * @param healthElement the health element to share.
     * @param options optional parameters to customize the sharing behaviour:
     * - shareSecretIds: specifies which secret ids of the entity should be shared. If not provided all secret ids available to the current user will be shared
     * - shareEncryptionKey: specifies if the encryption key of the access log should be shared with the delegate, giving access to all encrypted
     * content of the entity, excluding other encrypted metadata (defaults to {@link ShareMetadataBehaviour.IF_AVAILABLE}). Note that by default a
     * health element does not have encrypted content.
     * - sharePatientId: specifies if the id of the patient that this health element refers to should be shared with the delegate (defaults to
     * {@link ShareMetadataBehaviour.IF_AVAILABLE}).
     * - requestedPermissions: the requested permissions for the delegate, defaults to {@link RequestedPermissionEnum.MAX_WRITE}.
     * @return the updated entity
     */
    shareWith(delegateId_1, healthElement_1) {
        return __awaiter(this, arguments, void 0, function* (delegateId, healthElement, options = {}) {
            return this.shareWithMany(healthElement, { [delegateId]: options });
        });
    }
    /**
     * Share an existing health element with other data owners, allowing them to access the non-encrypted data of the health element and optionally also
     * the encrypted content, with read-only or read-write permissions.
     * @param delegateId the id of the data owner which will be granted access to the health element.
     * @param healthElement the health element to share.
     * @param delegates associates the id of data owners which will be granted access to the entity, to the following sharing options:
     * - shareSecretIds: specifies which secret ids of the entity should be shared. If not provided all secret ids available to the current user will be shared
     * - shareEncryptionKey: specifies if the encryption key of the access log should be shared with the delegate, giving access to all encrypted
     * content of the entity, excluding other encrypted metadata (defaults to {@link ShareMetadataBehaviour.IF_AVAILABLE}). Note that by default a
     * health element does not have encrypted content.
     * - sharePatientId: specifies if the id of the patient that this health element refers to should be shared with the delegate (defaults to
     * {@link ShareMetadataBehaviour.IF_AVAILABLE}).
     * - requestedPermissions: the requested permissions for the delegate, defaults to {@link RequestedPermissionEnum.MAX_WRITE}.
     * @return the updated entity
     */
    shareWithMany(healthElement, delegates) {
        return __awaiter(this, void 0, void 0, function* () {
            return (yield this.tryShareWithMany(healthElement, delegates)).updatedEntityOrThrow;
        });
    }
    /**
     * Share an existing health element with other data owners, allowing them to access the non-encrypted data of the health element and optionally also
     * the encrypted content, with read-only or read-write permissions.
     * @param healthElement the health element to share.
     * @param delegates associates the id of data owners which will be granted access to the entity, to the following sharing options:
     * - shareSecretIds: specifies which secret ids of the entity should be shared. If not provided all secret ids available to the current user will be shared
     * - shareEncryptionKey: specifies if the encryption key of the access log should be shared with the delegate, giving access to all encrypted
     * content of the entity, excluding other encrypted metadata (defaults to {@link ShareMetadataBehaviour.IF_AVAILABLE}). Note that by default a
     * health element does not have encrypted content.
     * - sharePatientId: specifies if the id of the patient that this health element refers to should be shared with the delegate (defaults to
     * {@link ShareMetadataBehaviour.IF_AVAILABLE}).
     * - requestedPermissions: the requested permissions for the delegate, defaults to {@link RequestedPermissionEnum.MAX_WRITE}.
     * @return a promise which will contain the result of the operation: the updated entity if the operation was successful or details of the error if
     * the operation failed.
     */
    tryShareWithMany(healthElement, delegates) {
        return __awaiter(this, void 0, void 0, function* () {
            const self = yield this.dataOwnerApi.getCurrentDataOwnerId();
            // All entities should have an encryption key.
            const entityWithEncryptionKey = yield this.crypto.xapi.ensureEncryptionKeysInitialised(healthElement, utils_1.EntityWithDelegationTypeName.HealthElement);
            const updatedEntity = entityWithEncryptionKey ? yield this.modifyHealthElementAs(self, entityWithEncryptionKey) : healthElement;
            return this.crypto.xapi
                .simpleShareOrUpdateEncryptedEntityMetadata({
                entity: updatedEntity,
                type: utils_1.EntityWithDelegationTypeName.HealthElement,
            }, Object.fromEntries(Object.entries(delegates).map(([delegateId, options]) => [
                delegateId,
                {
                    requestedPermissions: options.requestedPermissions,
                    shareEncryptionKeys: options.shareEncryptionKey,
                    shareOwningEntityIds: options.sharePatientId,
                    shareSecretIds: options.shareSecretIds,
                },
            ])), (x) => this.bulkShareHealthElements(x))
                .then((r) => r.mapSuccessAsync((e) => this.decrypt(self, [e]).then((es) => es[0])));
        });
    }
    /**
     * Retrieves the data owners that have access to the given health element, along with their access levels.
     * @param entity the health element.
     * @return an object containing a map of data owner ids to their access levels, and a flag indicating if there are unknown anonymous data owners.
     */
    getDataOwnersWithAccessTo(entity) {
        return this.crypto.delegationsDeAnonymization.getDataOwnersWithAccessTo({ entity, type: utils_1.EntityWithDelegationTypeName.HealthElement });
    }
    /**
     * Retrieves the encryption keys of the given health element.
     * @param entity the health element.
     * @return the encryption key ids.
     */
    getEncryptionKeysOf(entity) {
        return this.crypto.xapi.encryptionKeysOf({ entity, type: utils_1.EntityWithDelegationTypeName.HealthElement }, undefined);
    }
    /**
     * Subscribes to real-time health element events using a WebSocket connection. Received events are automatically decrypted.
     * @param eventTypes the types of events to listen for (e.g. 'CREATE', 'UPDATE', 'DELETE').
     * @param filter an optional filter to restrict which health element events trigger the callback.
     * @param eventFired the callback function invoked when a matching health element event is received.
     * @param options optional subscription configuration such as connection parameters and retry behaviour.
     * @return a connection object that can be used to manage the WebSocket subscription lifecycle.
     */
    subscribeToHealthElementEvents(eventTypes_1, filter_1, eventFired_1) {
        return __awaiter(this, arguments, void 0, function* (eventTypes, filter, eventFired, options = {}) {
            const currentUser = yield this.userApi.getCurrentUser();
            return yield (0, utils_1.subscribeToEntityEvents)(this.host, this.authApi, utils_1.EntityWithDelegationTypeName.HealthElement, eventTypes, filter, eventFired, options, (encrypted) => __awaiter(this, void 0, void 0, function* () { return (yield this.decrypt(this.dataOwnerApi.getDataOwnerIdOf(currentUser), [encrypted]))[0]; })).then((rs) => new Connection_1.ConnectionImpl(rs));
        });
    }
    /**
     * Creates or updates de-anonymization metadata for the given health element, allowing the specified delegates to
     * identify the data owners that have access to it.
     * @param entity the health element.
     * @param delegates the data owner ids for which to create de-anonymization metadata.
     */
    createDelegationDeAnonymizationMetadata(entity, delegates) {
        return this.crypto.delegationsDeAnonymization.createOrUpdateDeAnonymizationInfo({ entity, type: utils_1.EntityWithDelegationTypeName.HealthElement }, delegates);
    }
}
exports.IccHelementXApi = IccHelementXApi;
//# sourceMappingURL=icc-helement-x-api.js.map