"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.Message = exports.DwnMethodName = exports.DwnInterfaceName = void 0;
const cid_js_1 = require("../utils/cid.js");
const signer_js_1 = require("../jose/jws/general/signer.js");
const jws_js_1 = require("../utils/jws.js");
const string_js_1 = require("../utils/string.js");
const object_js_1 = require("../utils/object.js");
const schema_validator_js_1 = require("../schema-validator.js");
var DwnInterfaceName;
(function (DwnInterfaceName) {
    DwnInterfaceName["Events"] = "Events";
    DwnInterfaceName["Hooks"] = "Hooks";
    DwnInterfaceName["Messages"] = "Messages";
    DwnInterfaceName["Permissions"] = "Permissions";
    DwnInterfaceName["Protocols"] = "Protocols";
    DwnInterfaceName["Records"] = "Records";
    DwnInterfaceName["Snapshots"] = "Snapshots";
})(DwnInterfaceName || (exports.DwnInterfaceName = DwnInterfaceName = {}));
var DwnMethodName;
(function (DwnMethodName) {
    DwnMethodName["Configure"] = "Configure";
    DwnMethodName["Create"] = "Create";
    DwnMethodName["Get"] = "Get";
    DwnMethodName["Grant"] = "Grant";
    DwnMethodName["Query"] = "Query";
    DwnMethodName["Read"] = "Read";
    DwnMethodName["Request"] = "Request";
    DwnMethodName["Revoke"] = "Revoke";
    DwnMethodName["Write"] = "Write";
    DwnMethodName["Delete"] = "Delete";
})(DwnMethodName || (exports.DwnMethodName = DwnMethodName = {}));
class Message {
    constructor(message) {
        this.message = message;
        if (message.authorization !== undefined) {
            this.authorizationPayload = jws_js_1.Jws.decodePlainObjectPayload(message.authorization);
            this.author = Message.getAuthor(message);
        }
    }
    /**
     * Called by `JSON.stringify(...)` automatically.
     */
    toJSON() {
        return this.message;
    }
    /**
     * Validates the given message against the corresponding JSON schema.
     * @throws {Error} if fails validation.
     */
    static validateJsonSchema(rawMessage) {
        const dwnInterface = rawMessage.descriptor.interface;
        const dwnMethod = rawMessage.descriptor.method;
        const schemaLookupKey = dwnInterface + dwnMethod;
        // throws an error if message is invalid
        (0, schema_validator_js_1.validateJsonSchema)(schemaLookupKey, rawMessage);
    }
    ;
    /**
     * Gets the DID of the author of the given message, returned `undefined` if message is not signed.
     */
    static getAuthor(message) {
        if (message.authorization === undefined) {
            return undefined;
        }
        const author = jws_js_1.Jws.getSignerDid(message.authorization.signatures[0]);
        return author;
    }
    /**
     * Gets the CID of the given message.
     */
    static getCid(message) {
        return __awaiter(this, void 0, void 0, function* () {
            // NOTE: we wrap the `computeCid()` here in case that
            // the message will contain properties that should not be part of the CID computation
            // and we need to strip them out (like `encodedData` that we historically had for a long time),
            // but we can remove this method entirely if the code becomes stable and it is apparent that the wrapper is not needed
            const cid = yield cid_js_1.Cid.computeCid(message);
            return cid;
        });
    }
    /**
     * Compares message CID in lexicographical order according to the spec.
     * @returns 1 if `a` is larger than `b`; -1 if `a` is smaller/older than `b`; 0 otherwise (same message)
     */
    static compareCid(a, b) {
        return __awaiter(this, void 0, void 0, function* () {
            // the < and > operators compare strings in lexicographical order
            const cidA = yield Message.getCid(a);
            const cidB = yield Message.getCid(b);
            return (0, string_js_1.lexicographicalCompare)(cidA, cidB);
        });
    }
    /**
     * Compares the CID of two messages.
     * @returns `true` if `a` is newer than `b`; `false` otherwise
     */
    static isCidLarger(a, b) {
        return __awaiter(this, void 0, void 0, function* () {
            const aIsLarger = ((yield Message.compareCid(a, b)) > 0);
            return aIsLarger;
        });
    }
    /**
     * @returns message with the largest CID in the array using lexicographical compare. `undefined` if given array is empty.
     */
    static getMessageWithLargestCid(messages) {
        return __awaiter(this, void 0, void 0, function* () {
            let currentNewestMessage = undefined;
            for (const message of messages) {
                if (currentNewestMessage === undefined || (yield Message.isCidLarger(message, currentNewestMessage))) {
                    currentNewestMessage = message;
                }
            }
            return currentNewestMessage;
        });
    }
    /**
     * Signs over the CID of provided `descriptor`. The output is used as an `authorization` property.
     * @param signatureInput - the signature material to use (e.g. key and header data)
     * @returns General JWS signature used as an `authorization` property.
     */
    static signAsAuthorization(descriptor, signatureInput, permissionsGrantId) {
        return __awaiter(this, void 0, void 0, function* () {
            const descriptorCid = yield cid_js_1.Cid.computeCid(descriptor);
            const authPayload = { descriptorCid, permissionsGrantId };
            (0, object_js_1.removeUndefinedProperties)(authPayload);
            const authPayloadStr = JSON.stringify(authPayload);
            const authPayloadBytes = new TextEncoder().encode(authPayloadStr);
            const signer = yield signer_js_1.GeneralJwsSigner.create(authPayloadBytes, [signatureInput]);
            return signer.getJws();
        });
    }
    /**
     * @returns newest message in the array. `undefined` if given array is empty.
     */
    static getNewestMessage(messages) {
        return __awaiter(this, void 0, void 0, function* () {
            let currentNewestMessage = undefined;
            for (const message of messages) {
                if (currentNewestMessage === undefined || (yield Message.isNewer(message, currentNewestMessage))) {
                    currentNewestMessage = message;
                }
            }
            return currentNewestMessage;
        });
    }
    /**
     * @returns oldest message in the array. `undefined` if given array is empty.
     */
    static getOldestMessage(messages) {
        return __awaiter(this, void 0, void 0, function* () {
            let currentOldestMessage = undefined;
            for (const message of messages) {
                if (currentOldestMessage === undefined || (yield Message.isOlder(message, currentOldestMessage))) {
                    currentOldestMessage = message;
                }
            }
            return currentOldestMessage;
        });
    }
    /**
     * Checks if first message is newer than second message.
     * @returns `true` if `a` is newer than `b`; `false` otherwise
     */
    static isNewer(a, b) {
        return __awaiter(this, void 0, void 0, function* () {
            const aIsNewer = ((yield Message.compareMessageTimestamp(a, b)) > 0);
            return aIsNewer;
        });
    }
    /**
     * Checks if first message is older than second message.
     * @returns `true` if `a` is older than `b`; `false` otherwise
     */
    static isOlder(a, b) {
        return __awaiter(this, void 0, void 0, function* () {
            const aIsOlder = ((yield Message.compareMessageTimestamp(a, b)) < 0);
            return aIsOlder;
        });
    }
    /**
     * Compares the `messageTimestamp` of the given messages with a fallback to message CID according to the spec.
     * @returns 1 if `a` is larger/newer than `b`; -1 if `a` is smaller/older than `b`; 0 otherwise (same age)
     */
    static compareMessageTimestamp(a, b) {
        return __awaiter(this, void 0, void 0, function* () {
            if (a.descriptor.messageTimestamp > b.descriptor.messageTimestamp) {
                return 1;
            }
            else if (a.descriptor.messageTimestamp < b.descriptor.messageTimestamp) {
                return -1;
            }
            // else `messageTimestamp` is the same between a and b
            // compare the `dataCid` instead, the < and > operators compare strings in lexicographical order
            return Message.compareCid(a, b);
        });
    }
}
exports.Message = Message;
//# sourceMappingURL=message.js.map