/* ============================================================
   market-sensor-cards.jsx — body components.
   [B] PlanStrip  [C] Catalysts  [D] SensorDetail  [E] Watchlist
   [F] Method  + MsrAccordion (mobile collapse for D & E)
   ============================================================ */
const { useState: useStateC } = React;

const msrTrunc = (s, n) => (s && s.length > n) ? s.slice(0, n - 1).trimEnd() + '\u2026' : (s || '');

/* ---- [B] PLAN INTEGRATION STRIP ---- */
function MsrPlanStrip({ data }) {
  const pi = data.plan_integration || {};
  const s = data.state || {};
  const tone = window.msrTone(s.color);
  const meta = window.msrStateMeta(s.label);
  return (
    <div className="msr-plan">
      <a className="msr-pcard link" href="structural-bias.html">
        <div className="pk">Structural Bias <span className="lnk">{'\u2197'}</span></div>
        <div className="pv">{pi.sbs_band || '—'}</div>
        <p className="ps">{msrTrunc(pi.sbs_summary, 150)}</p>
      </a>
      <div className="msr-pcard">
        <div className="pk">Market Sensor</div>
        <span className="msr-chip pchip" style={{ ['--tone']: tone }}><span className="gly">{meta.glyph}</span>{pi.sensor_state || s.label}</span>
        <p className="ps">{msrTrunc(s.one_line_message, 130)}</p>
      </div>
      <div className="msr-pcard">
        <div className="pk">Combined Action</div>
        <div className="pv tone" style={{ ['--tone']: tone, fontSize: 17 }}>{pi.combined_action || s.action_label}</div>
        <p className="ps">{pi.precedence_note || 'Sensor downgrades conviction; never inverts SBS direction.'}</p>
      </div>
    </div>
  );
}

