/**
 * sse.js — SSE reconnect loop + onevent router.
 * Calls onEvent(card) for every inbound server-sent event.
 * Exports connect(), disconnect(), and onEvent hook.
 */

import { S, API_VERSION, MAX_EVENTS, TICKER_MAX, bus } from './state.js';
import { bustCache } from './api.js';

let _stream = null;
let _delay = 1_000;
let _retryTimer = null;

/** Registered onEvent handler — set by main.js after view modules load. */
let _onEvent = null;
export function setOnEvent(fn) { _onEvent = fn; }

export function connect() {
    if (_stream) { _stream.close(); _stream = null; }
    if (_retryTimer) { clearTimeout(_retryTimer); _retryTimer = null; }

    const rid = S.workspace?.stateKey
        ? '?runtimeId=' + encodeURIComponent(S.workspace.stateKey)
        : '';
    _stream = new EventSource('/stream' + rid);

    _stream.addEventListener('bootstrap', e => {
        try {
            const d = JSON.parse(e.data);
            if (d.version && d.version !== API_VERSION) {
                bus.emit('banner', { msg: 'Dashboard version mismatch — reload.', kind: 'warn' });
            }
        } catch { /* ignore */ }
    });

    _stream.onopen = () => {
        S.connected = true;
        _delay = 1_000;
        bus.emit('connection', true);
        bus.emit('banner', null);
    };

    _stream.onerror = () => {
        S.connected = false;
        bus.emit('connection', false);
        if (_stream) { _stream.close(); _stream = null; }
        bus.emit('banner', {
            msg: `Stream disconnected — reconnecting in ${Math.round(_delay / 1_000)}s…`,
            kind: 'err',
        });
        _retryTimer = setTimeout(connect, _delay);
        _delay = Math.min(_delay * 2, 30_000);
    };

    _stream.onmessage = ev => {
        try {
            const r = JSON.parse(ev.data);
            if (!r?.connected) routeEvent(normEvent(r));
        } catch { /* ignore */ }
    };
}

export function disconnect() {
    if (_stream) { _stream.close(); _stream = null; }
    if (_retryTimer) { clearTimeout(_retryTimer); _retryTimer = null; }
}

function normEvent(raw) {
    if (!raw || typeof raw !== 'object') return null;
    return {
        id:       raw.id || `${raw.type || 'evt'}-${Date.now()}-${Math.random()}`,
        type:     raw.type || 'unknown',
        category: raw.category || 'system',
        severity: raw.severity || 'info',
        title:    raw.title || raw.type || 'Event',
        summary:  raw.summary || '',
        source:   raw.source || '',
        time:     raw.time || Date.now(),
        payload:  raw.payload,
    };
}

function routeEvent(evt) {
    if (!evt) return;

    // Dedup + cap event log
    if (!S.events.some(e => e.id === evt.id)) {
        S.events.unshift(evt);
        if (S.events.length > MAX_EVENTS) S.events = S.events.slice(0, MAX_EVENTS);
    }

    // Ticker (all events)
    S.ticker.unshift(evt);
    if (S.ticker.length > TICKER_MAX) S.ticker = S.ticker.slice(0, TICKER_MAX);
    bus.emit('ticker', S.ticker);

    // Cache-busting by category
    const cat = evt.category;
    if (cat === 'tokens') {
        bustCache('/api/tokens/summary');
        bus.emit('event:tokens', evt);
    }
    if (cat === 'memory') {
        S.gNodes = [];
        bus.emit('event:memory', evt);
    }
    if (cat === 'shield') {
        S.auditEvents.unshift(evt);
        if (S.auditEvents.length > 60) S.auditEvents = S.auditEvents.slice(0, 60);
        bus.emit('event:shield', evt);
    }
    if (['runtime', 'pod'].includes(cat)) {
        bustCache('/api/dashboard/surface/operate');
        bus.emit('event:runtime', evt);
    }
    if (['synapse', 'operative', 'mission'].includes(cat)) {
        bustCache('/api/synapse/health');
        bus.emit('event:synapse', evt);
    }
    if (['architects', 'worklist', 'workitem'].includes(cat)) {
        bustCache('/api/architects/worklist');
        bus.emit('event:architects', evt);
    }
    if (cat === 'rag') {
        bustCache('/api/rag/collections');
        bus.emit('event:rag', evt);
    }
    if (cat === 'byzantine') {
        bus.emit('event:byzantine', evt);
    }
    if (cat === 'darwin') {
        bus.emit('event:darwin', evt);
    }

    bus.emit('event', evt);
    _onEvent?.(evt);
}
