// Cross-process live-event bridge.
//
// Why this exists:
//   lib/live-events.js fans out events through `globalThis.__madarxLiveEventBus`,
//   which is process-local. The WhatsApp daemon, cron daemon, and heartbeat
//   worker all run as separate Node processes — when they call publishLiveEvent
//   the events stay in their own bus and the dashboard SSE never sees them.
//
//   Every published event is also written to the SQLite `activity_log` table
//   (via persistLiveEvent in lib/delegation.js). This bridge polls that table
//   from inside the Next.js server and republishes foreign rows onto the local
//   bus, so the dashboard can see activity from any process.
//
// De-duplication:
//   publishLiveEvent records every event ID emitted in THIS process in a
//   bounded LRU. The bridge skips rows whose details.id is in that set, so
//   events that originated here are not delivered twice.
//
// Lifecycle:
//   ensureBridgeStarted() is idempotent. The SSE route calls it on every GET;
//   the first call seeds the cursor at MAX(seq) (so we don't replay history)
//   and starts an unref'd 500ms interval. Subsequent calls are no-ops.

import { getMaxActivitySeq, listActivitySince } from './delegation.js';
import { publishLiveEvent, wasLocallyPublished } from './live-events.js';

const TICK_INTERVAL_MS = Number(process.env.MADARX_LIVE_EVENTS_BRIDGE_INTERVAL_MS) || 500;
const BATCH_LIMIT = 200;

let started = false;
let cursor = 0;
let timer = null;

function tick() {
  let rows;
  try {
    rows = listActivitySince(cursor, BATCH_LIMIT);
  } catch (err) {
    // Transient SQLite read failure — log and skip this tick.
    console.error('[live-events-bridge] read failure:', err.message);
    return;
  }
  for (const row of rows) {
    cursor = row.cursor;
    const details = row.details;
    if (!details || typeof details !== 'object') continue;
    if (!details.type) continue;
    if (details.id && wasLocallyPublished(details.id)) continue;
    // Republish onto the in-process bus without re-persisting (the row is
    // already in activity_log — re-persisting would create an infinite loop).
    try {
      publishLiveEvent({ ...details, persist: false });
    } catch (err) {
      console.error('[live-events-bridge] republish failure:', err.message);
    }
  }
}

export function ensureBridgeStarted() {
  if (started) return;
  started = true;
  try {
    cursor = getMaxActivitySeq();
  } catch (err) {
    console.error('[live-events-bridge] could not seed cursor, starting at 0:', err.message);
    cursor = 0;
  }
  timer = setInterval(tick, TICK_INTERVAL_MS);
  timer.unref?.();
}

// Test-only escape hatches. Production code never calls these.
export function _resetBridgeForTests() {
  if (timer) clearInterval(timer);
  timer = null;
  started = false;
  cursor = 0;
}

export function _tickOnceForTests() {
  tick();
}

export function _getCursorForTests() {
  return cursor;
}
