"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.IccIcureMaintenanceXApi = void 0;
const MaintenanceTask_1 = require("../icc-api/model/MaintenanceTask");
const utils_1 = require("./utils");
const PropertyStub_1 = require("../icc-api/model/PropertyStub");
const PropertyTypeStub_1 = require("../icc-api/model/PropertyTypeStub");
const TypedValueObject_1 = require("../icc-api/model/TypedValueObject");
const KeyPairUpdateRequest_1 = require("./maintenance/KeyPairUpdateRequest");
const SecureDelegation_1 = require("../icc-api/model/SecureDelegation");
const DataOwnerTypeEnum_1 = require("../icc-api/model/DataOwnerTypeEnum");
var AccessLevelEnum = SecureDelegation_1.SecureDelegation.AccessLevelEnum;
const utils_2 = require("./crypto/utils");
/**
 * Api for interpreting maintenance tasks and applying required client-side actions.
 */
class IccIcureMaintenanceXApi {
    constructor(crypto, tasksApi, dataOwnerApi, exchangeDataApi) {
        this.crypto = crypto;
        this.tasksApi = tasksApi;
        this.dataOwnerApi = dataOwnerApi;
        this.exchangeDataApi = exchangeDataApi;
    }
    // TODO api to get all tasks for current owner from owner with id
    /**
     * Applies a key pair update request between another data owner and the current data owner to allow the other data owner to access existing exchange
     * keys shared with the current data owner. IMPORTANT: it is your responsibility to verify the authenticity of the public key / update request
     * before calling this method: this method assumes the new public key for the concerned data owner is authentic.
     * @param update a keypair update request to the current data owner.
     */
    applyKeyPairUpdate(update) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.crypto.exchangeKeys.base.giveAccessBackTo(update.concernedDataOwnerId, update.newPublicKey, this.crypto.userKeysManager.getDecryptionKeys());
            yield this.crypto.exchangeData.giveAccessBackTo(update.concernedDataOwnerId, update.newPublicKey);
        });
    }
    /**
     * @internal This method is intended only for internal use and may be changed without notice.
     * Creates the necessary maintenance tasks to request access to existing exchange keys with the new key pair for the current user.
     * @param user the user which owns the new key pair.
     * @param keypair a new key pair for the current user.
     * @param requestToOwnerTypes specifies the types of data owner that have shared data with the current data owner or which were given access to data
     * from the current data owner will receive a 'give access back' request. If not specified, the value be inferred from the current data owner type.
     */
    createMaintenanceTasksForNewKeypair(user, keypair, requestToOwnerTypes) {
        return __awaiter(this, void 0, void 0, function* () {
            const currentUserType = yield this.dataOwnerApi.getCurrentDataOwnerType();
            if (!requestToOwnerTypes) {
                if (currentUserType === 'device') {
                    console.warn('Current data owner is a device and there is no need to create maintenance tasks for updated keypair.');
                    return;
                }
                else {
                    requestToOwnerTypes =
                        currentUserType === DataOwnerTypeEnum_1.DataOwnerTypeEnum.Patient ? [DataOwnerTypeEnum_1.DataOwnerTypeEnum.Patient, DataOwnerTypeEnum_1.DataOwnerTypeEnum.Hcp] : [DataOwnerTypeEnum_1.DataOwnerTypeEnum.Hcp];
                }
            }
            const hexNewPubKey = (0, utils_1.ua2hex)(yield this.crypto.primitives.RSA.exportKey(keypair.publicKey, 'spki'));
            const hexNewPubKeyFp = (0, utils_2.fingerprintV1)(hexNewPubKey);
            const selfId = yield this.dataOwnerApi.getCurrentDataOwnerId();
            const requestDataOwnersForExchangeKeys = (yield this.getExchangeKeysInfosOf(selfId, requestToOwnerTypes))
                .filter((info) => !info.fingerprints.has(hexNewPubKeyFp))
                .flatMap((info) => [info.delegate, info.delegator])
                .filter((dataOwner) => dataOwner !== selfId);
            const requestDataOwnersForExchangeData = yield this.exchangeDataApi.getExchangeDataParticipantCounterparts(selfId, [...new Set(requestToOwnerTypes)].join(','));
            const requestDataOwners = [...new Set([...requestDataOwnersForExchangeKeys, ...requestDataOwnersForExchangeData])];
            if (requestDataOwners.length > 0) {
                const tasksToCreate = requestDataOwners.map((dataOwner) => ({
                    delegate: dataOwner,
                    task: this.createMaintenanceTask(selfId, hexNewPubKey),
                }));
                for (const taskToCreate of tasksToCreate) {
                    const instance = yield this.tasksApi.newInstance(user, taskToCreate.task, {
                        additionalDelegates: { [taskToCreate.delegate]: AccessLevelEnum.WRITE },
                    });
                    if (instance) {
                        // TODO create in bulk
                        yield this.tasksApi.createMaintenanceTaskWithUser(user, instance);
                    }
                }
            }
        });
    }
    getExchangeKeysInfosOf(dataOwnerId, otherOwnerTypes) {
        return __awaiter(this, void 0, void 0, function* () {
            const allExchangeKeys = yield this.crypto.exchangeKeys.base.getAllExchangeKeysWith(dataOwnerId, otherOwnerTypes);
            const infoTo = Object.entries(allExchangeKeys.keysToOwner).flatMap(([delegatorId, delegatorFpToKeys]) => Object.values(delegatorFpToKeys).map((encryptedKeys) => ({
                delegator: delegatorId,
                delegate: dataOwnerId,
                fingerprints: new Set(Object.keys(encryptedKeys).map((x) => (0, utils_2.fingerprintV1)(x))),
            })));
            const infoFrom = Object.values(allExchangeKeys.keysFromOwner).flatMap((delegateIdToKeys) => Object.entries(delegateIdToKeys).map(([delegateId, encryptedKeys]) => ({
                delegator: dataOwnerId,
                delegate: delegateId,
                fingerprints: new Set(Object.keys(encryptedKeys).map((x) => (0, utils_2.fingerprintV1)(x))),
            })));
            return [...infoFrom, ...infoTo];
        });
    }
    createMaintenanceTask(ownerId, concernedPubKey) {
        return new MaintenanceTask_1.MaintenanceTask({
            id: this.crypto.primitives.randomUuid(),
            taskType: KeyPairUpdateRequest_1.KeyPairUpdateRequest.TASK_TYPE,
            status: MaintenanceTask_1.MaintenanceTask.StatusEnum.Pending,
            properties: [
                new PropertyStub_1.PropertyStub({
                    id: KeyPairUpdateRequest_1.KeyPairUpdateRequest.OWNER_ID_PROP_ID,
                    type: new PropertyTypeStub_1.PropertyTypeStub({ type: PropertyTypeStub_1.PropertyTypeStub.TypeEnum.STRING }),
                    typedValue: new TypedValueObject_1.TypedValueObject({
                        type: TypedValueObject_1.TypedValueObject.TypeEnum.STRING,
                        stringValue: ownerId,
                    }),
                }),
                new PropertyStub_1.PropertyStub({
                    id: KeyPairUpdateRequest_1.KeyPairUpdateRequest.OWNER_PUB_PROP_ID,
                    type: new PropertyTypeStub_1.PropertyTypeStub({ type: PropertyTypeStub_1.PropertyTypeStub.TypeEnum.STRING }),
                    typedValue: new TypedValueObject_1.TypedValueObject({
                        type: TypedValueObject_1.TypedValueObject.TypeEnum.STRING,
                        stringValue: concernedPubKey,
                    }),
                }),
            ],
        });
    }
}
exports.IccIcureMaintenanceXApi = IccIcureMaintenanceXApi;
//# sourceMappingURL=icc-icure-maintenance-x-api.js.map