"use strict";
/**
 * Shared buffered-delivery plumbing for standalone WS listeners (slot,
 * oracle, native SOL). Owns the cadence triple-branch (immediate / throttle
 * timer / scheduler), latest-wins buffering, and error-routing fallback.
 *
 * Buffer is keyed by `keyOf(event)` — pass it for per-key buckets (e.g.
 * one slot per oracle address); omit for single-slot latest-wins.
 *
 * Each listener decodes the WS message into TEvent, calls `dispatcher.push()`,
 * and returns `makeSubscriptionHandle(dispatcher.unsubscribe, dispatcher.flush)`.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.createBufferedDispatcher = createBufferedDispatcher;
const wsCadenceOptions_1 = require("./wsCadenceOptions");
const wsErrorReporter_1 = require("./wsErrorReporter");
/** Sentinel — when `keyOf` is omitted, every event lands under the same key. */
const SINGLE_SLOT_KEY = Symbol('buffered-dispatcher.single-slot');
function createBufferedDispatcher(opts) {
    (0, wsCadenceOptions_1.validateThrottleMs)(opts.throttleMs, opts.callSiteName);
    (0, wsCadenceOptions_1.assertCadenceOptions)(opts.callSiteName, opts.scheduler, opts.throttleMs);
    const errorReporter = (0, wsErrorReporter_1.makeWsErrorReporter)(opts.errorPrefix, opts.onError);
    const keyOf = opts.keyOf ?? ((_e) => SINGLE_SLOT_KEY);
    const bufferedEvents = new Map();
    let throttleDrainTimer;
    let unsubscribed = false;
    let unregisterFromScheduler;
    const drainBuffer = () => {
        if (unsubscribed || bufferedEvents.size === 0)
            return;
        const drained = Array.from(bufferedEvents.values());
        bufferedEvents.clear();
        for (const event of drained) {
            try {
                opts.onDeliver(event);
            }
            catch (e) {
                errorReporter.reportError(e);
            }
        }
    };
    const onThrottleTimerFired = () => {
        throttleDrainTimer = undefined;
        drainBuffer();
    };
    if (opts.scheduler && opts.throttleMs !== undefined) {
        unregisterFromScheduler = opts.scheduler.register(drainBuffer, opts.throttleMs);
    }
    const bufferForSchedulerTick = (key, event) => {
        bufferedEvents.set(key, event);
    };
    const bufferAndArmThrottleTimer = (key, event, throttleMs) => {
        bufferedEvents.set(key, event);
        if (throttleDrainTimer === undefined) {
            throttleDrainTimer = setTimeout(onThrottleTimerFired, throttleMs);
        }
    };
    const deliverImmediately = (event) => {
        try {
            opts.onDeliver(event);
        }
        catch (e) {
            errorReporter.reportError(e);
        }
    };
    return {
        push(event) {
            if (unsubscribed)
                return;
            const key = keyOf(event);
            if (opts.scheduler)
                return bufferForSchedulerTick(key, event);
            if (opts.throttleMs !== undefined)
                return bufferAndArmThrottleTimer(key, event, opts.throttleMs);
            deliverImmediately(event);
        },
        flush() {
            drainBuffer();
        },
        unsubscribe() {
            unsubscribed = true;
            if (throttleDrainTimer !== undefined) {
                clearTimeout(throttleDrainTimer);
                throttleDrainTimer = undefined;
            }
            bufferedEvents.clear();
            unregisterFromScheduler?.();
            unregisterFromScheduler = undefined;
        },
    };
}
//# sourceMappingURL=bufferedDispatcher.js.map