/* Skyhearth Codex — collection views (load after cards2.jsx)
 * Grid overview of all 34 + a full per-form detail page. */
/* global CX_ELEM, FORMS, VERDICT_META, AVAIL_META, restLabel, timeAwakePct, triggerLore,
   ElComposite, ElChip, LedgerBars, StatLines, TriggerSlip, pad2, VectorDial */

// A believable mid-game save: two independent flags per form.
// `seen` = ascended/sighted one · `tk` = achieved once OR rescued who remembers.
const SAVE = {
  // R0 / R1
  mote:{seen:1,tk:1},
  lesser_fire:{seen:1,tk:1}, lesser_water:{seen:1,tk:1}, lesser_earth:{seen:1,tk:1}, lesser_nature:{seen:1,tk:1},
  lesser_ice:{seen:1}, lesser_lit:{seen:0,tk:0}, lesser_wind:{seen:0,tk:0}, lesser_holy:{tk:1},
  // R2
  pure_fire:{seen:1,tk:1}, pure_water:{seen:1,tk:0}, pure_earth:{seen:1,tk:0}, pure_nature:{seen:1,tk:1},
  d_earth_nature:{seen:1,tk:1}, junk:{seen:1,tk:1}, d_water_earth:{seen:1,tk:0},
  pure_ice:{seen:1,tk:0}, d_nature_ice:{seen:1,tk:0},
  // crossed path — lore-keeper rescued before ever raising one:
  pure_holy:{seen:0,tk:1}, d_holy_fire:{seen:0,tk:1},
};
const sv = (id) => SAVE[id] || { seen:0, tk:0 };
function tierFor(id){ const s=sv(id); return s.tk?2:s.seen?1:0; }

// ---- compact grid tile ------------------------------------------------------
function GridTile({ f, num, svFn = sv }) {
  const s = svFn(f.id); const t = s.tk?2:s.seen?1:0;
  const av = AVAIL_META[f.avail];
  const tierDot = ['var(--ink-ghost)','var(--el-air)','var(--stamp-amber)'][t];
  return (
    <div className="panel" style={{ background:'rgba(244,236,214,0.5)', padding:'9px 9px 8px', position:'relative', borderColor: t===0?'var(--ink-faded)':'var(--ink)',
      borderStyle: t===0?'dashed':'solid' }}>
      <div className="row" style={{ justifyContent:'space-between', alignItems:'baseline', marginBottom:3 }}>
        <span className="label" style={{ fontSize:8.5, letterSpacing:'0.14em' }}>№{pad2(num)}</span>
        <span style={{ width:7, height:7, borderRadius:'50%', background:tierDot, boxShadow: t===2?'0 0 0 2px rgba(184,99,42,0.25)':'none' }} />
      </div>
      <div style={{ height:54, display:'flex', alignItems:'center', justifyContent:'center', position:'relative' }}>
        {t>=1
          ? <FormPortrait f={f} size={52} />
          : <><FormPortrait f={f} size={46} ghost />
              <span style={{ position:'absolute', fontFamily:'IM Fell English SC, serif', fontSize:30, color:'var(--ink-faded)' }}>?</span></>}
      </div>
      <div className="fell" style={{ fontSize:12.5, textAlign:'center', lineHeight:1.05, marginTop:4, minHeight:26,
        color: t>=1?'var(--ink)':'var(--ink-faded)' }}>
        {t>=1 ? f.slot : '— — —'}</div>
      <div className="row" style={{ justifyContent:'center', gap:5, marginTop:3 }}>
        <span style={{ fontFamily:'Special Elite, monospace', fontSize:8, letterSpacing:'0.08em', color:'var(--sepia)' }}>
          {s.seen ? f.role.toUpperCase() : (s.tk?'LORE ONLY':'RUMORED')}</span>
      </div>
      {s.tk && !s.seen && <span className="stamp stamp-amber" style={{ position:'absolute', bottom:-8, left:'50%', transform:'translateX(-50%) rotate(-3deg)', fontSize:7, background:'var(--paper)', padding:'2px 5px 1px' }}>TOLD, NOT SEEN</span>}
    </div>
  );
}

