"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.Dwn = void 0;
const tenant_gate_js_1 = require("./core/tenant-gate.js");
const data_store_level_js_1 = require("./store/data-store-level.js");
const did_resolver_js_1 = require("./did/did-resolver.js");
const event_log_level_js_1 = require("./event-log/event-log-level.js");
const events_get_js_1 = require("./handlers/events-get.js");
const message_reply_js_1 = require("./core/message-reply.js");
const messages_get_js_1 = require("./handlers/messages-get.js");
const message_store_level_js_1 = require("./store/message-store-level.js");
const permissions_grant_js_1 = require("./handlers/permissions-grant.js");
const permissions_request_js_1 = require("./handlers/permissions-request.js");
const permissions_revoke_js_1 = require("./handlers/permissions-revoke.js");
const protocols_configure_js_1 = require("./handlers/protocols-configure.js");
const protocols_query_js_1 = require("./handlers/protocols-query.js");
const records_delete_js_1 = require("./handlers/records-delete.js");
const records_query_js_1 = require("./handlers/records-query.js");
const records_read_js_1 = require("./handlers/records-read.js");
const records_write_js_1 = require("./handlers/records-write.js");
const message_js_1 = require("./core/message.js");
class Dwn {
    constructor(config) {
        this.didResolver = config.didResolver;
        this.messageStore = config.messageStore;
        this.dataStore = config.dataStore;
        this.eventLog = config.eventLog;
        this.tenantGate = config.tenantGate;
        this.methodHandlers = {
            [message_js_1.DwnInterfaceName.Events + message_js_1.DwnMethodName.Get]: new events_get_js_1.EventsGetHandler(this.didResolver, this.eventLog),
            [message_js_1.DwnInterfaceName.Messages + message_js_1.DwnMethodName.Get]: new messages_get_js_1.MessagesGetHandler(this.didResolver, this.messageStore, this.dataStore),
            [message_js_1.DwnInterfaceName.Permissions + message_js_1.DwnMethodName.Grant]: new permissions_grant_js_1.PermissionsGrantHandler(this.didResolver, this.messageStore, this.eventLog),
            [message_js_1.DwnInterfaceName.Permissions + message_js_1.DwnMethodName.Request]: new permissions_request_js_1.PermissionsRequestHandler(this.didResolver, this.messageStore, this.eventLog),
            [message_js_1.DwnInterfaceName.Permissions + message_js_1.DwnMethodName.Revoke]: new permissions_revoke_js_1.PermissionsRevokeHandler(this.didResolver, this.messageStore, this.eventLog),
            [message_js_1.DwnInterfaceName.Protocols + message_js_1.DwnMethodName.Configure]: new protocols_configure_js_1.ProtocolsConfigureHandler(this.didResolver, this.messageStore, this.dataStore, this.eventLog),
            [message_js_1.DwnInterfaceName.Protocols + message_js_1.DwnMethodName.Query]: new protocols_query_js_1.ProtocolsQueryHandler(this.didResolver, this.messageStore, this.dataStore),
            [message_js_1.DwnInterfaceName.Records + message_js_1.DwnMethodName.Delete]: new records_delete_js_1.RecordsDeleteHandler(this.didResolver, this.messageStore, this.dataStore, this.eventLog),
            [message_js_1.DwnInterfaceName.Records + message_js_1.DwnMethodName.Query]: new records_query_js_1.RecordsQueryHandler(this.didResolver, this.messageStore, this.dataStore),
            [message_js_1.DwnInterfaceName.Records + message_js_1.DwnMethodName.Read]: new records_read_js_1.RecordsReadHandler(this.didResolver, this.messageStore, this.dataStore),
            [message_js_1.DwnInterfaceName.Records + message_js_1.DwnMethodName.Write]: new records_write_js_1.RecordsWriteHandler(this.didResolver, this.messageStore, this.dataStore, this.eventLog),
        };
    }
    /**
     * Creates an instance of the DWN.
     */
    static create(config) {
        var _a, _b, _c, _d, _e;
        return __awaiter(this, void 0, void 0, function* () {
            config !== null && config !== void 0 ? config : (config = {});
            (_a = config.didResolver) !== null && _a !== void 0 ? _a : (config.didResolver = new did_resolver_js_1.DidResolver());
            (_b = config.tenantGate) !== null && _b !== void 0 ? _b : (config.tenantGate = new tenant_gate_js_1.AllowAllTenantGate());
            (_c = config.messageStore) !== null && _c !== void 0 ? _c : (config.messageStore = new message_store_level_js_1.MessageStoreLevel());
            (_d = config.dataStore) !== null && _d !== void 0 ? _d : (config.dataStore = new data_store_level_js_1.DataStoreLevel());
            (_e = config.eventLog) !== null && _e !== void 0 ? _e : (config.eventLog = new event_log_level_js_1.EventLogLevel());
            const dwn = new Dwn(config);
            yield dwn.open();
            return dwn;
        });
    }
    open() {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.messageStore.open();
            yield this.dataStore.open();
            yield this.eventLog.open();
        });
    }
    close() {
        return __awaiter(this, void 0, void 0, function* () {
            this.messageStore.close();
            this.dataStore.close();
            this.eventLog.close();
        });
    }
    /**
     * Processes the given DWN message and returns with a reply.
     * @param tenant The tenant DID to route the given message to.
     */
    processMessage(tenant, rawMessage, dataStream) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            const errorMessageReply = (_a = yield this.validateTenant(tenant)) !== null && _a !== void 0 ? _a : yield this.validateMessageIntegrity(rawMessage);
            if (errorMessageReply !== undefined) {
                return errorMessageReply;
            }
            const handlerKey = rawMessage.descriptor.interface + rawMessage.descriptor.method;
            const methodHandlerReply = yield this.methodHandlers[handlerKey].handle({
                tenant,
                message: rawMessage,
                dataStream
            });
            return methodHandlerReply;
        });
    }
    /**
     * Handles a `RecordsQuery` message.
     */
    handleRecordsQuery(tenant, message) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            const errorMessageReply = (_a = yield this.validateTenant(tenant)) !== null && _a !== void 0 ? _a : yield this.validateMessageIntegrity(message, message_js_1.DwnInterfaceName.Records, message_js_1.DwnMethodName.Query);
            if (errorMessageReply !== undefined) {
                return errorMessageReply;
            }
            const handler = new records_query_js_1.RecordsQueryHandler(this.didResolver, this.messageStore, this.dataStore);
            return handler.handle({ tenant, message });
        });
    }
    /**
     * Handles a `RecordsRead` message.
     */
    handleRecordsRead(tenant, message) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            const errorMessageReply = (_a = yield this.validateTenant(tenant)) !== null && _a !== void 0 ? _a : yield this.validateMessageIntegrity(message, message_js_1.DwnInterfaceName.Records, message_js_1.DwnMethodName.Read);
            if (errorMessageReply !== undefined) {
                return errorMessageReply;
            }
            const handler = new records_read_js_1.RecordsReadHandler(this.didResolver, this.messageStore, this.dataStore);
            return handler.handle({ tenant, message });
        });
    }
    /**
     * Handles a `MessagesGet` message.
     */
    handleMessagesGet(tenant, message) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            const errorMessageReply = (_a = yield this.validateTenant(tenant)) !== null && _a !== void 0 ? _a : yield this.validateMessageIntegrity(message, message_js_1.DwnInterfaceName.Messages, message_js_1.DwnMethodName.Get);
            if (errorMessageReply !== undefined) {
                return errorMessageReply;
            }
            const handler = new messages_get_js_1.MessagesGetHandler(this.didResolver, this.messageStore, this.dataStore);
            return handler.handle({ tenant, message });
        });
    }
    /**
     * Privileged method for writing a pruned initial `RecordsWrite` to a DWN without needing to supply associated data.
     */
    synchronizePrunedInitialRecordsWrite(tenant, message) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            const errorMessageReply = (_a = yield this.validateTenant(tenant)) !== null && _a !== void 0 ? _a : yield this.validateMessageIntegrity(message, message_js_1.DwnInterfaceName.Records, message_js_1.DwnMethodName.Write);
            if (errorMessageReply !== undefined) {
                return errorMessageReply;
            }
            const options = {
                skipDataStorage: true,
            };
            const handler = new records_write_js_1.RecordsWriteHandler(this.didResolver, this.messageStore, this.dataStore, this.eventLog);
            const methodHandlerReply = yield handler.handle({ tenant, message, options });
            return methodHandlerReply;
        });
    }
    /**
     * Checks tenant gate to see if tenant is allowed.
     * @param tenant The tenant DID to route the given message to.
     * @returns GenericMessageReply if the message has an integrity error, otherwise undefined.
     */
    validateTenant(tenant) {
        return __awaiter(this, void 0, void 0, function* () {
            const isTenant = yield this.tenantGate.isTenant(tenant);
            if (!isTenant) {
                return {
                    status: { code: 401, detail: `${tenant} is not a tenant` }
                };
            }
        });
    }
    /**
     * Validates structure of DWN message
     * @param tenant The tenant DID to route the given message to.
     * @param dwnMessageInterface The interface of DWN message.
     * @param dwnMessageMethod The interface of DWN message.
  
     * @returns GenericMessageReply if the message has an integrity error, otherwise undefined.
     */
    validateMessageIntegrity(rawMessage, expectedInterface, expectedMethod) {
        var _a, _b;
        return __awaiter(this, void 0, void 0, function* () {
            // Verify interface and method
            const dwnInterface = (_a = rawMessage === null || rawMessage === void 0 ? void 0 : rawMessage.descriptor) === null || _a === void 0 ? void 0 : _a.interface;
            const dwnMethod = (_b = rawMessage === null || rawMessage === void 0 ? void 0 : rawMessage.descriptor) === null || _b === void 0 ? void 0 : _b.method;
            if (dwnInterface === undefined || dwnMethod === undefined) {
                return {
                    status: { code: 400, detail: `Both interface and method must be present, interface: ${dwnInterface}, method: ${dwnMethod}` }
                };
            }
            if (expectedInterface !== undefined && expectedInterface !== dwnInterface) {
                return {
                    status: { code: 400, detail: `Expected interface ${expectedInterface}, received ${dwnInterface}` }
                };
            }
            if (expectedMethod !== undefined && expectedMethod !== dwnMethod) {
                return {
                    status: { code: 400, detail: `Expected method ${expectedInterface}${expectedMethod}, received ${dwnInterface}${dwnMethod}` }
                };
            }
            // validate message structure
            try {
                // consider to push this down to individual handlers
                message_js_1.Message.validateJsonSchema(rawMessage);
            }
            catch (error) {
                return (0, message_reply_js_1.messageReplyFromError)(error, 400);
            }
        });
    }
}
exports.Dwn = Dwn;
;
//# sourceMappingURL=dwn.js.map