// views_score.jsx — the glass-box "Why this score?" lab + what-if simulator.
// Openable from the composite dial, any of the 6 dimension gauges, or a gap.
// Two variations, switchable live from Preferences:
//   A · Forensic  — dense analytical side-drawer with per-factor bars + ROI
//   B · Editorial — calm centred takeover sheet with narrative + projection
(function () {
  const { useState, useEffect, useLayoutEffect, useRef } = React;
  const D = window.JS_DATA;
  const S = window.JS_SCORE;
  const META = D.SCORE_META;
  const metaFor = key => META.find(m => m.key === key);
  const r = n => Math.round(n);

  // tone helper mirrors primitives.scoreTone but local to avoid coupling
  const toneColor = v => v >= 75 ? 'var(--lime)' : v >= 50 ? 'var(--gold)' : 'var(--coral)';

  // ---- shared simulator state -------------------------------------------
  function useSim() {
    const [active, setActive] = useState([]);
    const toggle = id => setActive(a => a.includes(id) ? a.filter(x => x !== id) : [...a, id]);
    const set = ids => setActive(ids);
    const reset = () => setActive([]);
    const sim = S.simulate(active);
    const base = S.simulate([]);
    return { active, toggle, set, reset, sim, base };
  }

  // ---- LabDial: projection ring (baseline tick → projected arc) ---------
  function LabDial({ value, base, label, size = 132 }) {
    const R = 46, C = 2 * Math.PI * R;
    const off = C * (1 - value / 100);
    const delta = r(value) - r(base);
    const col = delta > 0 ? 'var(--lime)' : toneColor(value);
    // baseline marker angle
    const baseAng = (base / 100) * 360 - 90;
    const bx = 50 + Math.cos(baseAng * Math.PI / 180) * R;
    const by = 50 + Math.sin(baseAng * Math.PI / 180) * R;
    return (
      <div className="lab-dial" style={{ width: size, height: size }}>
        <svg viewBox="0 0 100 100">
          <circle cx="50" cy="50" r={R} fill="none" stroke="var(--surface-2)" strokeWidth="7" />
          <circle cx="50" cy="50" r={R} fill="none" stroke={col} strokeWidth="7" strokeLinecap="round"
            strokeDasharray={C} strokeDashoffset={off} transform="rotate(-90 50 50)"
            style={{ filter: 'drop-shadow(0 0 7px ' + col + ')', transition: 'stroke-dashoffset .6s var(--ease-out-expo), stroke .4s' }} />
          {delta > 0 && <circle cx={bx} cy={by} r="2.4" fill="var(--mute)" />}
        </svg>
        <div className="ld-center">
          <div className="ld-num" style={{ color: col }}>{r(value)}</div>
          {label && <div className="ld-lbl">{label}</div>}
          {delta !== 0
            ? <div className="ld-delta">▴ +{delta} <span>from {r(base)}</span></div>
            : <div className="ld-delta ld-delta--flat">baseline</div>}
        </div>
      </div>
    );
  }

  // ---- FactorBar: one driver, current sub → projected sub ---------------
  function FactorBar({ f, cur, proj }) {
    const changed = proj != null && r(proj) !== r(cur);
    return (
      <div className="lab-factor">
        <div className="lf-top">
          <span className="lf-name">{f.name}</span>
          <span className="lf-weight">×{f.w.toFixed(2)}</span>
          <span className="lf-vals">
            <b style={{ color: toneColor(cur) }}>{r(cur)}</b>
            {changed && <><span className="lf-arrow">→</span><b style={{ color: 'var(--lime)' }}>{r(proj)}</b></>}
          </span>
        </div>
        <div className="lf-track">
          <div className="lf-fill" style={{ width: r(cur) + '%', background: toneColor(cur) }} />
          {changed && <div className="lf-gain" style={{ left: r(cur) + '%', width: (r(proj) - r(cur)) + '%' }} />}
        </div>
        <div className="lf-meta"><span className="lf-why">{f.why}</span></div>
        <div className="lf-evid"><span className="js-tag">Evidence</span> {f.evidence}</div>
      </div>
    );
  }

  // ---- FixToggle: a checkable fix with marginal ROI ---------------------
  function FixToggle({ fx, on, lift, onToggle, variant }) {
    const g = D.AUDIT.analysis.gaps[fx.gap];
    return (
      <button className={'lab-fix' + (on ? ' is-on' : '') + (variant === 'card' ? ' lab-fix--card' : '')} onClick={onToggle}>
        <span className="fx-check">{on && <Icon name="check" size={14} sw={3} />}</span>
        <span className="fx-body">
          <span className="fx-label">{fx.label}</span>
          <span className="fx-desc">{fx.desc}</span>
          <span className="fx-tags">
            <span className={'fx-effort fx-effort--' + fx.effort.toLowerCase()}>{fx.effort} effort</span>
            <span className="fx-lift">+{lift.toFixed(1)} JiffyScore</span>
          </span>
        </span>
      </button>
    );
  }

  // dims touched by a fix-set, for the overview list
  function dimRows(sim, base) {
    return S.DIM_ORDER.map(key => ({
      key, name: metaFor(key).name, k: metaFor(key).k,
      cur: base.dims[key], proj: sim.dims[key],
    }));
  }

  // ====================================================================
  //  VARIATION A — FORENSIC SIDE-DRAWER
  // ====================================================================
  function ForensicDrawer({ focus, lab, onClose, onShip }) {
    const { active, toggle, sim, base, reset } = lab;
    const isTotal = focus === 'total';
    const dimKey = isTotal ? null : focus;
    const m = isTotal ? null : metaFor(dimKey);
    const factors = isTotal ? null : S.SCORE_MODEL.dims[dimKey].factors;
    const relFixes = (isTotal ? S.FIXES : S.FIXES.filter(f => f.effects[dimKey]));
    const headVal = isTotal ? sim.composite : sim.dims[dimKey];
    const headBase = isTotal ? base.composite : base.dims[dimKey];
    const dimWeight = isTotal ? null : S.SCORE_MODEL.weights[dimKey];

    return (
      <div className="lab-drawer-inner">
        <div className="lab-head">
          <div>
            <p className="eyebrow">Glass box · why this score</p>
            <h2>{isTotal ? <>The <em>JiffyScore</em></> : <>{m.name}</>}</h2>
            <p className="lab-sub">{isTotal
              ? 'A weighted blend of six dimensions. Toggle the fixes below to watch it move.'
              : <>Code <b>{m.k}</b> · weighted <b>{dimWeight.toFixed(2)}</b> of the composite. {m.why}</>}</p>
          </div>
          <button className="lab-close" onClick={onClose} aria-label="Close"><Icon name="x" size={18} /></button>
        </div>

        <div className="lab-body">
          <div className="lab-dialrow">
            <LabDial value={headVal} base={headBase} label={isTotal ? 'Projected' : m.k} size={138} />
            <div className="lab-formula">
              <div className="js-tag" style={{ marginBottom: 8 }}>How it’s computed</div>
              {isTotal
                ? <code className="lab-code">JiffyScore = Σ (dimension × weight)</code>
                : <code className="lab-code">{m.name} = Σ (factor × weight)</code>}
              <p className="lab-fnote">{isTotal
                ? 'No black boxes — every input is visible and every fix is reversible.'
                : 'Each driver below contributes its sub-score times its weight.'}</p>
            </div>
          </div>

          {isTotal ? (
            <div className="lab-sect">
              <div className="lab-secthead"><span className="js-tag">Six dimensions</span><span className="js-tag">now → projected</span></div>
              <div className="lab-dimlist">
                {dimRows(sim, base).map(d => {
                  const ch = r(d.proj) !== r(d.cur);
                  return (
                    <div key={d.key} className="lab-dimrow">
                      <span className="ldr-name">{d.name} <i>{d.k}</i></span>
                      <div className="lf-track" style={{ flex: 1 }}>
                        <div className="lf-fill" style={{ width: r(d.cur) + '%', background: toneColor(d.cur) }} />
                        {ch && <div className="lf-gain" style={{ left: r(d.cur) + '%', width: (r(d.proj) - r(d.cur)) + '%' }} />}
                      </div>
                      <span className="ldr-val"><b style={{ color: toneColor(d.cur) }}>{r(d.cur)}</b>{ch && <><span className="lf-arrow">→</span><b style={{ color: 'var(--lime)' }}>{r(d.proj)}</b></>}</span>
                    </div>
                  );
                })}
              </div>
            </div>
          ) : (
            <div className="lab-sect">
              <div className="lab-secthead"><span className="js-tag">What drives {m.k}</span></div>
              {factors.map(f => {
                const ov = sim.overrides[dimKey] || {};
                return <FactorBar key={f.name} f={f} cur={f.sub} proj={ov[f.name]} />;
              })}
            </div>
          )}

          <div className="lab-sect">
            <div className="lab-secthead">
              <span className="js-tag">What if you fixed{isTotal ? ' it' : ' this'}?</span>
              {active.length > 0 && <button className="lab-reset" onClick={reset}>Reset</button>}
            </div>
            <div className="lab-fixes">
              {relFixes
                .map(fx => ({ fx, lift: S.marginalLift(fx.id, active) }))
                .sort((a, b) => b.lift - a.lift)
                .map(({ fx, lift }) => (
                  <FixToggle key={fx.id} fx={fx} on={active.includes(fx.id)} lift={lift} onToggle={() => toggle(fx.id)} />
                ))}
            </div>
          </div>
        </div>

        <div className="lab-foot">
          <div className="lf-score">
            <span className="js-tag">JiffyScore</span>
            <span className="lf-now">{r(base.composite)}</span>
            <Icon name="arrow" size={16} style={{ color: 'var(--mute)' }} />
            <span className="lf-proj" style={{ color: active.length ? 'var(--lime)' : 'var(--ink)' }}>{r(sim.composite)}</span>
            {active.length > 0 && <span className="lf-deltabig">+{r(sim.composite) - r(base.composite)}</span>}
          </div>
          <Btn kind="gold" icon="sparkles" disabled={active.length === 0} onClick={() => onShip(active)}>
            {active.length ? 'Generate ' + active.length + ' fix' + (active.length > 1 ? 'es' : '') : 'Pick a fix to project'}
          </Btn>
        </div>
      </div>
    );
  }

  // ====================================================================
  //  VARIATION B — EDITORIAL TAKEOVER SHEET
  // ====================================================================
  function EditorialSheet({ focus, lab, onClose, onShip }) {
    const { active, toggle, set, sim, base, reset } = lab;
    const isTotal = focus === 'total';
    const dimKey = isTotal ? null : focus;
    const m = isTotal ? null : metaFor(dimKey);
    const factors = isTotal ? null : S.SCORE_MODEL.dims[dimKey].factors;
    const relFixes = (isTotal ? S.FIXES : S.FIXES.filter(f => f.effects[dimKey]));
    const headVal = isTotal ? sim.composite : sim.dims[dimKey];
    const headBase = isTotal ? base.composite : base.dims[dimKey];
    const allOn = relFixes.every(f => active.includes(f.id));

    // weakest driver narrative
    const weakest = !isTotal && factors ? factors.slice().sort((a, b) => a.sub - b.sub)[0] : null;
    const projHistory = D.SCORE_HISTORY.map(h => ({ ...h }));
    // append a projected forward point if fixes are picked
    const fwd = active.length ? [...projHistory, { date: '90d', total: r(sim.composite) }] : projHistory;

    return (
      <div className="lab-sheet-inner">
        <button className="lab-close lab-close--sheet" onClick={onClose} aria-label="Close"><Icon name="x" size={18} /></button>
        <div className="es-grid">
          {/* left — the why */}
          <div className="es-why">
            <p className="eyebrow">Glass box · the reasoning</p>
            <h2 className="es-h">{isTotal
              ? <>Why <em>{r(base.composite)}</em>?</>
              : <>Why <em>{r(base.dims[dimKey])}</em> on {m.name.toLowerCase()}?</>}</h2>
            <p className="es-lead">{isTotal
              ? 'Your JiffyScore is a weighted blend of six dimensions — strong local and technical foundations, held back by how citable your content is. Here is every input, in the open.'
              : <>{m.why} {weakest && <>The drag is <b>{weakest.name.toLowerCase()}</b> — {weakest.evidence.toLowerCase()}.</>}</>}</p>

            {isTotal ? (
              <div className="es-dimlist">
                {dimRows(sim, base).map(d => {
                  const ch = r(d.proj) !== r(d.cur);
                  return (
                    <div key={d.key} className="es-dimrow">
                      <span className="ed-name">{d.name}</span>
                      <div className="lf-track" style={{ flex: 1 }}>
                        <div className="lf-fill" style={{ width: r(d.cur) + '%', background: toneColor(d.cur) }} />
                        {ch && <div className="lf-gain" style={{ left: r(d.cur) + '%', width: (r(d.proj) - r(d.cur)) + '%' }} />}
                      </div>
                      <span className="ed-val">{r(ch ? d.proj : d.cur)}</span>
                    </div>
                  );
                })}
              </div>
            ) : (
              <div className="es-factors">
                {factors.map(f => {
                  const ov = sim.overrides[dimKey] || {};
                  const proj = ov[f.name];
                  const ch = proj != null && r(proj) !== r(f.sub);
                  return (
                    <div key={f.name} className={'es-factor' + (ch ? ' is-lifted' : '')}>
                      <div className="ef-head"><span className="ef-name">{f.name}</span>
                        <span className="ef-val" style={{ color: ch ? 'var(--lime)' : toneColor(f.sub) }}>{r(ch ? proj : f.sub)}</span></div>
                      <p className="ef-evid">{f.evidence}</p>
                    </div>
                  );
                })}
              </div>
            )}
          </div>

          {/* right — the projection + simulator */}
          <div className="es-sim">
            <div className="es-dialwrap">
              <LabDial value={headVal} base={headBase} label={isTotal ? 'JiffyScore' : m.k} size={196} />
              <p className="es-projnote">{active.length
                ? <>Ship {active.length} fix{active.length > 1 ? 'es' : ''} → <b>{r(sim.composite)}</b> composite</>
                : 'Pick the fixes you’d ship to project the lift.'}</p>
            </div>

            <div className="es-pack">
              <div className="es-packhead">
                <span className="js-tag">Sleep-on-it simulator</span>
                <button className="lab-reset" onClick={() => allOn ? reset() : set(relFixes.map(f => f.id))}>{allOn ? 'Clear' : 'Project full pack'}</button>
              </div>
              <div className="es-fixes">
                {relFixes.map(fx => (
                  <FixToggle key={fx.id} fx={fx} on={active.includes(fx.id)} lift={S.marginalLift(fx.id, active)} onToggle={() => toggle(fx.id)} variant="card" />
                ))}
              </div>
            </div>

            {active.length > 0 && (
              <div className="es-timeline">
                <div className="js-tag" style={{ marginBottom: 8 }}>Projected — ship the pack, hold the line</div>
                <Sparkline data={fwd} h={84} accent="var(--lime)" />
                <div className="es-tlabels">{fwd.map((h, i) => <span key={i} className={'js-tag' + (i === fwd.length - 1 ? ' es-proj' : '')}>{h.date}</span>)}</div>
              </div>
            )}

            <Btn kind="gold" icon="sparkles" disabled={active.length === 0} onClick={() => onShip(active)} style={{ width: '100%', justifyContent: 'center', marginTop: 4 }}>
              {active.length ? 'Generate this pack · +' + (r(sim.composite) - r(base.composite)) + ' projected' : 'Pick fixes to ship'}
            </Btn>
          </div>
        </div>
      </div>
    );
  }

  // ====================================================================
  //  ScoreLab wrapper — open/close transition + variation switch
  // ====================================================================
  function ScoreLab({ focus, style, onClose, onShip }) {
    const open = focus != null;
    const [active, setActive] = useState(false);
    const [mountFocus, setMountFocus] = useState(focus);
    const elRef = useRef(null);
    const lab = useSim();

    // keep last focus while closing so content doesn't vanish mid-transition
    useEffect(() => { if (focus != null) setMountFocus(focus); }, [focus]);
    // reset the simulator whenever a fresh target is opened
    useEffect(() => { if (focus != null) lab.reset(); /* eslint-disable-next-line */ }, [focus]);

    useLayoutEffect(() => {
      const node = elRef.current;
      if (open) { if (node) void node.offsetWidth; setActive(true); }
      else setActive(false);
    }, [open]);

    const editorial = style === 'editorial';
    const shipAndClose = ids => { onShip(ids); onClose(); };

    return (
      <>
        <div className={'lab-backdrop' + (active ? ' is-open' : '')} onClick={onClose} />
        {editorial ? (
          <div ref={elRef} className={'lab-sheet' + (active ? ' is-open' : '')} role="dialog" aria-label="Why this score" aria-hidden={!open}>
            {mountFocus != null && <EditorialSheet focus={mountFocus} lab={lab} onClose={onClose} onShip={shipAndClose} />}
          </div>
        ) : (
          <aside ref={elRef} className={'lab-drawer' + (active ? ' is-open' : '')} role="dialog" aria-label="Why this score" aria-hidden={!open}>
            {mountFocus != null && <ForensicDrawer focus={mountFocus} lab={lab} onClose={onClose} onShip={shipAndClose} />}
          </aside>
        )}
      </>
    );
  }

  window.ScoreLab = ScoreLab;
})();
