/**
 * Directional-CLV badge model (Session 64). CommonJS so it is unit-testable
 * and shared by the card without a React import.
 *
 * ABSENCE IS HONEST. unknown / flat / null / missing render NOTHING — not a
 * placeholder, not a zero, not "pending". A board where some reads are badged
 * and others are not is the REAL state (closes are captured per game as each
 * locks), so a badge-less card must never look broken or worse.
 *
 * THE RECEIPT IS THE POINT. "MOVED TOWARD US" alone is a claim; "graded −110 ·
 * closed −145" is evidence. The numbers are what make it persuasive, so the
 * badge always carries them and is suppressed if they are missing.
 *
 * NO AGGREGATE. There is deliberately no count, rollup, or "N reads moved
 * toward us" — an on-screen tally is the held aggregate claim through the side
 * door. Per-read only.
 */

/** Format an American price for the receipt. */
function fmtPrice(v) {
  if (v == null || v === '') return null;
  const n = Number(v);
  if (!Number.isFinite(n)) return null;
  return n > 0 ? `+${Math.round(n)}` : `${Math.round(n)}`;
}

/**
 * @param {Object} read  { dclv_state, locked_odds, closing_odds }
 * @returns {null|{tone,label,receipt}}  null = render NOTHING
 */
function clvBadge(read) {
  if (!read) return null;
  const state = read.dclv_state;
  // flat and unknown are both silence — a held line is not news, and a missing
  // close is not a zero.
  if (state !== 'positive' && state !== 'negative') return null;

  const graded = fmtPrice(read.locked_odds);
  const closed = fmtPrice(read.closing_odds);
  // No receipt → no badge. A bare label is an assertion without evidence.
  if (!graded || !closed) return null;

  return {
    tone: state === 'positive' ? 'confirm' : 'caution',
    label: state === 'positive' ? 'MOVED TOWARD US' : 'MOVED AWAY',
    receipt: `graded ${graded} · closed ${closed}`,
  };
}

module.exports = { clvBadge, fmtPrice };