function FilterChip({ children, active, onClick }) {
  return <button onClick={onClick} style={{ fontFamily:'Special Elite, monospace', fontSize:10, letterSpacing:'0.06em', padding:'4px 9px',
    border:`1px solid ${active?'var(--ink)':'var(--ink-ghost)'}`, color:active?'var(--ink)':'var(--ink-faded)',
    background:active?'rgba(26,46,74,0.07)':'transparent', cursor:'pointer' }}>{children}</button>;
}

function GridOverview({ w = 1320, svFn = sv, forms = FORMS.filter(f => f.rank === 2), title = 'Bestiary of Ascension' }) {
  const known = forms.filter(f => svFn(f.id).seen || svFn(f.id).tk).length;
  const triggers = forms.filter(f => svFn(f.id).tk).length;
  const [prof, setProf] = React.useState('all');
  const [restF, setRestF] = React.useState('all');
  const [sortK, setSortK] = React.useState('gate');
  React.useEffect(() => { setProf('all'); setRestF('all'); }, [forms.length, title]);
  const numById = {}; forms.forEach((f,i) => numById[f.id] = i+1);
  const profs = ['pure','dual','multi','junk'].filter(p => forms.some(f => f.profile===p));
  const showRest = forms.some(f => f.rest.slots!==1 || f.rest.tod[0]!=='Night');
  const restCat = (f) => f.rest.slots===0?'tireless':f.rest.slots===2?'double':f.rest.tod[0]==='Night'?'night':'shifted';
  let visible = forms.filter(f => (prof==='all'||f.profile===prof) && (restF==='all'||restCat(f)===restF));
  visible = visible.slice().sort((a,b) => sortK==='gate' ? (window.GATE_VALUE[a.gate]-window.GATE_VALUE[b.gate]) : (b.d.net-a.d.net));
  return (
    <div style={{ width:w, background:'var(--paper)', border:'1.5px solid var(--ink)', padding:'20px 24px 26px', position:'relative' }}>
      {/* header */}
      <div className="row" style={{ justifyContent:'space-between', alignItems:'flex-end', borderBottom:'1.5px solid var(--ink)', paddingBottom:12 }}>
        <div className="col" style={{ gap:3 }}>
          <span className="label" style={{ letterSpacing:'0.2em' }}>SKYHEARTH · HEARTH CODEX</span>
          <span className="fell" style={{ fontSize:30, lineHeight:1 }}>{title}</span>
        </div>
        <div className="row" style={{ gap:22, alignItems:'flex-end' }}>
          <div className="col" style={{ alignItems:'flex-end' }}>
            <span style={{ fontFamily:'JetBrains Mono, monospace', fontSize:26, color:'var(--ink)' }}>{known}<span style={{ color:'var(--ink-faded)', fontSize:18 }}>/{forms.length}</span></span>
            <span className="label" style={{ fontSize:9 }}>FORMS KNOWN</span>
          </div>
          <div className="col" style={{ alignItems:'flex-end' }}>
            <span style={{ fontFamily:'JetBrains Mono, monospace', fontSize:26, color:'var(--stamp-amber)' }}>{triggers}<span style={{ color:'var(--ink-faded)', fontSize:18 }}>/{forms.length}</span></span>
            <span className="label" style={{ fontSize:9 }}>TRIGGERS LEARNED</span>
          </div>
        </div>
      </div>

      {/* filter / sort chrome */}
      <div className="row" style={{ justifyContent:'space-between', margin:'14px 0 16px', flexWrap:'wrap', gap:10 }}>
        <div className="row" style={{ gap:6, flexWrap:'wrap' }}>
          <span className="label" style={{ marginRight:2 }}>CLASS</span>
          <FilterChip active={prof==='all'} onClick={()=>setProf('all')}>All</FilterChip>
          {profs.map(p => <FilterChip key={p} active={prof===p} onClick={()=>setProf(p)}>{window.plabel(p)}</FilterChip>)}
          {showRest && <React.Fragment>
            <span className="divider-v" style={{ height:18 }} />
            <span className="label" style={{ marginRight:2 }}>SLEEP</span>
            {[['all','All'],['tireless','Tireless'],['night','Night'],['shifted','Shifted'],['double','Double']].map(([k,l]) =>
              <FilterChip key={k} active={restF===k} onClick={()=>setRestF(k)}>{l}</FilterChip>)}
          </React.Fragment>}
        </div>
        <div className="row" style={{ gap:6, alignItems:'center' }}>
          <span className="label">SORT</span>
          <FilterChip active={sortK==='gate'} onClick={()=>setSortK('gate')}>Gate</FilterChip>
          <FilterChip active={sortK==='net'} onClick={()=>setSortK('net')}>Net</FilterChip>
        </div>
      </div>

      {/* the grid */}
      <div style={{ display:'grid', gridTemplateColumns:'repeat(7, 1fr)', gap:14 }}>
        {visible.map(f => <GridTile key={f.id} f={f} num={numById[f.id]} svFn={svFn} />)}
      </div>
      {visible.length===0 && <div className="elite" style={{ textAlign:'center', color:'var(--ink-faded)', fontSize:12, padding:'24px 0' }}>No forms match this filter.</div>}

      {/* legend */}
      <div className="row" style={{ gap:18, marginTop:18, paddingTop:12, borderTop:'1px dashed var(--ink-faded)' }}>
        {[['var(--ink-ghost)','Rumored — silhouette only'],['var(--el-air)','Discovered — full stats'],['var(--stamp-amber)','Trigger known — build target']].map(([c,l])=>(
          <span key={l} className="row" style={{ gap:6 }}>
            <span style={{ width:8, height:8, borderRadius:'50%', background:c }} />
            <span className="elite" style={{ fontSize:10.5, color:'var(--sepia)' }}>{l}</span></span>
        ))}
      </div>
    </div>
  );
}

