// ==========================================
// Armature — SDK: Realtime
// ==========================================
// Room-scoped publish/subscribe over the WebSocket bridge.
//
// Extensions publish events from Starlark via realtime.publish().
// Clients subscribe to channels here — the SDK manages room
// join/leave over WS and dispatches incoming realtime.* events.
//
// Factory: createRealtime(events)
// ==========================================

/**
 * Create the realtime module.
 *
 * @param {object} events — SDK event bus (from createEvents)
 * @returns {object} realtime
 */
export function createRealtime(events) {

    // channel → Set<{event, fn}>
    const _channels = new Map();

    // ── Room management ─────────────────────────

    function _joinRoom(channel) {
        events.emit('room.subscribe', { room: channel });
    }

    function _leaveRoom(channel) {
        events.emit('room.unsubscribe', { room: channel });
    }

    // ── Incoming realtime events ────────────────

    // Single wildcard listener for all realtime.* events from server
    events.on('realtime.*', (payload, meta) => {
        if (!meta?.room) return;

        const subs = _channels.get(meta.room);
        if (!subs || subs.size === 0) return;

        // Extract event name: "realtime.foo.bar" → "foo.bar"
        const eventName = meta.event?.replace(/^realtime\./, '') || '';

        for (const entry of subs) {
            // Match: no event filter (catch-all) or exact event match
            if (!entry.event || entry.event === eventName) {
                try {
                    entry.fn(payload, { ...meta, channel: meta.room, event: eventName });
                } catch (e) {
                    console.error(`[sw.realtime] Handler error on ${meta.room}/${eventName}:`, e);
                }
            }
        }
    });

    // ── Reconnect: re-join all active rooms ─────

    events.on('ws.connected', () => {
        for (const channel of _channels.keys()) {
            if (_channels.get(channel).size > 0) {
                _joinRoom(channel);
            }
        }
    });

    // ── Public API ──────────────────────────────

    const realtime = {
        /**
         * Subscribe to a realtime channel.
         *
         * @param {string} channel — room/channel name
         * @param {string|Function} eventOrCallback — event name filter or callback
         * @param {Function} [callback] — callback if event name provided
         * @returns {Function} unsubscribe
         */
        subscribe(channel, eventOrCallback, callback) {
            if (!channel) throw new Error('sw.realtime.subscribe: channel is required');

            let event = null;
            let fn;

            if (typeof eventOrCallback === 'function') {
                fn = eventOrCallback;
            } else if (typeof eventOrCallback === 'string' && typeof callback === 'function') {
                event = eventOrCallback;
                fn = callback;
            } else {
                throw new Error('sw.realtime.subscribe: invalid arguments');
            }

            const entry = { event, fn };

            // First subscriber for this channel — join room
            if (!_channels.has(channel)) {
                _channels.set(channel, new Set());
                _joinRoom(channel);
            }

            _channels.get(channel).add(entry);

            // Return unsubscribe function
            return () => {
                const subs = _channels.get(channel);
                if (!subs) return;
                subs.delete(entry);

                // Last subscriber — leave room
                if (subs.size === 0) {
                    _channels.delete(channel);
                    _leaveRoom(channel);
                }
            };
        },

        /**
         * Debug helper: active channels and subscriber counts.
         * @returns {object} { channelName: count, ... }
         */
        channels() {
            const result = {};
            for (const [channel, subs] of _channels) {
                result[channel] = subs.size;
            }
            return result;
        },
    };

    return realtime;
}
