"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const matrix_bot_sdk_1 = require("matrix-bot-sdk");
const DummyLogger = {
    info: () => { },
    warn: () => { },
    error: () => { },
    debug: () => { },
};
/**
 * This class provides a "one stop shop" to determine if a user is online. It will use a combination of a
 * local cache, presence endpoints and admin APIs in that order.
 */
class MatrixActivityTracker {
    constructor(homeserverUrl, accessToken, serverName, canUsePresence = true, logger = DummyLogger) {
        this.serverName = serverName;
        this.canUsePresence = canUsePresence;
        this.canUseWhois = null;
        matrix_bot_sdk_1.LogService.setLogger(logger);
        this.client = new matrix_bot_sdk_1.MatrixClient(homeserverUrl, accessToken);
        this.lastActiveTime = new Map();
    }
    get usingWhois() {
        return this.canUseWhois;
    }
    /**
     * This should be called when a user has performed an action to bump their locally stored active time.
     * @param userId The userId of a user who performed an action.
     */
    bumpLastActiveTime(userId) {
        this.lastActiveTime.set(userId, Date.now());
    }
    /**
     * Determine if a user is online or offline using a range of metrics.
     * @param userId The userId to check
     * @param maxTimeMs The maximum time a user may be inactive for before they are considered offline.
     */
    async isUserOnline(userId, maxTimeMs) {
        if (this.canUseWhois === null) {
            try {
                // HACK: Synapse exposes no way to directly determine if a user is an admin, so we use this auth check.
                await this.client.doRequest("POST", "/_synapse/admin/v1/send_server_notice", null, {});
                this.canUseWhois = false; // This should never succeed, but prevent it from trying anyway.
            }
            catch (ex) {
                // We expect this to fail
                this.canUseWhois = (ex.statusCode !== 403);
            }
        }
        // First, check if the user has bumped recently.
        const now = Date.now();
        const lastActiveTime = this.lastActiveTime.get(userId);
        if (lastActiveTime) {
            if (now - lastActiveTime < maxTimeMs) {
                // Return early, user has bumped recently.
                return { online: true, inactiveMs: now - lastActiveTime };
            }
        }
        // The user hasn't interacted with the bridge, or it was too long ago.
        // Check the user's presence.
        try {
            if (this.canUsePresence) {
                const presence = await this.client.getPresenceStatusFor(userId);
                if (presence.currently_active || presence.presence === "online") {
                    return { online: true, inactiveMs: presence.last_active_ago || 0 };
                }
                else if (presence.last_active_ago !== undefined && presence.last_active_ago > maxTimeMs) {
                    return { online: false, inactiveMs: presence.last_active_ago };
                } // Otherwise, we can't know conclusively.
            }
        }
        catch {
            // Failed to get presence, going to fallback to admin api.
        }
        if (!this.canUseWhois || userId.split(":")[1] !== this.serverName) {
            // The user is remote, we don't have any presence for them and they've 
            // not interacted with us so we are going to have to treat them as offline.
            return { online: false, inactiveMs: -1 };
        }
        const whois = await this.client.adminApis.whoisUser(userId);
        const connections = Object.values(whois.devices).flatMap((device) => device.sessions.flatMap((session => session.connections)));
        const bestConnection = connections.sort((conA, conB) => conB.last_seen - conA.last_seen)[0];
        return { online: (now - bestConnection.last_seen) < maxTimeMs, inactiveMs: now - bestConnection.last_seen };
    }
}
exports.MatrixActivityTracker = MatrixActivityTracker;