// ---- full detail page (§7.2) ------------------------------------------------
function RingHint({ f }) {
  // pures: the leak you must DODGE; duals: the curated blend. (teaching from §3)
  const RING = ['fire','water','earth','nature','ice','lightning','wind','holy'];
  const size=128, cx=size/2, cy=size/2, R=size/2-16;
  return (
    <svg viewBox={`0 0 ${size} ${size}`} style={{ width:size, height:size }}>
      <circle cx={cx} cy={cy} r={R} fill="none" stroke="var(--ink-ghost)" strokeWidth="1" strokeDasharray="3 4" />
      {RING.map((e,i)=>{
        const ang=-Math.PI/2 + i*(Math.PI*2/8);
        const x=cx+Math.cos(ang)*R, y=cy+Math.sin(ang)*R;
        const on=f.elements.includes(e);
        return <g key={e}>
          <circle cx={x} cy={y} r={on?7:3.5} fill={on?CX_ELEM[e].color:'var(--ink-ghost)'} stroke={on?'var(--ink)':'none'} strokeWidth="1" />
          {on && <text x={x} y={y+4} textAnchor="middle" fontSize="10" fontFamily="IM Fell English SC, serif" fill="#fff">{CX_ELEM[e].glyph}</text>}
        </g>;
      })}
      <text x={cx} y={cy+4} textAnchor="middle" fontSize="9" fontFamily="Special Elite, monospace" fill="var(--ink-faded)" letterSpacing="0.05em">LEAK RING</text>
    </svg>
  );
}

