import { EventEmitter } from "events";
import { createLogger } from "../utils/logger.js";
const log = createLogger("session-registry");
/**
 * Centralized registry of all active chat sessions (both web and CLI).
 *
 * Extends EventEmitter to broadcast "change" events whenever a session
 * starts or stops, enabling real-time status updates across the app.
 */
class SessionRegistry extends EventEmitter {
    sessions = new Map();
    _version = 0;
    _metadataVersion = 0;
    _activeSummons = new Map();
    constructor() {
        super();
        // Each SSE client adds a "change" listener, plus the CLI watcher.
        // Raise the limit to avoid spurious warnings with multiple browser tabs.
        this.setMaxListeners(50);
    }
    /** Session version counter — incremented on register/unregister/migrate */
    get version() {
        return this._version;
    }
    /** Metadata version counter — incremented on metadata/summon changes */
    get metadataVersion() {
        return this._metadataVersion;
    }
    /** Current active summons (chatId → summon data) */
    get activeSummons() {
        return this._activeSummons;
    }
    /** Bump metadata version and emit a change event */
    notifyMetadata(chatId, data) {
        this._metadataVersion++;
        this.emit("change", { event: "chat_metadata_updated", chatId, data });
    }
    /** Register an active summon for a chat */
    addSummon(chatId, data) {
        this._activeSummons.set(chatId, data);
        this._metadataVersion++;
        this.emit("change", { event: "user_summoned", chatId, data: data });
    }
    /** Clear summon for a chat */
    clearSummon(chatId) {
        if (this._activeSummons.has(chatId)) {
            this._activeSummons.delete(chatId);
        }
    }
    /**
     * Register a new active session.
     * If a session already exists for this chatId, it is unregistered first
     * (emitting a "session_stopped" event) to avoid leaking resources.
     * Emits a "change" event with { event: "session_started", chatId, type }.
     */
    register(chatId, info) {
        const existing = this.sessions.get(chatId);
        if (existing) {
            log.warn(`register: overwriting existing session for chatId=${chatId} (was type=${existing.type}, now type=${info.type})`);
            this.sessions.delete(chatId);
            this.emit("change", { event: "session_stopped", chatId, type: existing.type });
        }
        const session = {
            chatId,
            startedAt: Date.now(),
            ...info,
        };
        this.sessions.set(chatId, session);
        this._version++;
        log.debug(`Session registered: chatId=${chatId}, type=${info.type}`);
        const event = { event: "session_started", chatId, type: info.type };
        this.emit("change", event);
    }
    /**
     * Unregister a session (it has completed or been stopped).
     * Emits a "change" event with { event: "session_stopped", chatId, type }.
     * Returns true if a session was actually removed, false if it didn't exist.
     */
    unregister(chatId) {
        const session = this.sessions.get(chatId);
        if (!session)
            return false;
        this.sessions.delete(chatId);
        this._version++;
        log.debug(`Session unregistered: chatId=${chatId}, type=${session.type}`);
        const event = { event: "session_stopped", chatId, type: session.type };
        this.emit("change", event);
        return true;
    }
    /**
     * Migrate a session from one tracking ID to another (e.g., temp ID → real session ID).
     * Emits stop for old ID and start for new ID.
     */
    migrate(oldId, newId) {
        const session = this.sessions.get(oldId);
        if (!session) {
            log.warn(`migrate: no session found for oldId=${oldId}`);
            return;
        }
        this.sessions.delete(oldId);
        const oldType = session.type;
        const newSession = { ...session, chatId: newId };
        this.sessions.set(newId, newSession);
        this._version++;
        log.debug(`Session migrated: ${oldId} → ${newId}`);
        // Emit stop for old, start for new
        this.emit("change", { event: "session_stopped", chatId: oldId, type: oldType });
        this.emit("change", { event: "session_started", chatId: newId, type: newSession.type });
    }
    /**
     * Get session info for a specific chat ID.
     */
    get(chatId) {
        return this.sessions.get(chatId);
    }
    /**
     * Check if a session exists for the given chat ID.
     */
    has(chatId) {
        return this.sessions.has(chatId);
    }
    /**
     * Get a snapshot of all active sessions.
     * Returns a plain object map of chatId → { type, startedAt }.
     */
    getAll() {
        const result = {};
        for (const [chatId, info] of this.sessions) {
            result[chatId] = { type: info.type, startedAt: info.startedAt };
        }
        return result;
    }
    /**
     * Get the number of active sessions.
     */
    get size() {
        return this.sessions.size;
    }
}
/** Singleton session registry instance */
export const sessionRegistry = new SessionRegistry();
