/**
 * In-process outbox drain signal (D-01 / D-03 / D-04).
 *
 * Module-level singleton EventEmitter — one emitter shared across all callers
 * in this Node.js process. Carries a zero-payload 'drain' event: signal is
 * fire-and-forget; no data crosses this boundary (D-04).
 *
 * Single-subscriber design:
 *   - signalOutboxDrain() is called only after a successful enqueue DB commit (D-01/D-03).
 *   - The sole listener is registered by initOutboxTrigger() in outboxWorker.ts.
 *   - With exactly one subscriber the default EventEmitter limit of 10 is correct.
 *   - Do NOT call setMaxListeners — unlike listEmitter.ts (T-04-04, 200 SSE fan-out),
 *     this emitter never fans out to multiple listeners.
 *
 * Exports: signalOutboxDrain, onOutboxDrain
 */

import { EventEmitter } from 'node:events';

// Module-level singleton — one emitter shared across all route handlers
// in this Node.js process.
const emitter = new EventEmitter();

/**
 * Fire a drain signal after a successful outbox enqueue commit.
 * Fire-and-forget — never awaited (D-04).
 *
 * WR-01: the emit is deferred to a microtask so the registered listener
 * (scheduleOutboxDrain) never runs on the enqueue caller's stack. EventEmitter.emit
 * dispatches listeners synchronously; without this defer, any synchronous throw in
 * the listener chain would propagate back into the route handler's try/catch and
 * surface a misleading 503 for an enqueue that actually committed. Deferring keeps
 * the documented decoupling honest: a misbehaving listener can never corrupt the
 * route response.
 */
export function signalOutboxDrain(): void {
  queueMicrotask(() => emitter.emit('drain'));
}

/**
 * Register a handler to be invoked on each drain signal.
 * Returns an unsubscribe function; call it to stop delivery to this handler.
 */
export function onOutboxDrain(handler: () => void): () => void {
  emitter.on('drain', handler);
  return () => emitter.off('drain', handler);
}
