"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        if (typeof b !== "function" && b !== null)
            throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
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 __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
var __values = (this && this.__values) || function(o) {
    var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
    if (m) return m.call(o);
    if (o && typeof o.length === "number") return {
        next: function () {
            if (o && i >= o.length) o = void 0;
            return { value: o && o[i++], done: !o };
        }
    };
    throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __read = (this && this.__read) || function (o, n) {
    var m = typeof Symbol === "function" && o[Symbol.iterator];
    if (!m) return o;
    var i = m.call(o), r, ar = [], e;
    try {
        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
    }
    catch (error) { e = { error: error }; }
    finally {
        try {
            if (r && !r.done && (m = i["return"])) m.call(i);
        }
        finally { if (e) throw e.error; }
    }
    return ar;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
    if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
        if (ar || !(i in from)) {
            if (!ar) ar = Array.prototype.slice.call(from, 0, i);
            ar[i] = from[i];
        }
    }
    return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RecordsWrite = void 0;
var encoder_js_1 = require("../../../utils/encoder.js");
var encryption_js_1 = require("../../../utils/encryption.js");
var encryption_js_2 = require("../../../utils/encryption.js");
var signer_js_1 = require("../../../jose/jws/general/signer.js");
var time_js_1 = require("../../../utils/time.js");
var jws_js_1 = require("../../../utils/jws.js");
var message_js_1 = require("../../../core/message.js");
var protocol_authorization_js_1 = require("../../../core/protocol-authorization.js");
var records_js_1 = require("../../../utils/records.js");
var object_js_1 = require("../../../utils/object.js");
var secp256k1_js_1 = require("../../../utils/secp256k1.js");
var auth_js_1 = require("../../../core/auth.js");
var cid_js_1 = require("../../../utils/cid.js");
var dwn_error_js_1 = require("../../../core/dwn-error.js");
var message_js_2 = require("../../../core/message.js");
var url_js_1 = require("../../../utils/url.js");
var RecordsWrite = /** @class */ (function (_super) {
    __extends(RecordsWrite, _super);
    function RecordsWrite(message) {
        var _this = _super.call(this, message) || this;
        _this.attesters = RecordsWrite.getAttesters(message);
        return _this;
        // consider converting isInitialWrite() & getEntryId() into properties for performance and convenience
    }
    RecordsWrite.parse = function (message) {
        return __awaiter(this, void 0, void 0, function () {
            var recordsWrite;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: 
                    // asynchronous checks that are required by the constructor to initialize members properly
                    return [4 /*yield*/, (0, auth_js_1.validateAuthorizationIntegrity)(message, { allowedProperties: new Set(['recordId', 'contextId', 'attestationCid', 'encryptionCid']) })];
                    case 1:
                        // asynchronous checks that are required by the constructor to initialize members properly
                        _a.sent();
                        return [4 /*yield*/, RecordsWrite.validateAttestationIntegrity(message)];
                    case 2:
                        _a.sent();
                        recordsWrite = new RecordsWrite(message);
                        return [4 /*yield*/, recordsWrite.validateIntegrity()];
                    case 3:
                        _a.sent(); // RecordsWrite specific data integrity check
                        return [2 /*return*/, recordsWrite];
                }
            });
        });
    };
    /**
     * Creates a RecordsWrite message.
     * @param options.recordId If `undefined`, will be auto-filled as a originating message as convenience for developer.
     * @param options.data Data used to compute the `dataCid`, must be the encrypted data bytes if `options.encryptionInput` is given.
     *                     Must specify `options.dataCid` if `undefined`.
     * @param options.dataCid CID of the data that is already stored in the DWN. Must specify `options.data` if `undefined`.
     * @param options.dataSize Size of data in number of bytes. Must be defined if `options.dataCid` is defined; must be `undefined` otherwise.
     * @param options.dateCreated If `undefined`, it will be auto-filled with current time.
     * @param options.dateModified If `undefined`, it will be auto-filled with current time.
     */
    RecordsWrite.create = function (options) {
        var _a, _b, _c, _d, _e;
        return __awaiter(this, void 0, void 0, function () {
            var dataCid, _f, dataSize, currentTime, descriptor, author, recordId, _g, contextId, descriptorCid, attestation, encryption, authorization, message;
            return __generator(this, function (_h) {
                switch (_h.label) {
                    case 0:
                        if ((options.protocol === undefined && options.protocolPath !== undefined) ||
                            (options.protocol !== undefined && options.protocolPath === undefined)) {
                            throw new Error('`protocol` and `protocolPath` must both be defined or undefined at the same time');
                        }
                        if ((options.data === undefined && options.dataCid === undefined) ||
                            (options.data !== undefined && options.dataCid !== undefined)) {
                            throw new Error('one and only one parameter between `data` and `dataCid` is allowed');
                        }
                        if ((options.dataCid === undefined && options.dataSize !== undefined) ||
                            (options.dataCid !== undefined && options.dataSize === undefined)) {
                            throw new Error('`dataCid` and `dataSize` must both be defined or undefined at the same time');
                        }
                        if (!((_a = options.dataCid) !== null && _a !== void 0)) return [3 /*break*/, 1];
                        _f = _a;
                        return [3 /*break*/, 3];
                    case 1: return [4 /*yield*/, cid_js_1.Cid.computeDagPbCidFromBytes(options.data)];
                    case 2:
                        _f = _h.sent();
                        _h.label = 3;
                    case 3:
                        dataCid = _f;
                        dataSize = (_b = options.dataSize) !== null && _b !== void 0 ? _b : options.data.length;
                        currentTime = (0, time_js_1.getCurrentTimeInHighPrecision)();
                        descriptor = {
                            interface: message_js_2.DwnInterfaceName.Records,
                            method: message_js_2.DwnMethodName.Write,
                            protocol: options.protocol !== undefined ? (0, url_js_1.normalizeProtocolUrl)(options.protocol) : undefined,
                            protocolPath: options.protocolPath,
                            recipient: options.recipient,
                            schema: options.schema !== undefined ? (0, url_js_1.normalizeSchemaUrl)(options.schema) : undefined,
                            parentId: options.parentId,
                            dataCid: dataCid,
                            dataSize: dataSize,
                            dateCreated: (_c = options.dateCreated) !== null && _c !== void 0 ? _c : currentTime,
                            dateModified: (_d = options.dateModified) !== null && _d !== void 0 ? _d : currentTime,
                            published: options.published,
                            datePublished: options.datePublished,
                            dataFormat: options.dataFormat
                        };
                        // generate `datePublished` if the message is to be published but `datePublished` is not given
                        if (options.published === true &&
                            options.datePublished === undefined) {
                            descriptor.datePublished = currentTime;
                        }
                        // delete all descriptor properties that are `undefined` else the code will encounter the following IPLD issue when attempting to generate CID:
                        // Error: `undefined` is not supported by the IPLD Data Model and cannot be encoded
                        (0, object_js_1.removeUndefinedProperties)(descriptor);
                        author = jws_js_1.Jws.extractDid(options.authorizationSignatureInput.protectedHeader.kid);
                        if (!((_e = options.recordId) !== null && _e !== void 0)) return [3 /*break*/, 4];
                        _g = _e;
                        return [3 /*break*/, 6];
                    case 4: return [4 /*yield*/, RecordsWrite.getEntryId(author, descriptor)];
                    case 5:
                        _g = _h.sent();
                        _h.label = 6;
                    case 6:
                        recordId = _g;
                        if (!(options.contextId !== undefined)) return [3 /*break*/, 7];
                        contextId = options.contextId;
                        return [3 /*break*/, 9];
                    case 7:
                        if (!(descriptor.protocol !== undefined)) return [3 /*break*/, 9];
                        return [4 /*yield*/, RecordsWrite.getEntryId(author, descriptor)];
                    case 8:
                        contextId = _h.sent();
                        _h.label = 9;
                    case 9: return [4 /*yield*/, (0, cid_js_1.computeCid)(descriptor)];
                    case 10:
                        descriptorCid = _h.sent();
                        return [4 /*yield*/, RecordsWrite.createAttestation(descriptorCid, options.attestationSignatureInputs)];
                    case 11:
                        attestation = _h.sent();
                        return [4 /*yield*/, RecordsWrite.createEncryptionProperty(recordId, contextId, descriptor, options.encryptionInput)];
                    case 12:
                        encryption = _h.sent();
                        return [4 /*yield*/, RecordsWrite.createAuthorization(recordId, contextId, descriptorCid, attestation, encryption, options.authorizationSignatureInput)];
                    case 13:
                        authorization = _h.sent();
                        message = {
                            recordId: recordId,
                            descriptor: descriptor,
                            authorization: authorization
                        };
                        if (contextId !== undefined) {
                            message.contextId = contextId;
                        } // assign `contextId` only if it is defined
                        if (attestation !== undefined) {
                            message.attestation = attestation;
                        } // assign `attestation` only if it is defined
                        if (encryption !== undefined) {
                            message.encryption = encryption;
                        } // assign `encryption` only if it is defined
                        message_js_1.Message.validateJsonSchema(message);
                        return [2 /*return*/, new RecordsWrite(message)];
                }
            });
        });
    };
    /**
     * Convenience method that creates a message by:
     * 1. Copying over immutable properties from the given unsigned message
     * 2. Copying over mutable properties that are not overwritten from the given unsigned message
     * 3. Replace the mutable properties that are given new value
     * @param options.unsignedRecordsWriteMessage Unsigned message that the new RecordsWrite will be based from.
     * @param options.dateModified The new date the record is modified. If not given, current time will be used .
     * @param options.data The new data or the record. If not given, data from given message will be used.
     * @param options.published The new published state. If not given, then will be set to `true` if {options.dateModified} is given;
     * else the state from given message will be used.
     * @param options.publishedDate The new date the record is modified. If not given, then:
     * - will not be set if the record will be unpublished as the result of this RecordsWrite; else
     * - will be set to the same published date as the given message if it wss already published; else
     * - will be set to current time (because this is a toggle from unpublished to published)
     */
    RecordsWrite.createFrom = function (options) {
        var _a, _b;
        return __awaiter(this, void 0, void 0, function () {
            var unsignedMessage, currentTime, published, datePublished, createOptions, recordsWrite;
            return __generator(this, function (_c) {
                switch (_c.label) {
                    case 0:
                        unsignedMessage = options.unsignedRecordsWriteMessage;
                        currentTime = (0, time_js_1.getCurrentTimeInHighPrecision)();
                        published = (_a = options.published) !== null && _a !== void 0 ? _a : (options.datePublished ? true : unsignedMessage.descriptor.published);
                        datePublished = undefined;
                        // if given explicitly published dated
                        if (options.datePublished) {
                            datePublished = options.datePublished;
                        }
                        else {
                            // if this RecordsWrite will publish the record
                            if (published) {
                                // the parent was already published, inherit the same published date
                                if (unsignedMessage.descriptor.published) {
                                    datePublished = unsignedMessage.descriptor.datePublished;
                                }
                                else {
                                    // this is a toggle from unpublished to published, use current time
                                    datePublished = currentTime;
                                }
                            }
                        }
                        createOptions = {
                            // immutable properties below, just inherit from the message given
                            recipient: unsignedMessage.descriptor.recipient,
                            recordId: unsignedMessage.recordId,
                            dateCreated: unsignedMessage.descriptor.dateCreated,
                            contextId: unsignedMessage.contextId,
                            protocol: unsignedMessage.descriptor.protocol,
                            protocolPath: unsignedMessage.descriptor.protocolPath,
                            parentId: unsignedMessage.descriptor.parentId,
                            schema: unsignedMessage.descriptor.schema,
                            dataFormat: unsignedMessage.descriptor.dataFormat,
                            // mutable properties below
                            dateModified: (_b = options.dateModified) !== null && _b !== void 0 ? _b : currentTime,
                            published: published,
                            datePublished: datePublished,
                            data: options.data,
                            dataCid: options.data ? undefined : unsignedMessage.descriptor.dataCid,
                            dataSize: options.data ? undefined : unsignedMessage.descriptor.dataSize,
                            // finally still need input for signing
                            authorizationSignatureInput: options.authorizationSignatureInput,
                            attestationSignatureInputs: options.attestationSignatureInputs
                        };
                        return [4 /*yield*/, RecordsWrite.create(createOptions)];
                    case 1:
                        recordsWrite = _c.sent();
                        return [2 /*return*/, recordsWrite];
                }
            });
        });
    };
    RecordsWrite.prototype.authorize = function (tenant, messageStore) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        if (!(this.message.descriptor.protocol !== undefined)) return [3 /*break*/, 2];
                        // NOTE: `author` definitely exists because of the earlier `authenticate()` call
                        return [4 /*yield*/, protocol_authorization_js_1.ProtocolAuthorization.authorize(tenant, this, this.author, messageStore)];
                    case 1:
                        // NOTE: `author` definitely exists because of the earlier `authenticate()` call
                        _a.sent();
                        return [3 /*break*/, 4];
                    case 2: return [4 /*yield*/, (0, auth_js_1.authorize)(tenant, this)];
                    case 3:
                        _a.sent();
                        _a.label = 4;
                    case 4: return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Validates the integrity of the RecordsWrite message assuming the message passed basic schema validation.
     * There is opportunity to integrate better with `validateSchema(...)`
     */
    RecordsWrite.prototype.validateIntegrity = function () {
        return __awaiter(this, void 0, void 0, function () {
            var isInitialWrite, dateCreated, dateModified, expectedContextId, expectedAttestationCid, actualAttestationCid, expectedEncryptionCid, actualEncryptionCid;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        // make sure the same `recordId` in message is the same as the `recordId` in `authorization`
                        if (this.message.recordId !== this.authorizationPayload.recordId) {
                            throw new Error("recordId in message ".concat(this.message.recordId, " does not match recordId in authorization: ").concat(this.authorizationPayload.recordId));
                        }
                        return [4 /*yield*/, this.isInitialWrite()];
                    case 1:
                        isInitialWrite = _a.sent();
                        if (!isInitialWrite) return [3 /*break*/, 3];
                        dateCreated = this.message.descriptor.dateCreated;
                        dateModified = this.message.descriptor.dateModified;
                        if (dateModified !== dateCreated) {
                            throw new Error("dateModified ".concat(dateModified, " must match dateCreated ").concat(dateCreated, " for the initial write"));
                        }
                        if (!(this.message.descriptor.protocol !== undefined &&
                            this.message.descriptor.parentId === undefined)) return [3 /*break*/, 3];
                        return [4 /*yield*/, this.getEntryId()];
                    case 2:
                        expectedContextId = _a.sent();
                        if (this.message.contextId !== expectedContextId) {
                            throw new Error("contextId in message: ".concat(this.message.contextId, " does not match deterministic contextId: ").concat(expectedContextId));
                        }
                        _a.label = 3;
                    case 3:
                        // if `contextId` is given in message, make sure the same `contextId` is in the `authorization`
                        if (this.message.contextId !== this.authorizationPayload.contextId) {
                            throw new Error("contextId in message ".concat(this.message.contextId, " does not match contextId in authorization: ").concat(this.authorizationPayload.contextId));
                        }
                        if (!(this.authorizationPayload.attestationCid !== undefined)) return [3 /*break*/, 5];
                        return [4 /*yield*/, (0, cid_js_1.computeCid)(this.message.attestation)];
                    case 4:
                        expectedAttestationCid = _a.sent();
                        actualAttestationCid = this.authorizationPayload.attestationCid;
                        if (actualAttestationCid !== expectedAttestationCid) {
                            throw new Error("CID ".concat(expectedAttestationCid, " of attestation property in message does not match attestationCid in authorization: ").concat(actualAttestationCid));
                        }
                        _a.label = 5;
                    case 5:
                        if (!(this.authorizationPayload.encryptionCid !== undefined)) return [3 /*break*/, 7];
                        return [4 /*yield*/, (0, cid_js_1.computeCid)(this.message.encryption)];
                    case 6:
                        expectedEncryptionCid = _a.sent();
                        actualEncryptionCid = this.authorizationPayload.encryptionCid;
                        if (actualEncryptionCid !== expectedEncryptionCid) {
                            throw new dwn_error_js_1.DwnError(dwn_error_js_1.DwnErrorCode.RecordsWriteValidateIntegrityEncryptionCidMismatch, "CID ".concat(expectedEncryptionCid, " of encryption property in message does not match encryptionCid in authorization: ").concat(actualEncryptionCid));
                        }
                        _a.label = 7;
                    case 7:
                        if (this.message.descriptor.protocol !== undefined) {
                            (0, url_js_1.validateProtocolUrlNormalized)(this.message.descriptor.protocol);
                        }
                        if (this.message.descriptor.schema !== undefined) {
                            (0, url_js_1.validateSchemaUrlNormalized)(this.message.descriptor.schema);
                        }
                        return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Validates the structural integrity of the `attestation` property.
     * NOTE: signature is not verified.
     */
    RecordsWrite.validateAttestationIntegrity = function (message) {
        return __awaiter(this, void 0, void 0, function () {
            var payloadJson, descriptorCid, expectedDescriptorCid, propertyCount;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        if (message.attestation === undefined) {
                            return [2 /*return*/];
                        }
                        if (message.attestation.signatures.length !== 1) {
                            throw new Error("Currently implementation only supports 1 attester, but got ".concat(message.attestation.signatures.length));
                        }
                        payloadJson = jws_js_1.Jws.decodePlainObjectPayload(message.attestation);
                        descriptorCid = payloadJson.descriptorCid;
                        return [4 /*yield*/, (0, cid_js_1.computeCid)(message.descriptor)];
                    case 1:
                        expectedDescriptorCid = _a.sent();
                        if (descriptorCid !== expectedDescriptorCid) {
                            throw new Error("descriptorCid ".concat(descriptorCid, " does not match expected descriptorCid ").concat(expectedDescriptorCid));
                        }
                        propertyCount = Object.keys(payloadJson).length;
                        if (propertyCount > 1) {
                            throw new Error("Only 'descriptorCid' is allowed in attestation payload, but got ".concat(propertyCount, " properties."));
                        }
                        return [2 /*return*/];
                }
            });
        });
    };
    ;
    /**
     * Computes the deterministic Entry ID of this message.
     */
    RecordsWrite.prototype.getEntryId = function () {
        return __awaiter(this, void 0, void 0, function () {
            var entryId;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, RecordsWrite.getEntryId(this.author, this.message.descriptor)];
                    case 1:
                        entryId = _a.sent();
                        return [2 /*return*/, entryId];
                }
            });
        });
    };
    ;
    /**
     * Computes the deterministic Entry ID of this message.
     */
    RecordsWrite.getEntryId = function (author, descriptor) {
        return __awaiter(this, void 0, void 0, function () {
            var entryIdInput, cid;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        if (author === undefined) {
                            throw new dwn_error_js_1.DwnError(dwn_error_js_1.DwnErrorCode.RecordsWriteGetEntryIdUndefinedAuthor, 'Property `author` is needed to compute entry ID.');
                        }
                        entryIdInput = __assign({}, descriptor);
                        entryIdInput.author = author;
                        return [4 /*yield*/, (0, cid_js_1.computeCid)(entryIdInput)];
                    case 1:
                        cid = _a.sent();
                        return [2 /*return*/, cid];
                }
            });
        });
    };
    ;
    /**
     * Checks if the given message is the initial entry of a record.
     */
    RecordsWrite.prototype.isInitialWrite = function () {
        return __awaiter(this, void 0, void 0, function () {
            var entryId;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.getEntryId()];
                    case 1:
                        entryId = _a.sent();
                        return [2 /*return*/, (entryId === this.message.recordId)];
                }
            });
        });
    };
    /**
     * Checks if the given message is the initial entry of a record.
     */
    RecordsWrite.isInitialWrite = function (message) {
        return __awaiter(this, void 0, void 0, function () {
            var recordsWriteMessage, author, entryId;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        // can't be the initial write if the message is not a Records Write
                        if (message.descriptor.interface !== message_js_2.DwnInterfaceName.Records ||
                            message.descriptor.method !== message_js_2.DwnMethodName.Write) {
                            return [2 /*return*/, false];
                        }
                        recordsWriteMessage = message;
                        author = message_js_1.Message.getAuthor(message);
                        return [4 /*yield*/, RecordsWrite.getEntryId(author, recordsWriteMessage.descriptor)];
                    case 1:
                        entryId = _a.sent();
                        return [2 /*return*/, (entryId === recordsWriteMessage.recordId)];
                }
            });
        });
    };
    /**
     * Creates the `encryption` property if encryption input is given. Else `undefined` is returned.
     */
    RecordsWrite.createEncryptionProperty = function (recordId, contextId, descriptor, encryptionInput) {
        var _a, _b;
        return __awaiter(this, void 0, void 0, function () {
            var keyEncryption, _c, _d, keyEncryptionInput, fullDerivationPath, leafPublicKey, keyEncryptionOutput, encryptedKey, ephemeralPublicKey, keyEncryptionInitializationVector, messageAuthenticationCode, encryptedKeyData, e_1_1, encryption;
            var e_1, _e;
            return __generator(this, function (_f) {
                switch (_f.label) {
                    case 0:
                        if (encryptionInput === undefined) {
                            return [2 /*return*/, undefined];
                        }
                        keyEncryption = [];
                        _f.label = 1;
                    case 1:
                        _f.trys.push([1, 8, 9, 10]);
                        _c = __values(encryptionInput.keyEncryptionInputs), _d = _c.next();
                        _f.label = 2;
                    case 2:
                        if (!!_d.done) return [3 /*break*/, 7];
                        keyEncryptionInput = _d.value;
                        fullDerivationPath = records_js_1.Records.constructKeyDerivationPath(keyEncryptionInput.derivationScheme, recordId, contextId, descriptor);
                        return [4 /*yield*/, records_js_1.Records.deriveLeafPublicKey(keyEncryptionInput.publicKey, fullDerivationPath)];
                    case 3:
                        leafPublicKey = _f.sent();
                        return [4 /*yield*/, encryption_js_1.Encryption.eciesSecp256k1Encrypt(leafPublicKey, encryptionInput.key)];
                    case 4:
                        keyEncryptionOutput = _f.sent();
                        encryptedKey = encoder_js_1.Encoder.bytesToBase64Url(keyEncryptionOutput.ciphertext);
                        return [4 /*yield*/, secp256k1_js_1.Secp256k1.publicKeyToJwk(keyEncryptionOutput.ephemeralPublicKey)];
                    case 5:
                        ephemeralPublicKey = _f.sent();
                        keyEncryptionInitializationVector = encoder_js_1.Encoder.bytesToBase64Url(keyEncryptionOutput.initializationVector);
                        messageAuthenticationCode = encoder_js_1.Encoder.bytesToBase64Url(keyEncryptionOutput.messageAuthenticationCode);
                        encryptedKeyData = {
                            rootKeyId: keyEncryptionInput.publicKeyId,
                            algorithm: (_a = keyEncryptionInput.algorithm) !== null && _a !== void 0 ? _a : encryption_js_2.EncryptionAlgorithm.EciesSecp256k1,
                            derivationScheme: keyEncryptionInput.derivationScheme,
                            ephemeralPublicKey: ephemeralPublicKey,
                            initializationVector: keyEncryptionInitializationVector,
                            messageAuthenticationCode: messageAuthenticationCode,
                            encryptedKey: encryptedKey
                        };
                        keyEncryption.push(encryptedKeyData);
                        _f.label = 6;
                    case 6:
                        _d = _c.next();
                        return [3 /*break*/, 2];
                    case 7: return [3 /*break*/, 10];
                    case 8:
                        e_1_1 = _f.sent();
                        e_1 = { error: e_1_1 };
                        return [3 /*break*/, 10];
                    case 9:
                        try {
                            if (_d && !_d.done && (_e = _c.return)) _e.call(_c);
                        }
                        finally { if (e_1) throw e_1.error; }
                        return [7 /*endfinally*/];
                    case 10:
                        encryption = {
                            algorithm: (_b = encryptionInput.algorithm) !== null && _b !== void 0 ? _b : encryption_js_2.EncryptionAlgorithm.Aes256Ctr,
                            initializationVector: encoder_js_1.Encoder.bytesToBase64Url(encryptionInput.initializationVector),
                            keyEncryption: keyEncryption
                        };
                        return [2 /*return*/, encryption];
                }
            });
        });
    };
    /**
     * Creates the `attestation` property of a RecordsWrite message if given signature inputs; returns `undefined` otherwise.
     */
    RecordsWrite.createAttestation = function (descriptorCid, signatureInputs) {
        return __awaiter(this, void 0, void 0, function () {
            var attestationPayload, attestationPayloadBytes, signer;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        if (signatureInputs === undefined || signatureInputs.length === 0) {
                            return [2 /*return*/, undefined];
                        }
                        attestationPayload = { descriptorCid: descriptorCid };
                        attestationPayloadBytes = encoder_js_1.Encoder.objectToBytes(attestationPayload);
                        return [4 /*yield*/, signer_js_1.GeneralJwsSigner.create(attestationPayloadBytes, signatureInputs)];
                    case 1:
                        signer = _a.sent();
                        return [2 /*return*/, signer.getJws()];
                }
            });
        });
    };
    /**
     * Creates the `authorization` property of a RecordsWrite message.
     */
    RecordsWrite.createAuthorization = function (recordId, contextId, descriptorCid, attestation, encryption, signatureInput) {
        return __awaiter(this, void 0, void 0, function () {
            var authorizationPayload, attestationCid, _a, encryptionCid, _b, authorizationPayloadBytes, signer;
            return __generator(this, function (_c) {
                switch (_c.label) {
                    case 0:
                        authorizationPayload = {
                            recordId: recordId,
                            descriptorCid: descriptorCid
                        };
                        if (!attestation) return [3 /*break*/, 2];
                        return [4 /*yield*/, (0, cid_js_1.computeCid)(attestation)];
                    case 1:
                        _a = _c.sent();
                        return [3 /*break*/, 3];
                    case 2:
                        _a = undefined;
                        _c.label = 3;
                    case 3:
                        attestationCid = _a;
                        if (!encryption) return [3 /*break*/, 5];
                        return [4 /*yield*/, (0, cid_js_1.computeCid)(encryption)];
                    case 4:
                        _b = _c.sent();
                        return [3 /*break*/, 6];
                    case 5:
                        _b = undefined;
                        _c.label = 6;
                    case 6:
                        encryptionCid = _b;
                        if (contextId !== undefined) {
                            authorizationPayload.contextId = contextId;
                        } // assign `contextId` only if it is defined
                        if (attestationCid !== undefined) {
                            authorizationPayload.attestationCid = attestationCid;
                        } // assign `attestationCid` only if it is defined
                        if (encryptionCid !== undefined) {
                            authorizationPayload.encryptionCid = encryptionCid;
                        } // assign `encryptionCid` only if it is defined
                        authorizationPayloadBytes = encoder_js_1.Encoder.objectToBytes(authorizationPayload);
                        return [4 /*yield*/, signer_js_1.GeneralJwsSigner.create(authorizationPayloadBytes, [signatureInput])];
                    case 7:
                        signer = _c.sent();
                        return [2 /*return*/, signer.getJws()];
                }
            });
        });
    };
    /**
     * Gets the initial write from the given list or record write.
     */
    RecordsWrite.getInitialWrite = function (messages) {
        return __awaiter(this, void 0, void 0, function () {
            var messages_1, messages_1_1, message, e_2_1;
            var e_2, _a;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        _b.trys.push([0, 5, 6, 7]);
                        messages_1 = __values(messages), messages_1_1 = messages_1.next();
                        _b.label = 1;
                    case 1:
                        if (!!messages_1_1.done) return [3 /*break*/, 4];
                        message = messages_1_1.value;
                        return [4 /*yield*/, RecordsWrite.isInitialWrite(message)];
                    case 2:
                        if (_b.sent()) {
                            return [2 /*return*/, message];
                        }
                        _b.label = 3;
                    case 3:
                        messages_1_1 = messages_1.next();
                        return [3 /*break*/, 1];
                    case 4: return [3 /*break*/, 7];
                    case 5:
                        e_2_1 = _b.sent();
                        e_2 = { error: e_2_1 };
                        return [3 /*break*/, 7];
                    case 6:
                        try {
                            if (messages_1_1 && !messages_1_1.done && (_a = messages_1.return)) _a.call(messages_1);
                        }
                        finally { if (e_2) throw e_2.error; }
                        return [7 /*endfinally*/];
                    case 7: throw new Error("initial write is not found");
                }
            });
        });
    };
    /**
     * Verifies that immutable properties of the two given messages are identical.
     * @throws {Error} if immutable properties between two RecordsWrite message
     */
    RecordsWrite.verifyEqualityOfImmutableProperties = function (existingWriteMessage, newMessage) {
        var e_3, _a;
        var mutableDescriptorProperties = ['dataCid', 'dataSize', 'datePublished', 'published', 'dateModified'];
        // get distinct property names that exist in either the existing message given or new message
        var descriptorPropertyNames = [];
        descriptorPropertyNames.push.apply(descriptorPropertyNames, __spreadArray([], __read(Object.keys(existingWriteMessage.descriptor)), false));
        descriptorPropertyNames.push.apply(descriptorPropertyNames, __spreadArray([], __read(Object.keys(newMessage.descriptor)), false));
        descriptorPropertyNames = __spreadArray([], __read(new Set(descriptorPropertyNames)), false); // step to remove duplicates
        try {
            // ensure all immutable properties are not modified
            for (var descriptorPropertyNames_1 = __values(descriptorPropertyNames), descriptorPropertyNames_1_1 = descriptorPropertyNames_1.next(); !descriptorPropertyNames_1_1.done; descriptorPropertyNames_1_1 = descriptorPropertyNames_1.next()) {
                var descriptorPropertyName = descriptorPropertyNames_1_1.value;
                // if property is supposed to be immutable
                if (mutableDescriptorProperties.indexOf(descriptorPropertyName) === -1) {
                    var valueInExistingWrite = existingWriteMessage.descriptor[descriptorPropertyName];
                    var valueInNewMessage = newMessage.descriptor[descriptorPropertyName];
                    if (valueInNewMessage !== valueInExistingWrite) {
                        throw new Error("".concat(descriptorPropertyName, " is an immutable property: cannot change '").concat(valueInExistingWrite, "' to '").concat(valueInNewMessage, "'"));
                    }
                }
            }
        }
        catch (e_3_1) { e_3 = { error: e_3_1 }; }
        finally {
            try {
                if (descriptorPropertyNames_1_1 && !descriptorPropertyNames_1_1.done && (_a = descriptorPropertyNames_1.return)) _a.call(descriptorPropertyNames_1);
            }
            finally { if (e_3) throw e_3.error; }
        }
        return true;
    };
    /**
     * Gets the DID of the author of the given message.
     */
    RecordsWrite.getAttesters = function (message) {
        var _a, _b;
        var attestationSignatures = (_b = (_a = message.attestation) === null || _a === void 0 ? void 0 : _a.signatures) !== null && _b !== void 0 ? _b : [];
        var attesters = attestationSignatures.map(function (signature) { return jws_js_1.Jws.getSignerDid(signature); });
        return attesters;
    };
    return RecordsWrite;
}(message_js_1.Message));
exports.RecordsWrite = RecordsWrite;
//# sourceMappingURL=records-write.js.map