/* ---- [C] CATALYSTS ---- */
function MsrCatalystCard({ c, feature }) {
  const badge = c.reaction_badge;
  const sent = c.sentiment_direction || 'neutral';
  return (
    <a className={`msr-cat ${feature ? 'feature' : ''}`} href={c.url || '#'} target="_blank" rel="noopener noreferrer">
      <div className="msr-cat-top">
        {badge
          ? <span className={`msr-rbadge ${String(badge).toLowerCase()}`}>{badge.replace('_', ' ')}</span>
          : <span className={`msr-cat-dirsent ${sent}`} style={{ border: '1px solid var(--line)', padding: '4px 9px', borderRadius: 5 }}>{(c.qqq_channel || sent)}</span>}
        <span className="msr-cat-time">{window.msrRelTime(c.published_at_et)}</span>
      </div>
      <p className="msr-cat-hl">{c.headline}</p>
      <div className="msr-cat-meta">
        <span className="msr-cat-src">{c.source}</span>
        {(c.tickers || []).slice(0, 4).map((t, i) => <span key={i} className="msr-cat-tick">{t}</span>)}
        <span className={`msr-cat-dirsent ${sent}`}>{sent}</span>
      </div>
      {(c.reaction_detail || c.summary) && (
        <div className="msr-cat-react">
          {c.reaction_detail ? <React.Fragment><b>Reaction:</b> {c.reaction_detail}</React.Fragment> : msrTrunc(c.summary, 150)}
        </div>
      )}
    </a>
  );
}
function MsrCatalysts({ data }) {
  const cats = data.catalysts || [];
  const newsOffline = (data.data_freshness || {}).news === 'unavailable';
  const alarm = (data.state || {}).label === 'DIRECTIONAL_RISK';
  return (
    <div className="msr-sec">
      <div className="msr-sec-head">
        <div>
          <div className="msr-sec-eb">News overlay</div>
          <h2 className="msr-sec-h">Catalysts</h2>
        </div>
        {newsOffline
          ? <span className="msr-sec-chip">News feed offline today</span>
          : <span className="msr-sec-note">Top stories from the last 48h, ranked by QQQ-relevance. News confirms regime — it isn't a signal on its own.</span>}
      </div>
      {cats.length === 0
        ? <div className="msr-cats-empty">No high-relevance catalysts in the last 48h. The sensor is reading positioning &amp; technicals only.</div>
        : <div className="msr-cats">{cats.slice(0, 5).map((c, i) => <MsrCatalystCard key={i} c={c} feature={alarm && i === 0} />)}</div>}
    </div>
  );
}

/* ---- [D] SENSOR DETAIL ---- */
function MsrZTile({ label, z, thr }) {
  const LO = -3.5, HI = 3.5;
  const pos = (v) => Math.max(0, Math.min(100, (v - LO) / (HI - LO) * 100));
  const hi = Math.abs(thr != null ? thr : 1.5);
  const cls = z == null ? 'mid' : z <= -hi ? 'neg' : z >= hi ? 'pos' : 'mid';
  const zp = pos(z || 0), c0 = pos(0);
  const left = Math.min(zp, c0), width = Math.abs(zp - c0);
  const fillColor = cls === 'neg' ? 'var(--bear)' : cls === 'pos' ? 'var(--bull)' : 'var(--blue)';
  return (
    <div className="msr-stile">
      <div className="sl">{label}</div>
      <div className={`sv ${cls}`}>{window.msrFmtZ(z)}<span className="ar">{z == null ? '' : z < 0 ? '\u25BC' : '\u25B2'}</span></div>
      <div className="msr-stile-bar">
        <div className="msr-stile-fill" style={{ left: left + '%', width: width + '%', background: fillColor }} />
        <div className="msr-stile-thr" style={{ left: pos(-hi) + '%' }} />
      </div>
      <div className="msr-stile-tl">whipsaw at {window.msrFmtZ(-hi)}</div>
    </div>
  );
}
function MsrSensorDetail({ data }) {
  const sen = data.sensors || {};
  const thr = sen.thresholds ? sen.thresholds.whipsaw_z : -1.5;
  return (
    <MsrAccordion eb="Under the hood" title="Sensor Detail" note="Z-scores vs the trailing 60-day window. Past the whipsaw line is a wide-range warning.">
      <div className="msr-sensors">
        <MsrZTile label="Composite z" z={sen.composite_z} thr={thr} />
        <MsrZTile label="Leadership z" z={sen.leadership_z} thr={thr} />
        <MsrZTile label="Macro z" z={sen.macro_z} thr={thr} />
      </div>
    </MsrAccordion>
  );
}

/* ---- [E] WATCHLIST — the 18 composite-sensor inputs, grouped ---- */
function MsrWatchTile({ w, accent }) {
  const px = w.premarket_last != null ? w.premarket_last : w.last_close;
  const chg = w.premarket_change_pct != null ? w.premarket_change_pct : w.change_pct_close;
  const src = w.premarket_last != null ? 'pre-mkt' : 'prev close';
  const chgCls = chg == null ? 'flat' : Math.abs(chg) < 0.05 ? 'flat' : chg > 0 ? 'pos' : 'neg';
  const z = w.z_score;
  const zMag = z == null ? 0 : Math.abs(z);
  const zBg = zMag >= 2 ? 'rgba(204,122,108,0.22)' : zMag >= 1 ? 'rgba(129,151,206,0.18)' : 'rgba(156,163,174,0.12)';
  const zCol = zMag >= 2 ? '#E0A99B' : zMag >= 1 ? '#E9D2A6' : 'var(--blue)';
  const imp = w.impact_weight;
  const sign = w.qqq_sign;
  const tip = sign === 1 ? `${w.sym} · pro-cyclical — moves with QQQ · impact ${imp != null ? imp : '—'}/100`
            : sign === -1 ? `${w.sym} · counter-cyclical — a move UP is bearish for QQQ · impact ${imp != null ? imp : '—'}/100`
            : w.sym;
  return (
    <div className="msr-wt" title={tip}>
      <div className="msr-wt-top">
        <span className="msr-wt-sym">{w.sym}{sign === -1 && <span className="msr-wt-sign" title="counter-cyclical">{'\u21C5'}</span>}</span>
        {z != null && <span className="msr-wt-z" style={{ background: zBg, color: zCol }}>z {window.msrFmtZ(z)}</span>}
      </div>
      <div className="msr-wt-px">{window.msrFmtPrice(px)}</div>
      <div className="msr-wt-row">
        <span className={`msr-wt-chg ${chgCls}`}>{chg == null ? '—' : (chg > 0 ? '\u25B2 ' : chg < 0 ? '\u25BC ' : '') + window.msrFmtPct(chg)}</span>
        <span className="msr-wt-src">{src}</span>
      </div>
      {imp != null && (
        <div className="msr-wt-imp" title={`Impact weight ${imp}/100 in the composite z`}>
          <span className="il">impact</span>
          <span className="ibar"><span className="ifill" style={{ width: imp + '%', background: accent }} /></span>
          <span className="iv">{imp}</span>
        </div>
      )}
    </div>
  );
}
function MsrWatchGroup({ name, desc, z, items, accent, cols }) {
  if (!items.length) return null;
  return (
    <div className="msr-watch-group">
      <div className="msr-watch-gh">
        <span className="gn">{name}</span>
        <span className="gd">{items.length} · {desc}</span>
        {z != null && <span className="gz" style={{ color: accent }}>{name} z {window.msrFmtZ(z)}</span>}
      </div>
      <div className={`msr-watch ${cols}`}>
        {items.map((w, i) => <MsrWatchTile key={i} w={w} accent={accent} />)}
      </div>
    </div>
  );
}
function MsrWatchlist({ data }) {
  const wl = data.watchlist || [];
  const sen = data.sensors || {};
  const lead = wl.filter((w) => (w.group || 'Leadership') === 'Leadership');
  const macro = wl.filter((w) => w.group === 'Macro');
  const grouped = lead.length > 0 || macro.length > 0;
  return (
    <MsrAccordion eb="Trace the score" title="Watchlist" note={`The exact ${wl.length} inputs feeding the composite — what you see is what produces the score.`}>
      {grouped ? (
        <React.Fragment>
          <MsrWatchGroup name="Leadership" desc="pro-cyclical · moves with QQQ" z={sen.leadership_z} items={lead} accent="var(--mustard)" cols="lead" />
          <MsrWatchGroup name="Macro" desc="counter-cyclical · a move up is bearish for QQQ" z={sen.macro_z} items={macro} accent="var(--blue)" cols="macro" />
        </React.Fragment>
      ) : (
        <div className="msr-watch lead">{wl.map((w, i) => <MsrWatchTile key={i} w={w} accent="var(--mustard)" />)}</div>
      )}
    </MsrAccordion>
  );
}

/* ---- [F] METHOD ---- */
function MsrMethod({ data }) {
  const [open, setOpen] = useStateC(false);
  const backed = data.meta && data.meta.backed_by;
  return (
    <div className={`msr-method ${open ? 'open' : ''}`}>
      <button className="msr-method-h" onClick={() => setOpen((o) => !o)}>
        <span>Method &amp; disclaimer</span><span className="ar">{'\u25B8'}</span>
      </button>
      {open && (
        <div className="msr-method-body">
          <p>The Market Sensor reads a <b>volatility regime</b>, not a direction. It blends a <b>leadership z-score</b> (Mag-7 + semis relative to QQQ) and a <b>macro z-score</b> (10Y, dollar, VIX) into a composite, then maps that — combined with the Structural Bias band — to a single action via a backtested decision matrix. It adjusts <b>size, stops, and entry style</b>; it never tells you to buy or sell.</p>
          {backed && <p>Validated against a 12-month backtest + 6-week live window (<code>{backed}</code>). WHIPSAW is a wide-range warning, not a bearish call; MILD_BEAR is informational only; DIRECTIONAL_RISK (WHIPSAW + confirmed catalyst) is the one state that alarms.</p>}
          <p className="dis">Education only — not financial advice and not a standalone buy/sell signal. All execution follows the published QQQ plan and risk rules. Operated from Singapore. Past performance is not indicative of future results.</p>
        </div>
      )}
    </div>
  );
}

/* ---- mobile accordion wrapper (always open on desktop) ---- */
function MsrAccordion({ eb, title, note, children }) {
  const [open, setOpen] = useStateC(false);
  return (
    <div className={`msr-sec msr-acc ${open ? 'open' : ''}`}>
      <button className="msr-acc-h" onClick={() => setOpen((o) => !o)}><span>{title}</span><span className="ar">{'\u25B8'}</span></button>
      <div className="msr-acc-body">
        <div className="msr-sec-head">
          <div>
            {eb && <div className="msr-sec-eb">{eb}</div>}
            <h2 className="msr-sec-h">{title}</h2>
          </div>
          {note && <span className="msr-sec-note">{note}</span>}
        </div>
        {children}
      </div>
    </div>
  );
}

Object.assign(window, { MsrPlanStrip, MsrCatalysts, MsrSensorDetail, MsrWatchlist, MsrMethod, MsrAccordion });
