/* Skyhearth Codex — element-wheel bestiary (alternate to the grid)
 * Places all 34 forms on the leak ring: pures on their element node, duals on
 * the chord between two, multi inward, junk at the hub. Reinforces the §3
 * teaching (coverage is uneven; Fire's blends all need a locked vein) and lets
 * you read element coverage at a glance. Click a marker to inspect. */
/* global CX_ELEM, FORMS, ElComposite, ElChip, LedgerBars, TriggerSlip, restLabel, timeAwakePct, AVAIL_META, VERDICT_META */
const svW = (id) => window.CodexCollection.sv(id);

const WRING = ['fire','water','earth','nature','ice','lightning','wind','holy'];
const widx = (e) => WRING.indexOf(e);
const wAng = (e) => -Math.PI/2 + widx(e) * (Math.PI*2/8);

function formPos(f, cx, cy, R) {
  if (f.profile === 'mote') return [cx, cy];
  if (f.profile === 'junk') return [cx + (f.variant==='rare' ? 30 : -30), cy + 58];
  if (f.profile === 'pure' || f.profile === 'lesser') {
    const a = wAng(f.elements[0]);
    const rr = f.variant === 'rare' ? R*0.66 : R*0.83;
    // nudge off-axis so std + rare of the same vein don't stack
    const off = f.variant === 'rare' ? 0.20 : -0.05;
    return [cx + Math.cos(a+off)*rr, cy + Math.sin(a+off)*rr];
  }
  const vs = f.elements.map(e => [Math.cos(wAng(e)), Math.sin(wAng(e))]);
  let sx=0, sy=0; vs.forEach(([x,y]) => { sx+=x; sy+=y; });
  const ax = sx/vs.length, ay = sy/vs.length, mag = Math.hypot(ax, ay);
  let ang, rr;
  if (f.profile === 'multi') { ang = Math.atan2(ay,ax); rr = R*0.34; }
  else { // dual
    if (mag > 0.18) { ang = Math.atan2(ay,ax); rr = R*0.50 + mag*R*0.14; }
    else { ang = (wAng(f.elements[0]) + wAng(f.elements[1]))/2; rr = R*0.30; }
  }
  return [cx + Math.cos(ang)*rr, cy + Math.sin(ang)*rr];
}

