/* ===================================================================
   panels.jsx — side panels: dice · party/turn · chat · poll · 5e sheet
   =================================================================== */
const { useState, useRef, useEffect } = React;

/* ---------- dice engine ---------- */
function rollFormula(formula) {
  const terms = String(formula).replace(/\s/g,'').match(/[+-]?(\d*d\d+|\d+)/gi) || [];
  let total = 0; const dice = [];
  terms.forEach(t => {
    const sign = t[0] === '-' ? -1 : 1; const body = t.replace(/^[+-]/,'');
    if (/d/i.test(body)) {
      const [n,m] = body.toLowerCase().split('d').map(Number); const cnt = n || 1;
      for (let i=0;i<cnt;i++){ const v = 1+Math.floor(Math.random()*m); dice.push({ d:m, v, sign }); total += sign*v; }
    } else total += sign * Number(body);
  });
  return { dice, total, formula: String(formula) };
}
window.rollFormula = rollFormula;

/* ---------- Dice panel ---------- */
function DicePanel({ tweaks, addRoll, activeRef }) {
  const mode = tweaks.diceMode || 'builder';
  const animate = tweaks.diceAnimate !== false;
  const [pool, setPool] = useState({});
  const [mod, setMod] = useState(0);
  const [last, setLast] = useState(null);
  const [rolling, setRolling] = useState(false);

  const add = (d) => setPool(p => ({ ...p, [d]: (p[d]||0)+1 }));
  const clear = () => { setPool({}); setMod(0); };
  const poolFormula = () => {
    const parts = DICE_TYPES.filter(t=>pool[t.d]).map(t=>`${pool[t.d]}d${t.d}`);
    let f = parts.join('+'); if (mod) f += (mod>0?'+':'')+mod; return f || '1d20';
  };
  const doRoll = (formula, label) => {
    const res = rollFormula(formula);
    if (animate) { setRolling(true); setTimeout(()=>setRolling(false), 520); }
    setLast({ ...res, label });
    addRoll({ ...res, label, who: activeRef });
  };

  const quick = () => (
    <div className="dice-grid">
      {DICE_TYPES.map(t => (
        <button key={t.d} className="die-btn" style={{ '--dc': t.color }}
          onClick={() => mode==='quick' ? doRoll(`1d${t.d}`, `d${t.d}`) : add(t.d)}>
          <span className="die-face">{`d${t.d}`}</span>
          {mode!=='quick' && pool[t.d] ? <i className="die-count">{pool[t.d]}</i> : null}
        </button>
      ))}
    </div>
  );

  return (
    <div className="panel-pad dice-panel">
      {mode==='sets' && (
        <div className="saved-rolls">
          <div className="eyebrow">Saved rolls</div>
          {SAVED_ROLLS.map(s => {
            const who = byId(s.who);
            return (
              <button key={s.id} className="saved-roll" onClick={()=>doRoll(s.formula, s.label)}>
                <span className="sr-dot" style={{background:who.color}} />
                <span className="sr-label">{s.label}</span>
                <span className="sr-formula">{s.formula}</span>
              </button>
            );
          })}
          <div className="hairline" />
        </div>
      )}

      {quick()}

      {mode!=='quick' && (
        <div className="dice-builder">
          <div className="mod-row">
            <span className="eyebrow">Modifier</span>
            <div className="stepper">
              <button aria-label="Decrease modifier" onClick={()=>setMod(m=>m-1)}>－</button>
              <b aria-live="polite">{mod>=0?`+${mod}`:mod}</b>
              <button aria-label="Increase modifier" onClick={()=>setMod(m=>m+1)}>＋</button>
            </div>
          </div>
          <div className="builder-actions">
            <button className="btn" style={{flex:1}} onClick={()=>doRoll(poolFormula(), 'Roll')}>Roll {poolFormula()}</button>
            <button className="btn ghost" onClick={clear}>Clear</button>
          </div>
        </div>
      )}

      {last && (
        <div className={`roll-result ${rolling?'rolling':''}`}>
          <div className="rr-total display">{last.total}</div>
          <div className="rr-meta">
            <span className="rr-label">{last.label} · {last.formula}</span>
            <div className="rr-dice">
              {last.dice.map((d,i)=>(
                <span key={i} className={`rr-die ${d.v===d.d?'crit':''} ${d.d===20&&d.v===1?'fumble':''}`}>{d.v}</span>
              ))}
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

/* ---------- Party / turn order ---------- */
function PartyPanel({ isDM, getEnt, setHp, order, turnIdx, round, onEndTurn, onPrev, activeRef, visibleMonsters, onRollInit, onRollOwn, justRolled, rolledCount, monsters, conditions }) {
  const MONS = monsters || (typeof MONSTERS !== 'undefined' ? MONSTERS : []);
  return (
    <div className="party-panel">
      <div className="party-head">
        <div>
          <span className="eyebrow">Round</span>
          <span className="round-num display">{round}</span>
        </div>
        <div className="turn-controls">
          <button className="mini-btn" onClick={onPrev} title="Previous">‹</button>
          <button className="btn" onClick={onEndTurn}>End turn</button>
        </div>
      </div>
      {isDM
        ? <button className="init-roll-btn" onClick={onRollInit}>🎲 Roll Initiative for enemies &amp; allies</button>
        : <button className="init-roll-btn" onClick={()=>onRollOwn(activeRef)}>🎲 Roll my Initiative (d20)</button>}
      <div className="initiative-list">
        {order.map((ref, i) => {
          const e = getEnt(ref); const active = i === turnIdx;
          const isMon = !!MONS.find(m=>m.id===ref);
          const isAlly = isMon && e.allegiance === 'ally';
          const canEdit = isDM || ref === activeRef;
          const hpPct = e.hpMax ? Math.max(0,(e.hp/e.hpMax)*100) : 100;
          return (
            <div key={ref} className={`init-row ${active?'active':''} ${isMon?'mon':''}`}>
              <button className={`init-badge ${justRolled===ref?'pop':''} ${(isDM||ref===activeRef)?'rollable':''}`}
                style={{borderColor:e.color}} title={(isDM||ref===activeRef)?'Roll d20 initiative':`Initiative ${e.init ?? '–'}`}
                onClick={()=> (isDM||ref===activeRef) && onRollOwn(ref)}>{e.init ?? '–'}</button>
              <span className="init-sprite" style={{'--ring':e.color}}>{e.portrait ? <img className="sprite-img" src={e.portrait} alt="" /> : e.sprite}</span>
              <div className="init-body">
                <div className="init-name">{e.char ? e.char.split(' ')[0] : e.name}
                  {isMon && isDM && <span className={`mon-tag ${isAlly?'ally':''}`}>{isAlly ? 'ally' : (e.hidden && !visibleMonsters.has(ref) ? 'hidden' : 'foe')}</span>}
                </div>
                {conditions && conditions[ref] && conditions[ref].length > 0 && (
                  <div className="init-conds">
                    {conditions[ref].map(c => (
                      <span key={c.id} className={`cond-chip k-${c.kind}`} style={{'--c':c.color}} title={c.name}>
                        <span aria-hidden="true">{c.glyph}</span>{c.name}
                      </span>
                    ))}
                  </div>
                )}
                {e.hpMax && (
                  <div className="init-hp">
                    <div className="hp-bar"><i style={{width:`${hpPct}%`, background: hpPct>50?'#3f7d4a':hpPct>25?'#c1933f':'#a23644'}} /></div>
                    {canEdit ? (
                      <div className="hp-edit">
                        <button aria-label={`Reduce ${e.name} hit points`} onClick={()=>setHp(ref,-1)}>－</button>
                        <span>{e.hp}/{e.hpMax}</span>
                        <button aria-label={`Raise ${e.name} hit points`} onClick={()=>setHp(ref,+1)}>＋</button>
                      </div>
                    ) : <span className="hp-text">{isMon && !isDM ? '???' : `${e.hp}/${e.hpMax}`}</span>}
                  </div>
                )}
              </div>
              {e.ac && <span className="ac-shield" title="Armor Class">{e.ac}</span>}
            </div>
          );
        })}
      </div>
    </div>
  );
}

/* ---------- Chat / log ---------- */
function ChatPanel({ log, isDM, activeRef, onSend }) {
  const [draft, setDraft] = useState('');
  const endRef = useRef(null);
  useEffect(()=>{ endRef.current?.scrollIntoView?.({block:'end'}); }, [log.length]);
  const send = () => { if(!draft.trim()) return; onSend(draft.trim()); setDraft(''); };
  return (
    <div className="chat-panel">
      <div className="chat-scroll">
        {log.map(m => {
          const who = byId(m.who);
          if (m.kind === 'roll') return (
            <div key={m.id} className="chat-roll">
              <span className="cr-sprite" style={{'--ring':who.color}}>{who.sprite}</span>
              <div><b>{who.name}</b> rolls <i>{m.text}</i> <span className="cr-formula">{m.formula}</span>
                <span className="cr-total">{m.total}</span></div>
            </div>
          );
          if (m.kind === 'whisper' && !isDM && m.who!==activeRef) return null;
          return (
            <div key={m.id} className={`chat-msg ${m.kind}`}>
              {m.kind==='narrate' ? <span className="narrate-mark">❧</span> :
               <span className="cm-who" style={{color:who.color}}>{who.name}{m.kind==='whisper'?' (whisper)':''}</span>}
              <span className="cm-text">{m.text}</span>
            </div>
          );
        })}
        <div ref={endRef} />
      </div>
      <div className="chat-input">
        <input value={draft} onChange={e=>setDraft(e.target.value)} onKeyDown={e=>e.key==='Enter'&&send()}
          placeholder={isDM ? 'Narrate or speak…' : 'Say something…'} />
        <button className="btn" onClick={send}>Send</button>
      </div>
    </div>
  );
}

/* ---------- Settings poll ---------- */
function PollPanel({ polls, setPolls, queue, setQueue, activeRef, isDM, onAnnounce }) {
  const [draft, setDraft] = useState('');
  const vote = (pid, oid) => setPolls(ps => ps.map(p => {
    if (p.id !== pid || p.status !== 'open') return p;
    return { ...p, options: p.options.map(o => ({ ...o, votes: o.id===oid
      ? [...new Set([...o.votes, activeRef])] : o.votes.filter(v=>v!==activeRef) })) };
  }));
  const close = (pid) => setPolls(ps => ps.map(p => p.id===pid ? { ...p, status:'closed',
    result: [...p.options].sort((a,b)=>b.votes.length-a.votes.length)[0].label } : p));
  const upvote = (sid) => setQueue(q => q.map(s => s.id===sid ? { ...s, up:s.up+1 } : s));
  const promote = (sid) => {
    const s = queue.find(x=>x.id===sid); if(!s) return;
    setQueue(q=>q.filter(x=>x.id!==sid));
    setPolls(ps => [{ id:'np'+Date.now(), status:'open', by:s.by, title:s.text,
      desc:'Player suggestion put to a vote.', options:[
        {id:'a',label:'Adopt it',votes:[]},{id:'b',label:'Reject it',votes:[]}] }, ...ps]);
    onAnnounce?.({ kind:'vote', text:s.text, by:s.by });
  };
  const submit = () => { if(!draft.trim()) return;
    const text = draft.trim();
    setQueue(q=>[{id:'sg'+Date.now(), by:activeRef, text, up:0}, ...q]); setDraft('');
    onAnnounce?.({ kind:'suggestion', text, by:activeRef }); };

  return (
    <div className="poll-panel panel-pad">
      {polls.map(p => {
        const total = p.options.reduce((s,o)=>s+o.votes.length,0) || 1;
        const lead = Math.max(...p.options.map(o=>o.votes.length));
        const by = byId(p.by);
        return (
          <div key={p.id} className={`poll-card parchment stitched ${p.status}`}>
            <div className="poll-top">
              <span className="seal">{by.initial||'?'}</span>
              <div><div className="poll-title display">{p.title}</div>
                <div className="poll-by">suggested by {by.name}</div></div>
              {p.status==='open' && <span className="poll-live">● live</span>}
            </div>
            <p className="poll-desc">{p.desc}</p>
            <div className="poll-options">
              {p.options.map(o => {
                const pct = Math.round(o.votes.length/total*100);
                const mine = o.votes.includes(activeRef);
                const winning = p.status==='closed' && o.votes.length===lead;
                return (
                  <button key={o.id} disabled={p.status!=='open'}
                    className={`poll-opt ${mine?'mine':''} ${winning?'win':''}`} onClick={()=>vote(p.id,o.id)}>
                    <span className="po-fill" style={{width:`${pct}%`}} />
                    <span className="po-label">{o.label}</span>
                    <span className="po-tally">{o.votes.map(v=><i key={v} className="po-voter" style={{background:byId(v).color}} title={byId(v).name} />)}
                      <b>{pct}%</b></span>
                  </button>
                );
              })}
            </div>
            {p.status==='open' && isDM && <button className="btn ghost poll-close" onClick={()=>close(p.id)}>Tally &amp; close vote</button>}
            {p.status==='closed' && <div className="poll-result">Result: <b>{p.result}</b> — now a house rule.</div>}
          </div>
        );
      })}

      <div className="hairline" />
      <div className="eyebrow">Suggestion box</div>
      <div className="suggest-input">
        <input value={draft} onChange={e=>setDraft(e.target.value)} onKeyDown={e=>e.key==='Enter'&&submit()} placeholder="Propose a house rule…" />
        <button className="btn" onClick={submit}>Suggest</button>
      </div>
      <div className="suggest-list">
        {queue.map(s => (
          <div key={s.id} className="suggest-row">
            <button className="up-btn" onClick={()=>upvote(s.id)}>▲ {s.up}</button>
            <span className="sg-text">{s.text}</span>
            <span className="sg-by" style={{color:byId(s.by).color}}>{byId(s.by).name}</span>
            {isDM && <button className="mini-btn" onClick={()=>promote(s.id)} title="Put to vote">⚖</button>}
          </div>
        ))}
      </div>
    </div>
  );
}

/* ---------- 5e companion sheet ---------- */
const ABILS = ['STR','DEX','CON','INT','WIS','CHA'];
const modOf = (s) => Math.floor((s-10)/2);
const fmtMod = (m) => (m>=0?`+${m}`:`${m}`);

function SheetPanel({ getEnt, setHp, addRoll, charRef, setCharRef, isDM, syncToast, rollInitiative, sheetRefs, editChar, canEditChar, canUpload = true, onLockedUpload }) {
  const refs = (sheetRefs && sheetRefs.length) ? sheetRefs : PARTY.filter(p=>!p.isDM).map(p=>p.id);
  const e = getEnt(charRef);
  const sheet = e.sheet || {};
  const dexMod = modOf(sheet.DEX ?? 10);
  const mayEdit = canEditChar ? canEditChar(charRef) : true;
  const [editing, setEditing] = useState(false);
  const portraitInput = useRef(null);

  // Uploads go through the server (sniff + strip metadata + quota); it returns
  // a served URL string we store in place of the old inline data-URL.
  const swapPortrait = async (ev) => {
    const f = ev.target.files && ev.target.files[0]; ev.target.value = '';
    if (!f || !f.type.startsWith('image/')) return;
    try {
      const url = await window.Ent.uploadImage(f, 'portrait');
      editChar(charRef, { portrait: url });
    } catch (err) {
      if (syncToast) syncToast(err && err.message ? err.message : 'Portrait upload failed');
    }
  };
  const doImport = () => {
    // Pulls ability scores & AC from the 5e Companion sheet but KEEPS the player's name/portrait.
    const src = byId(charRef);
    editChar(charRef, { imported: true });
    syncToast(`Stats imported from 5e Companion — “${e.char}” keeps its name`);
  };
  return (
    <div className="sheet-panel panel-pad">
      <div className="sheet-tabs">
        {refs.map(ref => { const p = getEnt(ref); return (
          <button key={ref} className={`sheet-tab ${charRef===ref?'on':''}`} style={{'--ring':p.color}}
            aria-label={`View ${p.char}'s sheet`} aria-pressed={charRef===ref}
            onClick={()=>setCharRef(ref)}>{p.portrait ? <img className="sprite-img" src={p.portrait} alt="" /> : <span aria-hidden="true">{p.sprite}</span>}</button>
        ); })}
      </div>
      <div className="sheet-card parchment stitched">
        <div className="sheet-hd">
          <button className="sheet-portrait edit-portrait" style={{'--ring':e.color}} disabled={!mayEdit}
            onClick={()=>{ if(!mayEdit) return; canUpload ? portraitInput.current?.click() : onLockedUpload?.(); }}
            title={!mayEdit ? '' : canUpload ? 'Change portrait' : 'Image uploads are a supporter feature'}>
            {e.portrait ? <img className="sprite-img" src={e.portrait} alt="" /> : e.sprite}
            {mayEdit && <span className="portrait-pen">{canUpload?'✎':'🔒'}</span>}
          </button>
          <input ref={portraitInput} type="file" accept="image/*" style={{display:'none'}} onChange={swapPortrait} />
          <div className="sheet-id">
            {editing && mayEdit
              ? <input className="name-input" autoFocus value={e.char}
                  onChange={ev=>editChar(charRef, { char: ev.target.value, name: ev.target.value.split(' ')[0] })}
                  onKeyDown={ev=>ev.key==='Enter'&&setEditing(false)} onBlur={()=>setEditing(false)} />
              : <div className="sheet-name display" onClick={()=>mayEdit&&setEditing(true)} title={mayEdit?'Click to rename':''}>
                  {e.char}{mayEdit && <span className="rename-pen">✎</span>}
                </div>}
            <div className="sheet-sub">{e.race ? e.race+' · ' : ''}Level {e.lvl} {e.cls}</div>
          </div>
          {mayEdit && <button className="import-chip" onClick={doImport} title="Pull stats from the 5e Companion app — your name is kept">⤓ Import</button>}
        </div>

        {editing && mayEdit && (
          <div className="edit-row">
            <label>Race
              <select value={e.race||'Human'} onChange={ev=>editChar(charRef, { race: ev.target.value, sheet: { ...sheet, speed: (RACES_5E.find(r=>r.name===ev.target.value)||{}).speed || sheet.speed } })}>
                {RACES_5E.map(r=><option key={r.name} value={r.name}>{r.name}</option>)}
              </select>
            </label>
            <label>Class
              <select value={e.cls||'Fighter'} onChange={ev=>{ const k=CLASSES_5E.find(c=>c.name===ev.target.value); editChar(charRef, { cls: ev.target.value, color: e.portrait? e.color : (k&&k.color), sprite: e.portrait? e.sprite : (k&&k.sprite) }); }}>
                {CLASSES_5E.map(c=><option key={c.name} value={c.name}>{c.name}</option>)}
              </select>
            </label>
            <button className="btn ghost done-edit" onClick={()=>setEditing(false)}>Done</button>
          </div>
        )}

        <div className="vitals">
          <div className="vital"><span className="eyebrow">AC</span><b className="display">{e.ac}</b></div>
          <div className="vital hp-vital">
            <span className="eyebrow">Hit Points</span>
            <div className="hp-edit big">
              <button aria-label="Reduce hit points" onClick={()=>setHp(charRef,-1)}>－</button>
              <b>{e.hp}<i>/{e.hpMax}</i></b>
              <button aria-label="Raise hit points" onClick={()=>setHp(charRef,+1)}>＋</button>
            </div>
            <button className="sync-link" onClick={()=>syncToast('HP synced to token & party ✓')}>⟳ sync to table</button>
          </div>
          <div className="vital"><span className="eyebrow">Speed</span><b className="display">{sheet.speed}<i>ft</i></b></div>
        </div>

        <div className="abil-grid">
          {ABILS.map(a => {
            const score = sheet[a] ?? 10; const m = modOf(score);
            return (
              <button key={a} className="abil" onClick={()=>addRoll({ ...rollFormula(`1d20${fmtMod(m)}`), label:`${a} check`, who:charRef })}>
                <span className="abil-name">{a}</span>
                <span className="abil-mod display">{fmtMod(m)}</span>
                <span className="abil-score">{score}</span>
                <span className="abil-roll">roll ⚄</span>
              </button>
            );
          })}
        </div>

        <div className="sheet-actions">
          <button className="btn wine" onClick={()=>{ const tot = rollInitiative(charRef); syncToast(`${e.char.split(' ')[0]} rolled ${tot} for initiative — placed in turn order`); }}>🎲 Roll Initiative <i className="init-mod">1d20{fmtMod(dexMod)}</i></button>
          <button className="btn ghost" onClick={()=>syncToast('Opened spellbook (demo)')}>Spells &amp; Actions</button>
        </div>
      </div>
      <div className="companion-note">Linked to <b>5e Companion</b> — stats, HP &amp; rolls stay in sync with the table.</div>
    </div>
  );
}

Object.assign(window, { DicePanel, PartyPanel, ChatPanel, PollPanel, SheetPanel, modOf, fmtMod });
