"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());
    });
};
var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.IccCalendarItemXApi = void 0;
const i18n = require("./rsrc/contact.i18n");
const collection_utils_1 = require("./utils/collection-utils");
const models_1 = require("../icc-api/model/models");
const icc_api_1 = require("../icc-api");
const AuthenticationProvider_1 = require("./auth/AuthenticationProvider");
const EntityShareRequest_1 = require("../icc-api/model/requests/EntityShareRequest");
const SecureDelegation_1 = require("../icc-api/model/SecureDelegation");
const utils_1 = require("./utils");
var RequestedPermissionEnum = EntityShareRequest_1.EntityShareRequest.RequestedPermissionEnum;
var AccessLevelEnum = SecureDelegation_1.SecureDelegation.AccessLevelEnum;
const PaginatedListCalendarItem_1 = require("../icc-api/model/PaginatedListCalendarItem");
const SecretIdUseOption_1 = require("./crypto/SecretIdUseOption");
class IccCalendarItemXApi extends icc_api_1.IccCalendarItemApi {
    get headers() {
        return super.headers.then((h) => this.crypto.accessControlKeysHeaders.addAccessControlKeysHeaders(h, utils_1.EntityWithDelegationTypeName.CalendarItem));
    }
    constructor(host, headers, crypto, dataOwnerApi, autofillAuthor, encryptedKeys = ['details', 'title', 'patientId'], authenticationProvider = new AuthenticationProvider_1.NoAuthenticationProvider(), fetchImpl = typeof window !== 'undefined'
        ? window.fetch
        : typeof self !== 'undefined'
            ? self.fetch
            : fetch) {
        super(host, headers, authenticationProvider, fetchImpl);
        this.autofillAuthor = autofillAuthor;
        this.i18n = i18n;
        this.crypto = crypto;
        this.dataOwnerApi = dataOwnerApi;
        this.encryptedFields = (0, utils_1.parseEncryptedFields)(encryptedKeys, 'CalendarItem.');
    }
    /**
     * Creates a new instance of calendar item with initialised encryption metadata (not in the database), without
     * linking it to a patient. Use {@link newInstancePatient} to create a calendar item linked to a patient.
     * @param user the current user.
     * @param ci initialised data for the calendar item.
     * @param options optional parameters for delegates and root delegation.
     * @return a new instance of calendar item.
     */
    newInstance(user, ci, options = {}) {
        return this.newInstancePatient(user, null, ci, options);
    }
    /**
     * Creates a new instance of calendar item with initialised encryption metadata (not in the database).
     * @param user the current user.
     * @param patient the patient this calendar item refers to.
     * @param ci initialised data for the calendar item. 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.
     * - preferredSfk: secret id of the patient to use as the secret foreign key to use for the classcalendar itemification. The default value will be a
     * secret id of patient known by the topmost parent in the current data owner hierarchy.
     * - 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 calendar item.
     */
    newInstancePatient(user_1, patient_1, ci_1) {
        return __awaiter(this, arguments, void 0, function* (user, patient, ci, options = {}) {
            var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
            const calendarItem = Object.assign(Object.assign({}, (ci !== null && ci !== void 0 ? ci : {})), { _type: 'org.taktik.icure.entities.CalendarItem', id: (_a = ci === null || ci === void 0 ? void 0 : ci.id) !== null && _a !== void 0 ? _a : this.crypto.primitives.randomUuid(), created: (_b = ci === null || ci === void 0 ? void 0 : ci.created) !== null && _b !== void 0 ? _b : new Date().getTime(), modified: (_c = ci === null || ci === void 0 ? void 0 : ci.modified) !== null && _c !== void 0 ? _c : new Date().getTime(), responsible: (_d = ci === null || ci === void 0 ? void 0 : ci.responsible) !== null && _d !== void 0 ? _d : (this.autofillAuthor ? this.dataOwnerApi.getDataOwnerIdOf(user) : undefined), author: (_e = ci === null || ci === void 0 ? void 0 : ci.author) !== null && _e !== void 0 ? _e : (this.autofillAuthor ? user.id : undefined), codes: (_f = ci === null || ci === void 0 ? void 0 : ci.codes) !== null && _f !== void 0 ? _f : [], tags: (_g = ci === null || ci === void 0 ? void 0 : ci.tags) !== null && _g !== void 0 ? _g : [] });
            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 = patient != undefined
                ? yield this.crypto.xapi.resolveSecretIdUseOptions({ entity: patient, type: utils_1.EntityWithDelegationTypeName.Patient }, (_h = options.sfkOption) !== null && _h !== void 0 ? _h : SecretIdUseOption_1.SecretIdUseOption.UseAnySharedWithParent)
                : undefined;
            const extraDelegations = Object.assign(Object.assign({}, Object.fromEntries([...((_k = (_j = user.autoDelegations) === null || _j === void 0 ? void 0 : _j.all) !== null && _k !== void 0 ? _k : []), ...((_m = (_l = user.autoDelegations) === null || _l === void 0 ? void 0 : _l.medicalInformation) !== null && _m !== void 0 ? _m : [])].map((d) => [d, AccessLevelEnum.WRITE]))), ((_o = options === null || options === void 0 ? void 0 : options.additionalDelegates) !== null && _o !== void 0 ? _o : {}));
            return new models_1.CalendarItem(yield this.crypto.xapi
                .entityWithInitialisedEncryptedMetadata(calendarItem, utils_1.EntityWithDelegationTypeName.CalendarItem, patient === null || patient === void 0 ? void 0 : patient.id, sfk, true, extraDelegations, options.alternateRootDelegation)
                .then((x) => x.updatedEntity));
        });
    }
    /**
     * @deprecated use {@link findIdsBy} instead.
     */
    findBy(hcpartyId_1, patient_1) {
        return __awaiter(this, arguments, void 0, function* (hcpartyId, patient, usingPost = false) {
            const extractedKeys = yield this.crypto.xapi.secretIdsOf({ entity: patient, type: utils_1.EntityWithDelegationTypeName.Patient }, hcpartyId);
            const topmostParentId = (yield this.dataOwnerApi.getCurrentDataOwnerHierarchyIds())[0];
            return extractedKeys && extractedKeys.length > 0
                ? usingPost
                    ? this.findByHCPartyPatientSecretFKeysArray(hcpartyId, [...new Set(extractedKeys)])
                    : this.findByHCPartyPatientSecretFKeys(hcpartyId, [...new Set(extractedKeys)].join(','))
                : Promise.resolve([]);
        });
    }
    /**
     * Same as `findBy` but it will only return the ids of the calendar items. It can also filter the access logs where CalendarItem.date 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* () {
            const extractedKeys = yield this.crypto.xapi.secretIdsOf({ entity: patient, type: utils_1.EntityWithDelegationTypeName.Patient }, hcpartyId);
            return extractedKeys && extractedKeys.length > 0
                ? this.findCalendarItemIdsByDataOwnerPatientStartTime(hcpartyId, [...new Set(extractedKeys)], startDate, endDate, descending)
                : Promise.resolve([]);
        });
    }
    /**
     * @deprecated use {@link findCalendarItemIdsByDataOwnerPatientStartTime} instead.
     */
    findByHCPartyPatientSecretFKeys(hcPartyId, secretFKeys) {
        const _super = Object.create(null, {
            findCalendarItemsByHCPartyPatientForeignKeys: { get: () => super.findCalendarItemsByHCPartyPatientForeignKeys }
        });
        return __awaiter(this, void 0, void 0, function* () {
            const calendarItems = yield _super.findCalendarItemsByHCPartyPatientForeignKeys.call(this, hcPartyId, secretFKeys);
            return yield this.decrypt(hcPartyId, calendarItems);
        });
    }
    /**
     * @deprecated use {@link findCalendarItemIdsByDataOwnerPatientStartTime} instead.
     */
    findByHCPartyPatientSecretFKeysArray(hcPartyId, secretFKeys) {
        return super
            .findCalendarItemsByHCPartyPatientForeignKeysUsingPost(hcPartyId, secretFKeys)
            .then((calendarItems) => this.decrypt(hcPartyId, calendarItems));
    }
    /**
     * @throws always. Use {@link createCalendarItemWithHcParty} instead.
     */
    createCalendarItem(body) {
        throw new Error('Cannot call a method that must encrypt a calendar item without providing a user for de/encryption');
    }
    /**
     * Creates a calendar item after encrypting its content.
     * @param user the current user, used for encryption.
     * @param body the calendar item to create.
     * @return the created and decrypted calendar item.
     */
    createCalendarItemWithHcParty(user, body) {
        const _super = Object.create(null, {
            createCalendarItem: { get: () => super.createCalendarItem }
        });
        return __awaiter(this, void 0, void 0, function* () {
            return body
                ? this.encrypt(user, [(0, collection_utils_1.cloneDeep)(body)])
                    .then((items) => _super.createCalendarItem.call(this, items[0]))
                    .then((ci) => this.decrypt(this.dataOwnerApi.getDataOwnerIdOf(user), [ci]))
                    .then((cis) => cis[0])
                : null;
        });
    }
    /**
     * Retrieves a calendar item by id and decrypts it.
     * @param user the current user, used for decryption.
     * @param calendarItemId the id of the calendar item to retrieve.
     * @return the decrypted calendar item.
     */
    getCalendarItemWithUser(user, calendarItemId) {
        return super
            .getCalendarItem(calendarItemId)
            .then((calendarItem) => this.decrypt(this.dataOwnerApi.getDataOwnerIdOf(user), [calendarItem]))
            .then((cis) => cis[0]);
    }
    /**
     * @throws always. Use {@link getCalendarItemWithUser} instead.
     */
    getCalendarItem(calendarItemId) {
        throw new Error('Cannot call a method that must en/decrypt a calendar item without providing a user for de/encryption');
    }
    /**
     * @deprecated use {@link getCalendarItemsWithPaginationWithUser} instead.
     */
    getCalendarItemsWithUser(user) {
        const _super = Object.create(null, {
            getCalendarItems: { get: () => super.getCalendarItems }
        });
        return __awaiter(this, void 0, void 0, function* () {
            return _super.getCalendarItems.call(this).then((calendarItems) => this.decrypt(this.dataOwnerApi.getDataOwnerIdOf(user), calendarItems));
        });
    }
    /**
     * @throws always. Use {@link getCalendarItemsWithPaginationWithUser} instead.
     */
    getCalendarItems() {
        throw new Error('Cannot call a method that must en/decrypt a calendar item without providing a user for de/encryption');
    }
    /**
     * Retrieves calendar items with pagination and decrypts them.
     * @param user the current user, used for decryption.
     * @param startDocumentId the pagination start document id.
     * @param limit the maximum number of results to return.
     * @return a paginated list of decrypted calendar items.
     */
    getCalendarItemsWithPaginationWithUser(user, startDocumentId, limit) {
        const _super = Object.create(null, {
            getCalendarItemsWithPagination: { get: () => super.getCalendarItemsWithPagination }
        });
        return __awaiter(this, void 0, void 0, function* () {
            return _super.getCalendarItemsWithPagination.call(this, startDocumentId, limit).then((calendarItems) => {
                var _a;
                return this.decrypt(this.dataOwnerApi.getDataOwnerIdOf(user), (_a = calendarItems.rows) !== null && _a !== void 0 ? _a : []).then((decryptedItems) => new PaginatedListCalendarItem_1.PaginatedListCalendarItem({
                    rows: decryptedItems,
                    nextKeyPair: calendarItems.nextKeyPair,
                }));
            });
        });
    }
    /**
     * @throws always. Use {@link getCalendarItemsWithPaginationWithUser} instead.
     */
    getCalendarItemsWithPagination(startDocumentId, limit) {
        throw new Error('Cannot call a method that must en/decrypt a calendar item without providing a user for de/encryption');
    }
    /**
     * @deprecated use {@link findCalendarItemsByRecurrenceIdWithPaginationWithUser} instead.
     */
    findCalendarItemsByRecurrenceIdWithUser(user, recurrenceId) {
        const _super = Object.create(null, {
            findCalendarItemsByRecurrenceId: { get: () => super.findCalendarItemsByRecurrenceId }
        });
        return __awaiter(this, void 0, void 0, function* () {
            return _super.findCalendarItemsByRecurrenceId.call(this, recurrenceId)
                .then((calendarItems) => this.decrypt(this.dataOwnerApi.getDataOwnerIdOf(user), calendarItems));
        });
    }
    /**
     * @throws always. Use {@link findCalendarItemsByRecurrenceIdWithPaginationWithUser} instead.
     */
    findCalendarItemsByRecurrenceId(recurrenceId) {
        throw new Error('Cannot call a method that must en/decrypt a calendar item without providing a user for de/encryption');
    }
    /**
     * Finds calendar items by recurrence id with pagination and decrypts them.
     * @param user the current user, used for decryption.
     * @param recurrenceId the recurrence id to search for.
     * @param startKey the pagination start key.
     * @param startDocumentId the pagination start document id.
     * @param limit the maximum number of results to return.
     * @return a paginated list of decrypted calendar items matching the recurrence id.
     */
    findCalendarItemsByRecurrenceIdWithPaginationWithUser(user, recurrenceId, startKey, startDocumentId, limit) {
        const _super = Object.create(null, {
            findCalendarItemsByRecurrenceIdWithPagination: { get: () => super.findCalendarItemsByRecurrenceIdWithPagination }
        });
        return __awaiter(this, void 0, void 0, function* () {
            return _super.findCalendarItemsByRecurrenceIdWithPagination.call(this, recurrenceId, startKey, startDocumentId, limit).then((calendarItems) => {
                var _a;
                return this.decrypt(this.dataOwnerApi.getDataOwnerIdOf(user), (_a = calendarItems.rows) !== null && _a !== void 0 ? _a : []).then((decryptedItems) => new PaginatedListCalendarItem_1.PaginatedListCalendarItem({
                    rows: decryptedItems,
                    nextKeyPair: calendarItems.nextKeyPair,
                }));
            });
        });
    }
    /**
     * @throws always. Use {@link findCalendarItemsByRecurrenceIdWithPaginationWithUser} instead.
     */
    findCalendarItemsByRecurrenceIdWithPagination(recurrenceId, startKey, startDocumentId, limit) {
        throw new Error('Cannot call a method that must en/decrypt a calendar item without providing a user for de/encryption');
    }
    /**
     * Retrieves multiple calendar items by their ids and decrypts them.
     * @param user the current user, used for decryption.
     * @param body the list of calendar item ids to retrieve.
     * @return the decrypted calendar items.
     */
    getCalendarItemsWithIdsWithUser(user, body) {
        return super.getCalendarItemsWithIds(body).then((calendarItems) => this.decrypt(this.dataOwnerApi.getDataOwnerIdOf(user), calendarItems));
    }
    /**
     * @throws always. Use {@link getCalendarItemsWithIdsWithUser} instead.
     */
    getCalendarItemsWithIds(body) {
        throw new Error('Cannot call a method that must en/decrypt a calendar item without providing a user for de/encryption');
    }
    /**
     * Retrieves calendar items for a given healthcare party within a date range and decrypts them.
     * @param user the current user, used for decryption.
     * @param startDate the start of the date range.
     * @param endDate the end of the date range.
     * @param hcPartyId the id of the healthcare party.
     * @return the decrypted calendar items.
     */
    getCalendarItemsByPeriodAndHcPartyIdWithUser(user, startDate, endDate, hcPartyId) {
        return super
            .getCalendarItemsByPeriodAndHcPartyId(startDate, endDate, hcPartyId)
            .then((calendarItems) => this.decrypt(this.dataOwnerApi.getDataOwnerIdOf(user), calendarItems));
    }
    /**
     * @throws always. Use {@link getCalendarItemsByPeriodAndHcPartyIdWithUser} instead.
     */
    getCalendarItemsByPeriodAndHcPartyId(startDate, endDate, hcPartyId) {
        throw new Error('Cannot call a method that must en/decrypt a calendar item without providing a user for de/encryption');
    }
    /**
     * Retrieves calendar items for a given agenda within a date range and decrypts them.
     * @param user the current user, used for decryption.
     * @param startDate the start of the date range.
     * @param endDate the end of the date range.
     * @param agendaId the id of the agenda.
     * @return the decrypted calendar items.
     */
    getCalendarsByPeriodAndAgendaIdWithUser(user, startDate, endDate, agendaId) {
        return super
            .getCalendarsByPeriodAndAgendaId(startDate, endDate, agendaId)
            .then((calendarItems) => this.decrypt(this.dataOwnerApi.getDataOwnerIdOf(user), calendarItems));
    }
    /**
     * @throws always. Use {@link getCalendarsByPeriodAndAgendaIdWithUser} instead.
     */
    getCalendarsByPeriodAndAgendaId(startDate, endDate, agendaId) {
        throw new Error('Cannot call a method that must en/decrypt a calendar item without providing a user for de/encryption');
    }
    /**
     * @throws always. Use {@link modifyCalendarItemWithHcParty} instead.
     */
    modifyCalendarItem(body) {
        throw new Error('Cannot call a method that must encrypt a calendar item without providing a user for de/encryption');
    }
    /**
     * Remove the following delegation objects from the
     * CalendarItem instance: cryptedForeignKeys, secretForeignKeys.
     *
     * The delegations & encryptionKeys objects are not removed because
     * in the case the CalendarItem is saved in the DB & then encrypted,
     * if later we remove the patient from it, it'd reset the delegations
     * and encryptionKeys thus impossibilitating further access.
     *
     * @param calendarItem The Calendar Item object
     */
    resetCalendarDelegationObjects(calendarItem) {
        const { cryptedForeignKeys, secretForeignKeys } = calendarItem, resetCalendarItem = __rest(calendarItem, ["cryptedForeignKeys", "secretForeignKeys"]);
        return resetCalendarItem;
    }
    /**
     * Modifies a calendar item after encrypting its content.
     * @param user the current user, used for encryption/decryption.
     * @param body the calendar item with updated fields.
     * @return the modified and decrypted calendar item, or null if body was not provided.
     */
    modifyCalendarItemWithHcParty(user, body) {
        return __awaiter(this, void 0, void 0, function* () {
            return body ? this.modifyAs(this.dataOwnerApi.getDataOwnerIdOf(user), (0, collection_utils_1.cloneDeep)(body)) : null;
        });
    }
    modifyAs(dataOwner, body) {
        return this.encryptAs(dataOwner, [(0, collection_utils_1.cloneDeep)(body)])
            .then((items) => super.modifyCalendarItem(items[0]))
            .then((ci) => this.decrypt(dataOwner, [ci]))
            .then((cis) => cis[0]);
    }
    /**
     * Encrypts the encrypted fields of a list of calendar items.
     * @param user the current user, used to determine the data owner for encryption.
     * @param calendarItems the calendar items to encrypt.
     * @return the encrypted calendar items.
     */
    encrypt(user, calendarItems) {
        return this.encryptAs(this.dataOwnerApi.getDataOwnerIdOf(user), calendarItems);
    }
    encryptAs(dataOwner, calendarItems) {
        return this.crypto.xapi.tryEncryptEntities(calendarItems, utils_1.EntityWithDelegationTypeName.CalendarItem, this.encryptedFields, false, false, (json) => new models_1.CalendarItem(json));
    }
    /**
     * Decrypts a list of calendar items using the current data owner's keys.
     * @param hcpId the id of the healthcare party performing the decryption.
     * @param calendarItems the calendar items to decrypt.
     * @return the decrypted calendar items.
     */
    decrypt(hcpId, calendarItems) {
        return __awaiter(this, void 0, void 0, function* () {
            return (yield this.crypto.xapi.tryDecryptEntities(calendarItems, utils_1.EntityWithDelegationTypeName.CalendarItem, (json) => new models_1.CalendarItem(json))).map(({ entity }) => entity);
        });
    }
    /**
     * @param calendarItem a calendar item
     * @return the id of the patient that the calendar item 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(calendarItem) {
        return __awaiter(this, void 0, void 0, function* () {
            return this.crypto.xapi.owningEntityIdsOf({ entity: calendarItem, type: utils_1.EntityWithDelegationTypeName.CalendarItem }, undefined);
        });
    }
    /**
     * @return if the logged data owner has write access to the content of the given calendar item
     */
    hasWriteAccess(calendarItem) {
        return __awaiter(this, void 0, void 0, function* () {
            return this.crypto.xapi.hasWriteAccess({ entity: calendarItem, type: utils_1.EntityWithDelegationTypeName.CalendarItem });
        });
    }
    /**
     * Share an existing calendar item with other data owners, allowing them to access the non-encrypted data of the calendar item 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 calendar item.
     * @param calendarItem item the calendar item 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
     * calendar item does not have encrypted content.
     * - sharePatientId: specifies if the id of the patient that this calendar item 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.
     */
    shareWith(delegateId_1, calendarItem_1) {
        return __awaiter(this, arguments, void 0, function* (delegateId, calendarItem, options = {}) {
            return this.shareWithMany(calendarItem, { [delegateId]: options });
        });
    }
    /**
     * Share an existing calendar item with other data owners, allowing them to access the non-encrypted data of the calendar item and optionally also
     * the encrypted content, with read-only or read-write permissions.
     * @param calendarItem item the calendar item 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
     * calendar item does not have encrypted content.
     * - sharePatientId: specifies if the id of the patient that this calendar item 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(calendarItem, delegates) {
        return __awaiter(this, void 0, void 0, function* () {
            return (yield this.tryShareWithMany(calendarItem, delegates)).updatedEntityOrThrow;
        });
    }
    /**
     * Share an existing calendar item with other data owners, allowing them to access the non-encrypted data of the calendar item and optionally also
     * the encrypted content, with read-only or read-write permissions.
     * @param calendarItem item the calendar item 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
     * calendar item does not have encrypted content.
     * - sharePatientId: specifies if the id of the patient that this calendar item 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
     */
    tryShareWithMany(calendarItem, 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(calendarItem, utils_1.EntityWithDelegationTypeName.CalendarItem);
            const updatedEntity = entityWithEncryptionKey ? yield this.modifyAs(self, entityWithEncryptionKey) : calendarItem;
            return this.crypto.xapi
                .simpleShareOrUpdateEncryptedEntityMetadata({
                entity: updatedEntity,
                type: utils_1.EntityWithDelegationTypeName.CalendarItem,
            }, Object.fromEntries(Object.entries(delegates).map(([delegateId, options]) => [
                delegateId,
                {
                    requestedPermissions: options.requestedPermissions,
                    shareEncryptionKeys: options.shareEncryptionKey,
                    shareOwningEntityIds: options.sharePatientId,
                    shareSecretIds: options.shareSecretIds,
                },
            ])), (x) => this.bulkShareCalendarItems(x))
                .then((r) => r.mapSuccessAsync((e) => this.decrypt(self, [e]).then((es) => es[0])));
        });
    }
    /**
     * Retrieves the data owners that have access to the given calendar item, along with their access levels.
     * @param entity the calendar item.
     * @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.CalendarItem });
    }
    /**
     * Retrieves the encryption keys of the given calendar item.
     * @param entity the calendar item.
     * @return the encryption key ids.
     */
    getEncryptionKeysOf(entity) {
        return this.crypto.xapi.encryptionKeysOf({ entity, type: utils_1.EntityWithDelegationTypeName.CalendarItem }, undefined);
    }
    /**
     * Adds a bidirectional link between a calendar item with a patient.
     * Note that this operation is not reversible: it is not possible to change the patient linked to a calendar
     * item.
     * @param calendarItem a calendar item
     * @param patient the patient which will be linked to the calendar item.
     * @param shareLinkWithDelegates data owners other than the current data owner which will also be able to decrypt the id of the newly linked
     * patient. If any of these data owners do not already have access to the calendar item, they will be granted read access (no write).
     * @param sfkOption specify how to create the link from the patient
     * @return the updated calendar item
     */
    linkWithPatient(calendarItem, patient, shareLinkWithDelegates, sfkOption) {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.hasLinkFromPatient(calendarItem))
                throw new Error(`Calendar item ${calendarItem.id} is already linked to a patient`);
            const delegates = [...new Set([yield this.dataOwnerApi.getCurrentDataOwnerId(), ...shareLinkWithDelegates])];
            const sfk = yield this.crypto.xapi.resolveSecretIdUseOptions({ entity: patient, type: utils_1.EntityWithDelegationTypeName.Patient }, sfkOption !== null && sfkOption !== void 0 ? sfkOption : SecretIdUseOption_1.SecretIdUseOption.UseAnySharedWithParent);
            const individualShareData = {
                shareSecretIds: [],
                shareEncryptionKeys: [],
                shareOwningEntityIds: [patient.id],
                requestedPermissions: RequestedPermissionEnum.FULL_READ,
            };
            const shared = yield this.crypto.xapi.bulkShareOrUpdateEncryptedEntityMetadata(utils_1.EntityWithDelegationTypeName.CalendarItem, [
                {
                    entity: calendarItem,
                    dataForDelegates: Object.fromEntries(delegates.map((d) => [d, individualShareData])),
                },
            ], (x) => this.bulkShareCalendarItems(x));
            if (!shared.updatedEntities.length || shared.updatedEntities[0].id !== calendarItem.id) {
                const errorsForEntity = shared.updateErrors.filter((e) => e.entityId === calendarItem.id);
                if (!errorsForEntity.length || !errorsForEntity.find((x) => x.code === 409)) {
                    throw new Error(`Unexpected error while linking calendar item ${calendarItem.id}`);
                }
                else {
                    throw new Error(`Outdated calendar item revision ${calendarItem.id}-${calendarItem.rev}`);
                }
            }
            const self = yield this.dataOwnerApi.getCurrentDataOwnerId();
            const sharedDecrypted = (yield this.decrypt(self, [shared.updatedEntities[0]]))[0];
            if (sfk.length > 0) {
                return yield this.modifyAs(self, Object.assign(Object.assign({}, sharedDecrypted), { secretForeignKeys: sfk }));
            }
            else {
                return sharedDecrypted;
            }
        });
    }
    /**
     * Checks if a calendar item has an (encrypted) link from a patient.
     * Calendar item where this method returns false won't be findable when searching for them by patient.
     * If this method returns false there may still be a link to the patient; you can check for it by using the method
     * {@link decryptPatientIdOf}.
     *
     * Note: this method only checks the presence of a link, it doesn't verify that it is valid (for example, this method
     * doesn't check if the link from the patient matches the link to the patient).
     */
    hasLinkFromPatient(calendarItem) {
        var _a;
        return ((_a = calendarItem.secretForeignKeys) !== null && _a !== void 0 ? _a : []).length > 0;
    }
    /**
     * Adds a link to a calendar item allowing it to be found from the patient.
     * This method only adds a partial link; if the calendar item is not already linked to the patient you should
     * instead use the {@link linkWithPatient}
     * @param calendarItem the calendar item to update.
     * @param patient the patient to link with the calendar item.
     * @param sfkOption customize how to create the link.
     * @return the updated calendar item
     */
    addLinkFromPatient(calendarItem, patient, sfkOption) {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.hasLinkFromPatient(calendarItem))
                throw new Error(`Calendar item ${calendarItem.id} is already linked to a patient`);
            const self = yield this.dataOwnerApi.getCurrentDataOwnerId();
            const sfk = yield this.crypto.xapi.resolveSecretIdUseOptions({ entity: patient, type: utils_1.EntityWithDelegationTypeName.Patient }, sfkOption !== null && sfkOption !== void 0 ? sfkOption : SecretIdUseOption_1.SecretIdUseOption.UseAnySharedWithParent);
            if (sfk.length > 0) {
                return yield this.modifyAs(self, Object.assign(Object.assign({}, calendarItem), { secretForeignKeys: sfk }));
            }
            else {
                return calendarItem;
            }
        });
    }
    /**
     * Creates or updates de-anonymization metadata for the given calendar item, allowing the specified delegates to
     * identify the data owners that have access to it.
     * @param entity the calendar item.
     * @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.CalendarItem }, delegates);
    }
}
exports.IccCalendarItemXApi = IccCalendarItemXApi;
//# sourceMappingURL=icc-calendar-item-x-api.js.map