/* ============================================================
   market-sensor-options-drift.jsx — Options Flow (context).
   Two side-by-side context cards (Mag-7 · QQQ) that slot under
   the primary pulse/sectors reads and ABOVE Decoupling Breadth
   on the Risk Sensing page.

   Context only — not a trigger, not in the score, not sizing.
   Renders ONLY value + context_note + regime_guard; the $M
   readings appear at most as a muted "Raw flow readings"
   footnote (audit only, never bull/bear styled).

   Self-loads data/options_drift.json (via window.__resources.
   optdrift or the default path). Non-blocking: on a missing or
   stale feed it shows a quiet "updating" placeholder and never
   errors. Exports window.MsrOptionsDrift. Reuses .msr tokens.
   ============================================================ */
(function () {
  const { useState: odState, useEffect: odEffect, useCallback: odCb } = React;

  const _MO = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

  /* enum value -> { headline word, tone }. Tone: hedged = faint sage,
     inert = grey (never bullish), caution = soft amber. */
  const MAG7_MAP = {
    negative: { v: 'Defensive lean', tone: 'hedged' },
    positive: { v: 'Call-side \u00B7 neutral', tone: 'inert' },
    flat: { v: 'Balanced', tone: 'inert' },
  };
  const QQQ_MAP = {
    extreme_call_dominance: { v: "Don't chase", tone: 'caution' },
    heavy_put_selling: { v: 'Downside faded', tone: 'hedged' },
    deep_neg_call_drift: { v: 'Call washout', tone: 'hedged' },
    both_negative: { v: 'Broad de-risking', tone: 'hedged' },
    both_positive: { v: 'Two-sided', tone: 'inert' },
    neutral: { v: 'No edge', tone: 'inert' },
  };

  const DOT = '\u00B7';

  const odFmtM = (v) => (v == null || isNaN(v)) ? '\u2014'
    : (v < 0 ? '\u2212' : '+') + '$' + Math.abs(v).toFixed(1) + 'M';

  function odAsofLabel(d) {
    if (!/^\d{4}-\d{2}-\d{2}$/.test(d || '')) return d || '\u2014';
    const [, m, dd] = d.split('-').map(Number);
    return _MO[m - 1] + ' ' + dd;
  }

  /* staleness guard: key off meta.published_at (when the feed published for
     THIS session), NOT as_of_date. as_of_date is the PRIOR cash session by
     design and can legitimately sit several days back over holiday gaps
     (e.g. Juneteenth 6/19 -> last session 6/18 -> plan 6/22), so it must not
     drive freshness. Stale only if the publish itself is >5 days old. */
  function odIsStale(pub) {
    const t = Date.parse(pub || '');
    if (isNaN(t)) return true;
    return (Date.now() - t) > 5 * 864e5;
  }

  function OdCard({ k, map, note, foot }) {
    return (
      <div className={'ofx-card ' + map.tone}>
        <div className="ofx-top">
          <span className="ofx-k">{k}</span>
        </div>
        <div className="ofx-v"><span className="dot" />{map.v}</div>
        <p className="ofx-note">{note}</p>
        <div className="ofx-foot">{foot}</div>
      </div>);
  }

  function OdStale() {
    return (
      <div className="ofx-stale">
        <span className="ofx-stale-dot" />
        <div>
          <div className="ofx-stale-h">Options drift read updating</div>
          <div className="ofx-stale-d">The pre-market options-flow feed hasn't published for this session yet. It's non-blocking — it never holds back the primary pulse / sectors read.</div>
        </div>
      </div>);
  }

  function OdSectionHead({ right }) {
    return (
      <div className="msr-sec-head">
        <div>
          <div className="msr-sec-eb">Macro overlay {DOT} options flow</div>
          <h2 className="msr-sec-h">Options Flow</h2>
        </div>
        {right}
      </div>);
  }

  function MsrOptionsDrift() {
    const [data, setData] = odState(null);

    const load = odCb(() => {
      const R = window.__resources || {};
      const url = R.optdrift || 'data/options_drift.json';
      fetch(url, { cache: 'no-store' }).
        then((r) => { if (!r.ok) throw new Error('options_drift.json ' + r.status); return r.json(); }).
        then((d) => setData(d)).
        catch(() => setData(false)); /* false = checked, absent */
    }, []);
    odEffect(() => { load(); }, [load]);

    if (data === null) return null; /* not checked yet — never block */

    const note = <span className="msr-sec-note">Two pre-market reads from QuantData options flow. Context only — not a trigger, not part of the score.</span>;

    /* missing / failed feed -> quiet placeholder */
    if (data === false) {
      return <div className="msr-sec"><OdSectionHead right={note} /><OdStale /></div>;
    }

    const meta = data.meta || {};
    const m7 = data.mag7_drift_sign || {};
    const qq = data.qqq_drift_sign || {};

    /* stale date -> placeholder */
    if (odIsStale(meta.published_at)) {
      return <div className="msr-sec"><OdSectionHead right={note} /><OdStale /></div>;
    }

    const suppressed = (b) => (b.regime_guard === 'suppressed');
    /* read either the handoff schema (calls_drift_m) or the production
       schema (raw_flow_readings_reference_only.calls_m) */
    const raw = (b) => b.raw_flow_readings_reference_only || {};
    const calls = (b) => (b.calls_drift_m != null ? b.calls_drift_m : raw(b).calls_m);
    const puts = (b) => (b.puts_drift_m != null ? b.puts_drift_m : raw(b).puts_m);
    const diff = (b) => (b.diff_m != null ? b.diff_m : raw(b).calls_minus_puts_m);

    const m7map = suppressed(m7)
      ? { v: 'Set aside', tone: 'suppressed' }
      : (MAG7_MAP[m7.value] || { v: '\u2014', tone: 'inert' });
    const qqmap = suppressed(qq)
      ? { v: 'Set aside', tone: 'suppressed' }
      : (QQQ_MAP[qq.value] || { v: '\u2014', tone: 'inert' });

    const m7foot = suppressed(m7)
      ? <span><b>Raw flow readings</b> {DOT} withheld while suppressed</span>
      : <span><b>Raw flow readings</b> {DOT} calls <span className="num">{odFmtM(calls(m7))}</span> {DOT} puts <span className="num">{odFmtM(puts(m7))}</span></span>;
    const qqfoot = suppressed(qq)
      ? <span><b>Raw flow readings</b> {DOT} withheld while suppressed</span>
      : <span><b>Raw flow readings</b> {DOT} calls <span className="num">{odFmtM(calls(qq))}</span> {DOT} puts <span className="num">{odFmtM(puts(qq))}</span>{diff(qq) != null ? <span> {DOT} diff <span className="num">{odFmtM(diff(qq))}</span></span> : null}</span>;

    return (
      <div className="msr-sec">
        <OdSectionHead right={null} />
        <div className="ofx-grid">
          <OdCard k={'Mag-7 ' + DOT + ' options drift'} map={m7map} note={m7.context_note} foot={m7foot} />
          <OdCard k={'QQQ ' + DOT + ' options drift'} map={qqmap} note={qq.context_note} foot={qqfoot} />
        </div>
      </div>);
  }

  Object.assign(window, { MsrOptionsDrift });
})();
