/**
 * Remote Substrate — Observability Panel Data Provider
 *
 * Provides introspection data about remote connections for display in
 * diagnostic panels. Follows the same panel data provider pattern used
 * by the diagnostics subsystem (see src/runtime/diagnostics/).
 *
 * Unlike the diagnostics panels, RemoteObservabilityProvider is specialized
 * for remote transport state and does not subscribe to the general event bus.
 * It instead receives push updates from the RemoteSubstrate facade.
 */
import { logger } from '../../utils/logger.js';
import { summarizeError } from '../../utils/error-display.js';
// ── RemoteObservabilityProvider ───────────────────────────────────────────────
/**
 * RemoteObservabilityProvider — aggregates remote session state for panel rendering.
 *
 * Register active RemoteSessions via `trackSession()` and unregister via
 * `untrackSession()`. Subscribe to changes with `subscribe()`. Retrieve
 * point-in-time snapshots with `getSnapshot()`.
 *
 * @example
 * ```ts
 * const obs = new RemoteObservabilityProvider();
 * obs.trackSession(session);
 *
 * const unsubscribe = obs.subscribe(() => {
 *   const snap = obs.getSnapshot();
 *   renderRemotePanel(snap);
 * });
 *
 * // When session ends:
 * obs.untrackSession(session.connectionId);
 * unsubscribe();
 * ```
 */
export class RemoteObservabilityProvider {
    /** Sessions keyed by connectionId. */
    _sessions = new Map();
    /** Change subscribers. */
    _subscribers = new Set();
    /**
     * Begin tracking a remote session.
     *
     * @param session - The remote session to track.
     */
    trackSession(session) {
        this._sessions.set(session.connectionId, session);
        this._notify();
    }
    /**
     * Update an existing session's state.
     *
     * Call this whenever the RemoteSubstrate mutates a session.
     *
     * @param session - Updated session snapshot.
     */
    updateSession(session) {
        if (this._sessions.has(session.connectionId)) {
            this._sessions.set(session.connectionId, session);
            this._notify();
        }
    }
    /**
     * Stop tracking a remote session.
     *
     * @param connectionId - The connectionId of the session to remove.
     */
    untrackSession(connectionId) {
        if (this._sessions.delete(connectionId)) {
            this._notify();
        }
    }
    /**
     * Return a point-in-time snapshot of all tracked connections.
     *
     * @returns Frozen aggregated snapshot.
     */
    getSnapshot() {
        const connections = [];
        let totalSent = 0;
        let totalReceived = 0;
        let failedCount = 0;
        for (const session of this._sessions.values()) {
            const conn = this._buildConnectionSnapshot(session);
            connections.push(conn);
            totalSent += session.messagesSent;
            totalReceived += session.messagesReceived;
            if (session.transportState === 'terminal_failure')
                failedCount++;
        }
        return Object.freeze({
            capturedAt: Date.now(),
            connections: Object.freeze(connections),
            activeCount: connections.filter((c) => c.transportState !== 'terminal_failure' && c.transportState !== 'disconnected').length,
            totalMessagesSent: totalSent,
            totalMessagesReceived: totalReceived,
            failedCount,
        });
    }
    /**
     * Subscribe to observability data changes.
     *
     * @param callback - Invoked whenever the tracked session list or state changes.
     * @returns An unsubscribe function.
     */
    subscribe(callback) {
        this._subscribers.add(callback);
        return () => { this._subscribers.delete(callback); };
    }
    /**
     * Dispose this provider, clearing all sessions and subscribers.
     */
    dispose() {
        this._sessions.clear();
        this._subscribers.clear();
    }
    // ── Private helpers ──────────────────────────────────────────────────────────
    _buildConnectionSnapshot(session) {
        const tasks = [];
        for (const task of session.remoteTasks.values()) {
            tasks.push({
                taskId: task.taskId,
                agentId: task.agentId,
                title: task.title,
                status: task.status,
                updatedAt: task.updatedAt,
                progress: task.progress,
                error: task.error,
            });
        }
        return Object.freeze({
            connectionId: session.connectionId,
            identity: session.identity,
            endpoint: session.endpoint,
            transportState: session.transportState,
            reconnectAttempts: session.reconnectAttempts,
            lastConnectedAt: session.lastConnectedAt,
            health: session.health,
            tasks: Object.freeze(tasks),
            messagesSent: session.messagesSent,
            messagesReceived: session.messagesReceived,
            lastAckedOffset: session.lastAckedOffset,
            lastError: session.lastError,
        });
    }
    _notify() {
        for (const cb of this._subscribers) {
            try {
                cb();
            }
            catch (err) {
                // Non-fatal — subscriber errors must not crash the provider
                logger.debug('RemoteObservabilityProvider: subscriber error', {
                    err: summarizeError(err),
                });
            }
        }
    }
}
