/* ===================================================================
   embed3d.jsx — React → 3D-iframe bridge for the Chat / Rules / Powers drawers

   The 3D world (vrworld/world/embedpanels.js) renders the Chat, Rules and DM
   Powers drawers, but React owns the real data. This headless component mounts
   only while the 3D stage is showing and:

     • posts the resolved chat log, the SRD rules and the DM's element grid into
       the iframe (with boot-time retries to cover the world's async load), and
     • listens for the drawer's actions — a chat line, an element cast, a
       condition clear — and applies them through the real React handlers.

   No-build/global-scope rules: loads before app.jsx, so it uses React.useX
   (not destructured) and reads the leaked globals SRD_SECTIONS / ELEMENTS /
   ELEMENT_TIERS / elementEffect / byId at call time.
   =================================================================== */
function Embed3DBridge({ active, log, sendMsg, isDM, activeRef, order, getEnt, conditions, castElement, clearCondition }) {
  const post = (msg) => { if (window.__embersPostWorld) window.__embersPostWorld(msg); };

  // ---- build the payloads from live React state ----------------------------
  const chatEntries = React.useMemo(() => {
    const idOf = (typeof byId !== 'undefined') ? byId : ((x) => ({ name: '?', color: '#888', sprite: '•' }));
    return (log || [])
      .filter(m => !(m.kind === 'whisper' && !isDM && m.who !== activeRef))
      .map(m => { const w = idOf(m.who) || {}; return {
        id: m.id, kind: m.kind, text: m.text, formula: m.formula, total: m.total,
        who: { name: w.name, color: w.color, sprite: w.sprite } }; });
  }, [log, isDM, activeRef]);

  const powersData = React.useMemo(() => {
    const els = (typeof ELEMENTS !== 'undefined') ? ELEMENTS : [];
    const effOf = (typeof elementEffect !== 'undefined') ? elementEffect : (() => '');
    const tierList = (typeof ELEMENT_TIERS !== 'undefined') ? ELEMENT_TIERS : [];
    const elems = els.map(el => ({
      id: el.id, name: el.name, glyph: el.glyph, color: el.color, kind: el.kind, tier: el.tier,
      effect: effOf(el) }));
    const tiers = tierList.map(t => ({ key: t.key, label: t.label }));
    const targets = (order || []).map(ref => { const e = getEnt(ref) || {}; return {
      ref, name: e.char ? e.char.split(' ')[0] : e.name, sprite: e.portrait || e.sprite,
      color: e.color, hp: e.hp, hpMax: e.hpMax }; });
    const conds = {};
    (order || []).forEach(ref => { const c = conditions && conditions[ref];
      if (c && c.length) conds[ref] = c.map(x => ({ id: x.id, name: x.name, glyph: x.glyph, color: x.color, kind: x.kind })); });
    return { tiers, elements: elems, targets, conditions: conds, role: isDM ? 'dm' : 'player' };
  }, [order, conditions, isDM, getEnt]);

  const postAll = React.useCallback(() => {
    post({ type: 'embers:rulesData', sections: (typeof SRD_SECTIONS !== 'undefined') ? SRD_SECTIONS : [] });
    post({ type: 'embers:chatLog', entries: chatEntries, role: isDM ? 'dm' : 'player' });
    post({ type: 'embers:powersData', ...powersData });
  }, [chatEntries, powersData, isDM]);

  // push fresh data whenever it changes (only while the 3D stage is live)
  React.useEffect(() => {
    if (!active) return;
    postAll();
    const t1 = setTimeout(postAll, 700);
    const t2 = setTimeout(postAll, 1600);
    return () => { clearTimeout(t1); clearTimeout(t2); };
  }, [active, postAll]);

  // forward the drawer's actions back into the real React handlers
  React.useEffect(() => {
    if (!active) return;
    const onMsg = (e) => {
      const d = e.data; if (!d) return;
      if (d.type === 'embers:reqPanels') postAll();
      else if (d.type === 'embers:chatSend') { if (d.text) sendMsg(d.text); }
      else if (d.type === 'embers:cast') {
        if (!isDM) return;                      // powers are DM-only
        const el = (window.ELEMENTS || []).find(x => x.id === d.elementId);
        if (el && d.targetRef) castElement(el, d.targetRef);
      } else if (d.type === 'embers:clearCond') {
        if (!isDM) return;
        if (d.targetRef && d.condId) clearCondition(d.targetRef, d.condId);
      }
    };
    window.addEventListener('message', onMsg);
    return () => window.removeEventListener('message', onMsg);
  }, [active, postAll, sendMsg, isDM, castElement, clearCondition]);

  return null;
}
window.Embed3DBridge = Embed3DBridge;
