"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RoomConfig = void 0;
const quick_lru_1 = __importDefault(require("quick-lru"));
const logging_1 = __importDefault(require("../logging"));
const MAX_CACHE_SIZE = 512;
const STATE_TIMEOUT_MS = 2000;
const log = logging_1.default("RoomConfig");
class RoomConfig {
    constructor(bridge, config) {
        this.bridge = bridge;
        this.config = config;
        this.cache = new quick_lru_1.default({ maxSize: MAX_CACHE_SIZE });
    }
    /**
     * Fetch the state for the room, preferring a keyed state event over a global one.
     * This request will time out after `STATE_TIMEOUT_MS` if the state could not be fetched in time.
     * @param roomId The Matrix room ID
     * @param ircRoom The IRC room we want the configuration for.
     * @returns A content object containing the configuration, or null if the event was not found or the
     *          request timed out.
     */
    async getRoomState(roomId, ircRoom) {
        var _a;
        if (!((_a = this.config) === null || _a === void 0 ? void 0 : _a.enabled)) {
            // If not enabled, always return null
            return null;
        }
        const cacheKey = `${roomId}:${(ircRoom === null || ircRoom === void 0 ? void 0 : ircRoom.getId()) || 'global'}`;
        let keyedConfig = this.cache.get(cacheKey);
        if (keyedConfig) {
            return keyedConfig;
        }
        const internalFunc = async () => {
            const intent = this.bridge.getIntent();
            keyedConfig = ircRoom &&
                await intent.getStateEvent(roomId, RoomConfig.STATE_EVENT_TYPE, ircRoom.getId(), true);
            if (!keyedConfig) {
                // Fall back to an empty key
                keyedConfig = await intent.getStateEvent(roomId, RoomConfig.STATE_EVENT_TYPE, '', true);
            }
            log.debug(`Stored new config for ${cacheKey}: ${keyedConfig ? 'No config set' : JSON.stringify(keyedConfig)}`);
            this.cache.set(cacheKey, keyedConfig || undefined);
            return keyedConfig;
        };
        // We don't want to spend too long trying to fetch the state, so return null.
        return Promise.race([
            internalFunc(),
            new Promise(res => setTimeout(res, STATE_TIMEOUT_MS)),
            // We *never* want this function to throw, as it's critical for the bridging of messages.
            // Instead we return null for any errors.
        ]).catch(ex => {
            log.warn(`Failed to fetch state for ${cacheKey}`, ex);
            return null;
        });
    }
    /**
     * Invalidate the cache for a room.
     * @param roomId The Matrix roomId
     * @param stateKey The state event's key
     */
    invalidateConfig(roomId, stateKey = 'global') {
        log.info(`Invalidating config for ${roomId}:${stateKey}`);
        this.cache.delete(`${roomId}:${stateKey}`);
    }
    /**
     * Get the per-room configuration for the paste bin limit for a room.
     * Only "channel" room types are supported. Non "channel" types will return null.
     * @param roomId The Matrix roomId
     * @param ircRoom The IRC roomId. Optional.
     * @returns The number of lines required for a pastebin. `null` means no limit set in the room.
     */
    async getLineLimit(roomId, ircRoom) {
        var _a, _b;
        if ((ircRoom === null || ircRoom === void 0 ? void 0 : ircRoom.getType()) !== "channel") {
            return null;
        }
        const roomState = await this.getRoomState(roomId, ircRoom);
        if (typeof (roomState === null || roomState === void 0 ? void 0 : roomState.lineLimit) !== 'number' || roomState.lineLimit <= 0) {
            // A missing line limit or an invalid one is considered invalid.
            return null;
        }
        return Math.min(roomState.lineLimit, (_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.lineLimitMax) !== null && _b !== void 0 ? _b : roomState.lineLimit);
    }
    /**
     * Check if the room allows or denys unconnected matrix users.
     * @param roomId The Matrix roomId
     * @param ircRoom The IRC roomId. Optional.
     * @returns Whether unconnected Matrix users are allowed in the room. Will return null if not set.
     */
    async allowUnconnectedMatrixUsers(roomId, ircRoom) {
        var _a;
        if (((_a = this.config) === null || _a === void 0 ? void 0 : _a.allowUnconnectedMatrixUsers) !== true) {
            // If not allowed by config, return null.
            return null;
        }
        const roomState = await this.getRoomState(roomId, ircRoom);
        if (typeof (roomState === null || roomState === void 0 ? void 0 : roomState.allowUnconnectedMatrixUsers) !== "boolean") {
            // If not set or null, return null.
            return null;
        }
        return roomState.allowUnconnectedMatrixUsers;
    }
}
exports.RoomConfig = RoomConfig;
RoomConfig.STATE_EVENT_TYPE = 'org.matrix.appservice-irc.config';
//# sourceMappingURL=RoomConfig.js.map