function DetailPage({ f, num, w = 720, svFn = sv }) {
  const s = svFn(f.id); const t = s.tk?2:s.seen?1:0;
  const av = AVAIL_META[f.avail]; const vm = VERDICT_META[f.d.verdict];
  return (
    <div style={{ width:w, background:'var(--paper)', border:'1.5px solid var(--ink)' }}>
      {/* banner */}
      <div className="row" style={{ justifyContent:'space-between', alignItems:'flex-start', padding:'18px 24px 14px', borderBottom:'1.5px solid var(--ink)' }}>
        <div className="col" style={{ gap:4 }}>
          <span className="label" style={{ letterSpacing:'0.2em' }}>{(f.rank===0?'NASCENT':f.rank===1?'LESSER FORM':'ADULT FORM')} · №{pad2(num)} · {window.plabel(f.profile).toUpperCase()} {f.variant!=='std'&&f.variant!=='only'?'· '+f.variant.toUpperCase():''}</span>
          <span className="fell" style={{ fontSize:34, lineHeight:1, color: t>=1?'var(--ink)':'var(--ink-faded)' }}>{t>=1 ? f.slot : '— unnamed specimen —'}</span>
          <span className="elite" style={{ fontSize:11, color:'var(--sepia)', marginTop:2 }}>{s.seen ? `${f.role} · niche ${f.d.niche.join(' / ')||'none'}` : s.tk ? 'trigger known by report · the specimen itself is still unsighted' : 'sighted in rumor only · class is all that is certain'}</span>
        </div>
        <div className="col" style={{ alignItems:'flex-end', gap:6 }}>
          <span className="stamp" style={{ borderColor:av.color, color:av.color, fontSize:9 }}>{av.label}</span>
          <span className="stamp stamp-amber" style={{ fontSize:9 }}>{revLabel(s.seen, s.tk)}</span>
        </div>
      </div>

      <div className="row" style={{ alignItems:'stretch', gap:0 }}>
        {/* left — specimen + ring */}
        <div className="col" style={{ width:240, padding:'18px 20px', borderRight:'1px solid var(--ink-faded)', alignItems:'center', gap:10 }}>
          <div style={{ position:'relative', width:160, height:160, border:`1.5px ${t>=1?'solid':'dashed'} var(--ink-faded)`, background:'rgba(255,255,255,0.18)',
            display:'flex', alignItems:'center', justifyContent:'center' }}>
            {t>=1
              ? <><FormPortrait f={f} size={150} />
                  <span style={{ position:'absolute', top:8, left:'50%', width:9, height:9, borderRadius:'50%', background:'var(--stamp-red)' }} /></>
              : <><FormPortrait f={f} size={92} ghost />
                  <span style={{ position:'absolute', fontFamily:'IM Fell English SC, serif', fontSize:62, color:'var(--ink-faded)' }}>?</span></>}
          </div>
          <div className="col" style={{ gap:5, width:'100%', marginTop:4 }}>
            {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)', textAlign:'center' }}>all veins · 4/4/4/4</span>)
              : <span className="elite" style={{ fontSize:11, color:'var(--ink-faded)', textAlign:'center' }}>veins unknown</span>}
          </div>
          <hr className="divider-h-dashed" style={{ width:'100%', margin:'6px 0' }} />
          {t>=1 && <RingHint f={f} />}
          <span className="elite" style={{ fontSize:9.5, color:'var(--ink-faded)', textAlign:'center', lineHeight:1.45 }}>
            {t<1 ? 'class noted; the rest is hearsay until you raise one or hear it told'
              : f.profile==='pure' ? 'a pure must DODGE the leak — train clean' : f.profile==='dual' ? 'a curated blend — two veins held in balance' : f.profile==='multi' ? 'three veins — late & complex' : 'every vein in equal, useless measure'}</span>
        </div>

        {/* right — stats + ledger + trigger */}
        <div className="col" style={{ flex:1, padding:'18px 24px', gap:12 }}>
          <div className="row" style={{ justifyContent:'space-between', flexWrap:'wrap', gap:'8px 24px' }}>
            {[['Sleep', s.seen?`${restLabel(f.rest)}`:'?'],['Lifespan', s.seen?`${f.life} days`:'?'],
              ['Food', s.seen?f.food:'?'],['Bond', s.seen?f.bond:'?'],['Class', s.seen?f.profile:'?']].map(([k,v])=>(
              <div key={k} className="col" style={{ gap:2, minWidth:120 }}>
                <span className="label" style={{ fontSize:9 }}>{k}</span>
                <span style={{ fontFamily: s.seen?'JetBrains Mono, monospace':'IM Fell English SC, serif', fontSize: s.seen?13:18, color: s.seen?'var(--ink)':'var(--ink-faded)' }}>{v}</span>
              </div>
            ))}
          </div>

          <hr className="divider-h" />
          {s.seen
            ? <><LedgerBars f={f} />
                <div className="elite" style={{ fontSize:11, color:'var(--sepia)', lineHeight:1.5, marginTop:-2 }}>
                  Net {f.d.net} against a gate of {f.d.gate} — <span style={{ color:vm.color }}>{vm.label.toLowerCase()}</span>. {vm.note}.</div></>
            : <div className="crosshatch" style={{ border:'1px dashed var(--ink-faded)', padding:'14px 16px', textAlign:'center' }}>
                <span className="elite" style={{ fontSize:11, color:'var(--ink-faded)' }}>Stats sealed — raise this form once, or sight one in the wild, to fill the record.</span></div>}

          {s.seen && <TraitSlip f={f} />}
          <hr className="divider-h" />
          <TriggerSlip f={f} known={t>=2} />
        </div>
      </div>
    </div>
  );
}

window.CodexCollection = { GridOverview, DetailPage, GridTile, SAVE, sv, tierFor };