function ElementWheel({ size = 620, onPick, selId, sv = svW, forms = FORMS.filter(f => f.rank === 2) }) {
  const cx = size/2, cy = size/2, R = size/2 - 54;
  // veins are only known once a form is discovered — count & place KNOWN forms only
  const isKnown = (f) => { const s = sv(f.id); return !!(s.seen || s.tk); };
  const known = forms.filter(isKnown);
  const rumored = forms.filter(f => !isKnown(f));
  const cover = {}; WRING.forEach(e => cover[e] = 0);
  known.forEach(f => f.elements.forEach(e => cover[e]++));
  const maxCover = Math.max(1, ...Object.values(cover));

  return (
    <svg viewBox={`0 0 ${size} ${size}`} style={{ width:size, height:size, display:'block' }}>
      {/* leak-ring octagon (directional cycle) */}
      {WRING.map((e,i) => {
        const a0 = wAng(e), a1 = wAng(WRING[(i+1)%8]);
        const x0 = cx+Math.cos(a0)*R, y0 = cy+Math.sin(a0)*R;
        const x1 = cx+Math.cos(a1)*R, y1 = cy+Math.sin(a1)*R;
        const mx = (x0+x1)/2, my = (y0+y1)/2;
        return <g key={'leak'+e}>
          <line x1={x0} y1={y0} x2={x1} y2={y1} stroke="var(--ink-ghost)" strokeWidth="1" strokeDasharray="3 4" />
          <circle cx={mx} cy={my} r="1.6" fill="var(--ink-faded)" />
        </g>;
      })}

      {/* dual/multi chords (faint) — only for discovered forms */}
      {known.filter(f => f.elements.length >= 2).map(f => {
        const [fx,fy] = formPos(f, cx, cy, R);
        return f.elements.map(e => {
          const a = wAng(e);
          return <line key={f.id+e} x1={cx+Math.cos(a)*R} y1={cy+Math.sin(a)*R} x2={fx} y2={fy}
            stroke={selId===f.id ? CX_ELEM[e].color : 'var(--ink-trace)'} strokeWidth={selId===f.id?1.2:0.6} />;
        });
      })}

      {/* element nodes (rim) with coverage halo */}
      {WRING.map(e => {
        const a = wAng(e), x = cx+Math.cos(a)*R, y = cy+Math.sin(a)*R;
        const halo = 12 + (cover[e]/maxCover)*16;
        return <g key={'node'+e}>
          <circle cx={x} cy={y} r={halo} fill={CX_ELEM[e].color} opacity="0.10" />
          <circle cx={x} cy={y} r="17" fill="var(--paper-light)" stroke={CX_ELEM[e].color} strokeWidth="2" />
          <image href={CX_ELEM[e].icon} x={x-14} y={y-14} width="28" height="28"
            preserveAspectRatio="xMidYMid meet" style={{ filter:'url(#sh-dropwhite)' }} />
          <text x={cx+Math.cos(a)*(R+30)} y={cy+Math.sin(a)*(R+30)+4} textAnchor="middle"
            fontFamily="Special Elite, monospace" fontSize="9" letterSpacing="0.1em" fill="var(--sepia)">{CX_ELEM[e].name.toUpperCase()}</text>
          <text x={cx+Math.cos(a)*(R+30)} y={cy+Math.sin(a)*(R+30)+15} textAnchor="middle"
            fontFamily="JetBrains Mono, monospace" fontSize="8" fill="var(--ink-faded)">{cover[e]} known</text>
        </g>;
      })}

      {/* hub label */}
      <text x={cx} y={cy+82} textAnchor="middle" fontFamily="Special Elite, monospace" fontSize="8.5" letterSpacing="0.12em" fill="var(--ink-faded)">{forms.some(f=>f.profile==='junk') ? 'FORMLESS · THE HUB' : forms.some(f=>f.profile==='mote') ? 'THE NASCENT MOTE' : ''}</text>

      {/* known forms — placed at their true vein position */}
      {known.map(f => {
        const [x,y] = formPos(f, cx, cy, R);
        const s = sv(f.id); const t = s.tk?2:1;
        const lead = f.elements[0]; const col = lead ? CX_ELEM[lead].color : 'var(--sepia)';
        const sel = selId === f.id;
        const r = (f.profile==='pure'||f.profile==='lesser') ? 9 : f.profile==='multi' ? 8 : f.profile==='mote' ? 10 : 7;
        return (
          <g key={f.id} onClick={() => onPick(f.id)} style={{ cursor:'pointer' }}>
            {sel && <circle cx={x} cy={y} r={r+6} fill="none" stroke="var(--stamp-amber)" strokeWidth="1.5" />}
            {t===2 && <circle cx={x} cy={y} r={r+3} fill="none" stroke="var(--stamp-amber)" strokeWidth="1" opacity="0.7" />}
            <circle cx={x} cy={y} r={r} fill={col} stroke="var(--ink)" strokeWidth="1.2" />
            {f.elements.length>=2 && <circle cx={x} cy={y} r={r-3.5} fill={CX_ELEM[f.elements[1]].color} />}
          </g>
        );
      })}

      {/* rumored forms — veins unknown, so parked anonymously on the inner fog ring */}
      {rumored.length>0 && <circle cx={cx} cy={cy} r={R*0.44} fill="none" stroke="var(--ink-ghost)" strokeWidth="0.6" strokeDasharray="2 5" />}
      {rumored.map((f,i) => {
        const a = -Math.PI/2 + i*(Math.PI*2/rumored.length);
        const x = cx+Math.cos(a)*R*0.44, y = cy+Math.sin(a)*R*0.44;
        const sel = selId === f.id;
        return (
          <g key={f.id} onClick={() => onPick(f.id)} style={{ cursor:'pointer' }}>
            {sel && <circle cx={x} cy={y} r={11} fill="none" stroke="var(--stamp-amber)" strokeWidth="1.5" />}
            <circle cx={x} cy={y} r={6.5} fill="var(--paper)" stroke="var(--ink-faded)" strokeWidth="1" strokeDasharray="2 2" opacity="0.8" />
            <text x={x} y={y+3.5} textAnchor="middle" fontFamily="IM Fell English SC, serif" fontSize="10" fill="var(--ink-faded)">?</text>
          </g>
        );
      })}
    </svg>
  );
}

