"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.PermissionsRevokeHandler = void 0;
const auth_js_1 = require("../core/auth.js");
const message_reply_js_1 = require("../core/message-reply.js");
const permissions_revoke_js_1 = require("../interfaces/permissions-revoke.js");
const message_js_1 = require("../core/message.js");
class PermissionsRevokeHandler {
    constructor(didResolver, messageStore, eventLog) {
        this.didResolver = didResolver;
        this.messageStore = messageStore;
        this.eventLog = eventLog;
    }
    handle({ tenant, message }) {
        return __awaiter(this, void 0, void 0, function* () {
            let permissionsRevoke;
            try {
                permissionsRevoke = yield permissions_revoke_js_1.PermissionsRevoke.parse(message);
            }
            catch (e) {
                return (0, message_reply_js_1.messageReplyFromError)(e, 400);
            }
            // Authentication
            try {
                yield (0, auth_js_1.authenticate)(message.authorization, this.didResolver);
            }
            catch (e) {
                return (0, message_reply_js_1.messageReplyFromError)(e, 401);
            }
            // Authorization
            const permissionsGrantId = message.descriptor.permissionsGrantId;
            const permissionsGrantMessage = yield this.messageStore.get(tenant, permissionsGrantId);
            if (permissionsGrantMessage === undefined) {
                return {
                    status: {
                        code: 400,
                        detail: `Could not find PermissionsGrant with CID ${permissionsGrantId}`
                    }
                };
            }
            try {
                yield permissionsRevoke.authorize(permissionsGrantMessage);
            }
            catch (e) {
                return (0, message_reply_js_1.messageReplyFromError)(e, 401);
            }
            // Revoke must have `dateCreated` after that of the grant
            if (message.descriptor.messageTimestamp < permissionsGrantMessage.descriptor.messageTimestamp) {
                return {
                    status: { code: 400, detail: 'PermissionsRevoke has earlier date than associated PermissionsGrant' }
                };
            }
            // query for previous revocations of this grant
            const query = {
                interface: message_js_1.DwnInterfaceName.Permissions,
                method: message_js_1.DwnMethodName.Revoke,
                permissionsGrantId,
            };
            const existingRevokesForGrant = yield this.messageStore.query(tenant, query);
            // Conflict 409 if the grant already has an older revoke
            const oldestExistingRevoke = yield message_js_1.Message.getOldestMessage(existingRevokesForGrant);
            if (oldestExistingRevoke !== undefined) {
                if (yield message_js_1.Message.isOlder(message, oldestExistingRevoke)) {
                    // incoming revoke is older then existing revoke, proceed
                }
                else {
                    // existing revoke is older then incoming revoke, ignore incoming
                    return {
                        status: { code: 409, detail: 'Conflict' }
                    };
                }
            }
            // Store incoming PermissionsRevoke
            const indexes = {
                interface: message_js_1.DwnInterfaceName.Permissions,
                method: message_js_1.DwnMethodName.Revoke,
                permissionsGrantId: message.descriptor.permissionsGrantId,
            };
            yield this.messageStore.put(tenant, message, indexes);
            yield this.eventLog.append(tenant, yield message_js_1.Message.getCid(message));
            // Delete existing revokes which are all newer than the incoming message
            const removedRevokeCids = [];
            for (const existingRevoke of existingRevokesForGrant) {
                const existingRevokeCid = yield message_js_1.Message.getCid(existingRevoke);
                yield this.messageStore.delete(tenant, existingRevokeCid);
                removedRevokeCids.push(existingRevokeCid);
            }
            yield this.eventLog.deleteEventsByCid(tenant, removedRevokeCids);
            // Delete grant-authorized messages with timestamp after revocation
            const grantAuthdMessagesQuery = {
                permissionsGrantId,
                dateCreated: { gte: message.descriptor.messageTimestamp },
            };
            const grantAuthdMessagesAfterRevoke = yield this.messageStore.query(tenant, grantAuthdMessagesQuery);
            const grantAuthdMessageCidsAfterRevoke = [];
            for (const grantAuthdMessage of grantAuthdMessagesAfterRevoke) {
                const messageCid = yield message_js_1.Message.getCid(grantAuthdMessage);
                yield this.messageStore.delete(tenant, messageCid);
            }
            this.eventLog.deleteEventsByCid(tenant, grantAuthdMessageCidsAfterRevoke);
            return {
                status: { code: 202, detail: 'Accepted' }
            };
        });
    }
}
exports.PermissionsRevokeHandler = PermissionsRevokeHandler;
//# sourceMappingURL=permissions-revoke.js.map