"use strict";
// Copyright 2022 - 2024 Gnuxie <Gnuxie@protonmail.com>
// Copyright 2022 The Matrix.org Foundation C.I.C.
//
// SPDX-License-Identifier: Apache-2.0
//
// SPDX-FileAttributionText: <text>
// This modified file incorporates work from mjolnir
// https://github.com/matrix-org/mjolnir
// </text>
Object.defineProperty(exports, "__esModule", { value: true });
exports.DraupnirFailType = exports.UnstartedDraupnir = exports.StandardDraupnirManager = void 0;
const matrix_protection_suite_1 = require("matrix-protection-suite");
const SafeModeCause_1 = require("../safemode/SafeModeCause");
const SafeModeToggle_1 = require("../safemode/SafeModeToggle");
const BootOption_1 = require("../safemode/BootOption");
const log = new matrix_protection_suite_1.Logger("StandardDraupnirManager");
class StandardDraupnirManager {
    draupnirFactory;
    draupnir = new Map();
    failedDraupnir = new Map();
    safeModeDraupnir = new Map();
    constructor(draupnirFactory) {
        this.draupnirFactory = draupnirFactory;
        // nothing to do.
    }
    unregisterListeners() {
        for (const [, draupnir] of this.draupnir) {
            draupnir.stop();
        }
        for (const [, safeModeDraupnir] of this.safeModeDraupnir) {
            safeModeDraupnir.stop();
        }
    }
    makeSafeModeToggle(clientUserID, managementRoom, config) {
        // We need to alias to make the toggle frankly.
        // eslint-disable-next-line @typescript-eslint/no-this-alias
        const draupnirManager = this;
        const toggle = Object.freeze({
            async switchToSafeMode(cause) {
                draupnirManager.stopDraupnir(clientUserID);
                return draupnirManager.makeSafeModeDraupnir(clientUserID, managementRoom, config, cause);
            },
            async switchToDraupnir() {
                draupnirManager.stopDraupnir(clientUserID);
                return draupnirManager.makeDraupnir(clientUserID, managementRoom, config);
            },
        });
        return toggle;
    }
    async makeDraupnir(clientUserID, managementRoom, config) {
        this.safeModeDraupnir.delete(clientUserID);
        const draupnir = await this.draupnirFactory.makeDraupnir(clientUserID, managementRoom, config, this.makeSafeModeToggle(clientUserID, managementRoom, config));
        if (this.isNormalDraupnir(clientUserID)) {
            return matrix_protection_suite_1.ActionError.Result(`There is a draupnir for ${clientUserID} already running`);
        }
        if ((0, matrix_protection_suite_1.isError)(draupnir)) {
            this.reportUnstartedDraupnir(DraupnirFailType.InitializationError, draupnir.error, clientUserID);
            switch (config.safeMode?.bootOption) {
                case BootOption_1.SafeModeBootOption.Never:
                    return draupnir;
                case BootOption_1.SafeModeBootOption.RecoveryOnly:
                    if (!(draupnir.error instanceof matrix_protection_suite_1.ConfigRecoverableError)) {
                        return draupnir;
                    }
                // fallthrough
                default: {
                    log.error(`Failed to start draupnir ${clientUserID}, switching to safe mode as configured`, draupnir.error);
                    const safeModeResult = await this.makeSafeModeDraupnir(clientUserID, managementRoom, config, {
                        reason: SafeModeCause_1.SafeModeReason.InitializationError,
                        error: draupnir.error,
                    });
                    if ((0, matrix_protection_suite_1.isError)(safeModeResult)) {
                        return safeModeResult;
                    }
                    else {
                        return SafeModeToggle_1.DraupnirRestartError.Result(`Failed to start draupnir ${clientUserID}, switching to safe mode as configured`, { safeModeDraupnir: safeModeResult.ok });
                    }
                }
            }
        }
        this.draupnir.set(clientUserID, draupnir.ok);
        this.failedDraupnir.delete(clientUserID);
        draupnir.ok.start();
        return draupnir;
    }
    async makeSafeModeDraupnir(clientUserID, managementRoom, config, cause) {
        if (this.isSafeModeDraupnir(clientUserID)) {
            return matrix_protection_suite_1.ActionError.Result(`There is a draupnir for ${clientUserID} already running`);
        }
        const safeModeDraupnir = await this.draupnirFactory.makeSafeModeDraupnir(clientUserID, managementRoom, config, cause, this.makeSafeModeToggle(clientUserID, managementRoom, config));
        if ((0, matrix_protection_suite_1.isError)(safeModeDraupnir)) {
            this.reportUnstartedDraupnir(DraupnirFailType.InitializationError, safeModeDraupnir.error, clientUserID);
            return safeModeDraupnir;
        }
        safeModeDraupnir.ok.start();
        this.safeModeDraupnir.set(clientUserID, safeModeDraupnir.ok);
        this.draupnir.delete(clientUserID);
        this.failedDraupnir.delete(clientUserID);
        return safeModeDraupnir;
    }
    isNormalDraupnir(drapunirClientID) {
        return this.draupnir.has(drapunirClientID);
    }
    isSafeModeDraupnir(drapunirClientID) {
        return this.safeModeDraupnir.has(drapunirClientID);
    }
    /**
     * Whether the draupnir is available to the user, either normally or via safe mode.
     */
    isDraupnirAvailable(draupnirClientID) {
        return (this.isNormalDraupnir(draupnirClientID) ||
            this.isSafeModeDraupnir(draupnirClientID));
    }
    isDraupnirFailed(draupnirClientID) {
        return this.failedDraupnir.has(draupnirClientID);
    }
    reportUnstartedDraupnir(failType, cause, draupnirClientID) {
        this.failedDraupnir.set(draupnirClientID, new UnstartedDraupnir(draupnirClientID, failType, cause));
    }
    getUnstartedDraupnirs() {
        return [...this.failedDraupnir.values()];
    }
    findUnstartedDraupnir(draupnirClientID) {
        return this.failedDraupnir.get(draupnirClientID);
    }
    findRunningDraupnir(draupnirClientID) {
        return this.draupnir.get(draupnirClientID);
    }
    stopDraupnir(clientUserID) {
        const draupnir = this.draupnir.get(clientUserID);
        if (draupnir !== undefined) {
            draupnir.stop();
            this.draupnir.delete(clientUserID);
        }
        const safeModeDraupnir = this.safeModeDraupnir.get(clientUserID);
        if (safeModeDraupnir) {
            safeModeDraupnir.stop();
            this.safeModeDraupnir.delete(clientUserID);
        }
    }
}
exports.StandardDraupnirManager = StandardDraupnirManager;
class UnstartedDraupnir {
    clientUserID;
    failType;
    cause;
    constructor(clientUserID, failType, cause) {
        this.clientUserID = clientUserID;
        this.failType = failType;
        this.cause = cause;
        // nothing to do.
    }
}
exports.UnstartedDraupnir = UnstartedDraupnir;
var DraupnirFailType;
(function (DraupnirFailType) {
    DraupnirFailType["Unauthorized"] = "Unauthorized";
    DraupnirFailType["StartError"] = "StartError";
    DraupnirFailType["InitializationError"] = "InitializationError";
})(DraupnirFailType || (exports.DraupnirFailType = DraupnirFailType = {}));
//# sourceMappingURL=StandardDraupnirManager.js.map