'use strict';

// Background auto-resume drain, extracted from server.js _autoResumeDeferredSessions
// so the batch + coalesced-broadcast invariant is unit-testable without booting the
// whole server.
//
// Drains an ordered list of deferred-session ids in batches of `batchSize`. For each
// id it re-checks `canResume` (so a tab the user clicked mid-pass is skipped, not
// double-started) and, if resumable, awaits `startSession`. After each batch it emits
// EXACTLY ONE coalesced `broadcast()` — but only when >=1 session actually resumed in
// that batch (an all-skipped batch broadcasts nothing). A `scheduleGap(fn)` callback
// schedules the next tick after the inter-batch gap; `onComplete` fires once drained.
//
// batchSize=1 reproduces the legacy serial cadence (1 resume -> 1 broadcast -> gap):
// every resumed session is its own batch, so broadcasts == resumes.
//
// All effects are injected (no module state), so a test can drive it synchronously
// with spies + a `scheduleGap` that calls its fn immediately.
//
// deps:
//   ids          string[]                 ordered deferred ids (snapshotted once)
//   batchSize    number                   >=1; ids drained per tick
//   getSession   (id) => session|undefined
//   canResume    (session) => boolean     re-checked per id
//   startSession async (session) => any   resumes one session
//   onError      (id, err) => void        non-fatal per-session resume error (optional)
//   broadcast    () => void               one coalesced list refresh per non-empty batch
//   scheduleGap  (fn) => void             schedule next tick after the inter-batch gap
//   onComplete   () => void               all ids drained (optional)
function drainDeferredInBatches(deps) {
  const {
    ids,
    batchSize,
    getSession,
    canResume,
    startSession,
    onError,
    broadcast,
    scheduleGap,
    onComplete,
  } = deps || {};
  const list = Array.isArray(ids) ? ids : [];
  const size = Math.max(1, Math.floor(Number(batchSize)) || 1);
  let i = 0;

  async function tick() {
    if (i >= list.length) {
      if (typeof onComplete === 'function') onComplete();
      return;
    }
    let resumedInBatch = 0;
    for (let b = 0; b < size && i < list.length; b++) {
      const id = list[i++];
      const s = getSession(id);
      if (canResume(s)) {
        try {
          await startSession(s);
          resumedInBatch++;
        } catch (e) {
          if (typeof onError === 'function') onError(id, e);
        }
      }
    }
    // One session-list refresh for the whole batch; an all-skipped batch is silent.
    if (resumedInBatch > 0 && typeof broadcast === 'function') broadcast();
    scheduleGap(tick);
  }

  // First tick is scheduled through the same gap (the original kicked off with
  // setTimeout(resumeNext, gap) after the identity-cache prebuild settled).
  scheduleGap(tick);
}

module.exports = { drainDeferredInBatches };