// compact side panel for the selected form
function WheelPanel({ f }) {
  const s = svW(f.id); const t = s.tk?2:s.seen?1:0;
  const av = AVAIL_META[f.avail];
  return (
    <div className="panel brackets" style={{ width:330, background:'var(--paper)', padding:'14px 16px', alignSelf:'flex-start' }}>
      <div className="row" style={{ justifyContent:'space-between', alignItems:'flex-start', borderBottom:'1px solid var(--ink-faded)', paddingBottom:8 }}>
        <div className="col" style={{ gap:2 }}>
          <span className="label" style={{ letterSpacing:'0.16em' }}>{t>=1 ? window.plabel(f.profile).toUpperCase() : 'RUMORED'}{f.variant!=='std'&&f.variant!=='only'&&t>=1?' · '+f.variant.toUpperCase():''}</span>
          <span className="fell" style={{ fontSize:20, lineHeight:1, color:t>=1?'var(--ink)':'var(--ink-faded)' }}>{t>=1 ? f.slot : '— unknown —'}</span>
        </div>
        <span className="stamp" style={{ fontSize:8.5, borderColor:av.color, color:av.color }}>{s.seen?f.role:'?'}</span>
      </div>

      <div className="row" style={{ gap:10, alignItems:'center', margin:'10px 0' }}>
        <div style={{ width:64, height:64, border:'1px solid var(--ink-faded)', display:'flex', alignItems:'center', justifyContent:'center', position:'relative', flexShrink:0 }}>
          {t>=1 ? <FormPortrait f={f} size={60} />
                : <><FormPortrait f={f} size={44} ghost /><span style={{ position:'absolute', fontFamily:'IM Fell English SC, serif', fontSize:26, color:'var(--ink-faded)' }}>?</span></>}
        </div>
        <div className="col" style={{ gap:3, flex:1 }}>
          {t>=1
            ? (f.elements.length ? f.elements.map(e=><ElChip key={e} e={e} val={s.seen?f.d.dist[e]:null} />) : <span className="elite" style={{ fontSize:11, color:'var(--sepia)' }}>all veins · 4/4/4/4</span>)
            : <span className="elite" style={{ fontSize:11, color:'var(--ink-faded)' }}>veins unknown — raise or hear it told</span>}
        </div>
      </div>

      {s.seen && <>
        <div className="row" style={{ flexWrap:'wrap', gap:'4px 16px', marginBottom:8 }}>
          {[['Sleep', f.rest.slots===0?'none':f.rest.tod.join('+')],['Life',`${f.life}d`],['Food',f.food],['Bond',f.bond]].map(([k,v])=>(
            <div key={k} className="col" style={{ gap:1 }}>
              <span className="label" style={{ fontSize:8.5 }}>{k}</span>
              <span style={{ fontFamily:'JetBrains Mono, monospace', fontSize:11, color:'var(--ink)' }}>{v}</span>
            </div>
          ))}
        </div>
        <LedgerBars f={f} compact />
        <div style={{ marginTop:8 }}><TraitSlip f={f} compact /></div>
      </>}
      {(s.seen || s.tk) && <div style={{ marginTop:10 }}><TriggerSlip f={f} known={s.tk} /></div>}
      {!s.seen && !s.tk && <div className="crosshatch" style={{ border:'1px dashed var(--ink-faded)', padding:'10px', textAlign:'center', marginTop:4 }}>
        <span className="elite" style={{ fontSize:10.5, color:'var(--ink-faded)' }}>Sighted in rumor — class only. Raise one or find who remembers to open the record.</span></div>}
    </div>
  );
}

function WheelBestiary({ w = 1040, rank = 2, sv = svW }) {
  const forms = FORMS.filter(f => f.rank === rank);
  const dflt = (forms.find(x => sv(x.id).seen || sv(x.id).tk) || forms[0]).id;
  const [sel, setSel] = React.useState(dflt);
  React.useEffect(() => { setSel(dflt); }, [rank]);
  const f = forms.find(x => x.id === sel) || forms[0];
  const known = forms.filter(x => sv(x.id).seen || sv(x.id).tk).length;
  const rankWord = rank===0 ? 'Nascent' : rank===1 ? 'Lesser' : 'Adult';
  return (
    <div style={{ width:w, background:'var(--paper)', border:'1.5px solid var(--ink)', padding:'12px 22px 14px' }}>
      <div className="row" style={{ justifyContent:'space-between', alignItems:'flex-end', borderBottom:'1.5px solid var(--ink)', paddingBottom:7, marginBottom:5 }}>
        <div className="col" style={{ gap:3 }}>
          <span className="label" style={{ letterSpacing:'0.2em' }}>SKYHEARTH · CODEX — THE LEAK WHEEL</span>
          <span className="fell" style={{ fontSize:26, lineHeight:1 }}>{rankWord} Forms, by Vein</span>
        </div>
        <div className="row" style={{ gap:16, alignItems:'flex-end' }}>
          <span className="elite" style={{ fontSize:11, color:'var(--sepia)', maxWidth:230, textAlign:'right', lineHeight:1.4 }}>
            Halo &amp; tally count the forms you&rsquo;ve mapped to each vein.</span>
          <div className="col" style={{ alignItems:'flex-end' }}>
            <span style={{ fontFamily:'JetBrains Mono, monospace', fontSize:22, color:'var(--ink)' }}>{known}<span style={{ color:'var(--ink-faded)', fontSize:15 }}>/{forms.length}</span></span>
            <span className="label" style={{ fontSize:8.5 }}>KNOWN</span>
          </div>
        </div>
      </div>
      <div className="row" style={{ gap:18, alignItems:'flex-start' }}>
        <ElementWheel size={548} onPick={setSel} selId={sel} sv={sv} forms={forms} />
        <WheelPanel f={f} sv={sv} />
      </div>
    </div>
  );
}
window.CodexWheel = { WheelBestiary, ElementWheel };
