'use strict';

/**
 * Shared pipeline-freshness tier (Ship A + design-train M1b).
 *
 * Measures the INTRADAY HEARTBEAT (`refreshed_at`, ~every 20 min) against the
 * pipeline's expected cadence — NOT the grade-lock time (`updated_at`, 5×/day,
 * intentionally stable). Measuring the wrong field is what produced the
 * "SIGNAL LIVE vs STALE 8h" contradiction (Ship A).
 *
 * ONE source for desktop (HeartbeatBar) AND mobile (the app-bar clock): the
 * mobile clock rests as a wall clock and only reacts — amber, then red STALE —
 * when this tier crosses. "Alive is punctuated stillness": the clock is the
 * stillness, STALE is the punctuation. Design drew the happy path; we never
 * silently go stale on the device most users are on.
 *
 * Thresholds (same both surfaces): calm < 1.5× · amber ≥ 1.5× · red ≥ 3×.
 *
 * CommonJS so it's unit-testable by the plain-JS Jest suite AND importable by
 * the .tsx client components (allowJs).
 */

const DEFAULT_EXPECTED_S = 18000; // 5h max cron gap fallback (matches LiveLayer)

/**
 * @param {number} refreshedAtMs  Date.parse(refreshed_at); NaN/absent → calm.
 * @param {number} expectedIntervalS  summary.expected_interval_s (s); ≤0 → default.
 * @param {number} nowMs  Date.now().
 * @returns {{ tier: 'normal'|'amber'|'stale', elapsedMs: number|null }}
 */
function freshnessTier(refreshedAtMs, expectedIntervalS, nowMs) {
  if (!Number.isFinite(refreshedAtMs)) return { tier: 'normal', elapsedMs: null };
  const expectedMs = (Number(expectedIntervalS) > 0 ? Number(expectedIntervalS) : DEFAULT_EXPECTED_S) * 1000;
  const elapsedMs = Math.max(0, nowMs - refreshedAtMs);
  const tier = elapsedMs >= 3 * expectedMs ? 'stale' : elapsedMs >= 1.5 * expectedMs ? 'amber' : 'normal';
  return { tier, elapsedMs };
}

module.exports = { freshnessTier, DEFAULT_EXPECTED_S };
