/* runmode.jsx — live at-the-table reference.
   Two layouts: "focus" (single calm column, progressive disclosure — default)
   and "board" (the original masonry). Depends on ui.jsx + data.js (window). */

// ── RichText: renders body text with "- item" lines as styled bullets ─────────
function RichText({ text, className }) {
  if (!text) return null;
  const lines = text.split("\n");
  const hasBullets = lines.some(l => l.startsWith("- "));
  if (!hasBullets) return <span className={className}>{text}</span>;
  return (
    <span className={className}>
      {lines.map((line, i) =>
        line.startsWith("- ") ? (
          <span key={i} className="run-bullet">
            <span className="run-bullet-dot">•</span>
            <span>{line.slice(2)}</span>
          </span>
        ) : line ? (
          <span key={i} className="run-plain-line">{line}</span>
        ) : null
      )}
    </span>
  );
}

const RUN_ORDER = ["secrets", "scenes", "npcs", "locations", "monsters", "items", "characters", "notes"];
const RUN_META = {
  start: { icon: "spark", label: "Strong start" },
  secrets: { icon: "key", label: "Secrets", title: "Secrets", accent: true },
  scenes: { icon: "scroll", label: "Scenes", title: "Scenes" },
  npcs: { icon: "mask", label: "NPCs", title: "NPCs" },
  locations: { icon: "map", label: "Locations", title: "Locations" },
  monsters: { icon: "sword", label: "Monsters", title: "Monsters" },
  items: { icon: "gem", label: "Rewards", title: "Rewards" },
  characters: { icon: "users", label: "Party", title: "The party" },
  notes: { icon: "feather", label: "Notes", title: "Session notes" }
};

// ── shared body renderers (used by both layouts) ─────────────────────────────
function secretsBody(d, toggle) {
  const secrets = d.secrets || [];
  if (!secrets.length) return <p className="run-muted">No secrets prepped.</p>;
  return (
    <ul className="run-secrets">
      {secrets.map((s) =>
        <li key={s.id} className={s.revealed ? "revealed" : ""}>
          <button type="button" className="reveal-box" onClick={() => toggle("secrets", s.id, "revealed")}
            title={s.revealed ? "Revealed — click to hide" : "Click when revealed to players"}>
            <window.Icon name="check" size={13} sw={2.6} />
          </button>
          <span><RichText text={s.body} /></span>
        </li>
      )}
    </ul>
  );
}

function scenesBody(d, toggle) {
  const scenes = d.scenes || [];
  if (!scenes.length) return <p className="run-muted">No scenes outlined.</p>;
  return (
    <ul className="run-scenes">
      {scenes.map((s) =>
        <li key={s.id} className={s.done ? "done" : ""}>
          <button type="button" className="reveal-box" onClick={() => toggle("scenes", s.id, "done")}
            title={s.done ? "Played" : "Mark played"}>
            <window.Icon name="check" size={13} sw={2.6} />
          </button>
          <div>
            <strong>{s.title || "Untitled scene"}</strong>
            {s.body && <em><RichText text={s.body} /></em>}
          </div>
        </li>
      )}
    </ul>
  );
}

function refList(items, render) {
  if (!items || !items.length) return <p className="run-muted">Nothing here yet.</p>;
  return <ul className="run-ref">{items.map(render)}</ul>;
}

function bodyFor(id, d, toggle, session, setSession) {
  switch (id) {
    case "secrets": return secretsBody(d, toggle);
    case "scenes": return scenesBody(d, toggle);
    case "npcs": return refList(d.npcs, (n) =>
      <li key={n.id}><strong>{n.title}</strong>{n.sub && <span className="run-tag">{n.sub}</span>}{n.body && <em><RichText text={n.body} /></em>}</li>);
    case "locations": return refList(d.locations, (l) =>
      <li key={l.id}>
        <strong>{l.title}</strong>
        {Array.isArray(l.aspects) && l.aspects.length > 0 &&
          <span className="run-aspects">{l.aspects.map((a, i) => <i key={i}>{a}</i>)}</span>}
        {l.body && <em><RichText text={l.body} /></em>}
      </li>);
    case "monsters": return refList(d.monsters, (m) =>
      <li key={m.id}><strong>{m.title}{m.sub && <span className="run-x">×{m.sub}</span>}</strong>{m.body && <em><RichText text={m.body} /></em>}</li>);
    case "items": return refList(d.items, (it) =>
      <li key={it.id}><strong>{it.title}</strong>{it.body && <em><RichText text={it.body} /></em>}</li>);
    case "characters": return refList(d.characters, (c) =>
      <li key={c.id}><strong>{c.title}</strong>{c.sub && <span className="run-tag">{c.sub}</span>}{c.body && <em><RichText text={c.body} /></em>}</li>);
    case "notes": return (
      <window.EditableArea value={session.notes} minRows={3}
        placeholder="Jot what happened, dropped clues, dangling threads for next time…"
        onChange={(v) => setSession({ ...session, notes: v })} className="run-notes" />);
    default: return null;
  }
}

function countFor(id, d) {
  if (id === "secrets") { const s = d.secrets || []; return `${s.filter((x) => x.revealed).length}/${s.length}`; }
  if (id === "scenes") { const s = d.scenes || []; return `${s.filter((x) => x.done).length}/${s.length}`; }
  if (id === "notes") return null;
  return (d[id] || []).length;
}

// ── collapsible section (focus layout) ───────────────────────────────────────
function FocusSection({ id, meta, count, open, onToggle, innerRef, children }) {
  return (
    <section ref={innerRef} className={`focus-sec${open ? " open" : ""}${meta.accent ? " accent" : ""}`}>
      <button type="button" className="focus-sec-hd" onClick={() => onToggle(id)} aria-expanded={open}>
        <window.Icon name={meta.icon} size={18} className="focus-sec-icon" />
        <h3>{meta.title}</h3>
        {count != null && <span className="run-count">{count}</span>}
        <window.Icon name="chevron" size={16} className="focus-chev" />
      </button>
      {open && <div className="focus-sec-body">{children}</div>}
    </section>
  );
}

// ── board panel (board layout) ───────────────────────────────────────────────
function RunPanel({ id, meta, count, wide, innerRef, children }) {
  return (
    <section ref={innerRef} className={`run-panel${meta.accent ? " run-accent" : ""}${wide ? " run-wide" : ""}`}>
      <header className="run-panel-hd">
        <window.Icon name={meta.icon} size={17} />
        <h3>{meta.title}</h3>
        {count != null && <span className="run-count">{count}</span>}
      </header>
      <div>{children}</div>
    </section>
  );
}

function RunMode({ session, setSession, ctx, layout = "focus" }) {
  const d = session.data;
  const setStep = (stepId, items) => setSession({ ...session, data: { ...d, [stepId]: items } });
  const toggle = (stepId, id, key) =>
    setStep(stepId, (d[stepId] || []).map((it) => it.id === id ? { ...it, [key]: !it[key] } : it));

  const start = d.start && d.start[0] || {};
  const refs = React.useRef({});
  const [open, setOpen] = React.useState({ secrets: true, scenes: true });
  const [pendingJump, setPendingJump] = React.useState(null);
  const onToggle = (id) => setOpen((o) => ({ ...o, [id]: !o[id] }));

  const scrollToSection = React.useCallback((id) => {
    const el = refs.current[id];
    if (!el) return;
    let sc = el.parentElement;
    while (sc) {
      const oy = getComputedStyle(sc).overflowY;
      if ((oy === "auto" || oy === "scroll") && sc.scrollHeight > sc.clientHeight + 1) break;
      sc = sc.parentElement;
    }
    const wrap = el.closest(".run-wrap");
    const topbar = wrap && wrap.parentElement ? wrap.parentElement.querySelector(".topbar") : null;
    const nav = wrap ? wrap.querySelector(".run-jump") : null;
    const offset = (topbar ? topbar.offsetHeight : 0) + (nav ? nav.offsetHeight : 0) + 12;
    const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    const behavior = reduce ? "auto" : "smooth";
    if (sc) {
      const top = el.getBoundingClientRect().top - sc.getBoundingClientRect().top + sc.scrollTop - offset;
      sc.scrollTo({ top: Math.max(0, top), behavior });
    } else {
      window.scrollTo({ top: Math.max(0, el.getBoundingClientRect().top + window.scrollY - offset), behavior });
    }
  }, []);

  React.useEffect(() => {
    if (!pendingJump) return;
    const tm = setTimeout(() => { scrollToSection(pendingJump); setPendingJump(null); }, 60);
    return () => clearTimeout(tm);
  }, [pendingJump, open, scrollToSection]);

  const jump = (id) => {
    if (layout === "focus" && id !== "start") setOpen((o) => ({ ...o, [id]: true }));
    setPendingJump(id);
  };

  const startBlock =
    <section className="run-start" ref={(el) => refs.current.start = el} style={{ padding: "20px" }}>
      <span className="run-start-eyebrow"><window.Icon name="spark" size={14} /> Strong start</span>
      <p className="run-start-text" style={{ fontSize: "16px" }}>
        {start.body ? <RichText text={start.body} /> : <span className="run-muted">No strong start written — jump back to prep to set the opening scene.</span>}
      </p>
    </section>;

  return (
    <div className="run-wrap">
      <nav className="run-jump">
        <div className="run-jump-inner">
          {["start", ...RUN_ORDER].map((id) =>
            <button key={id} type="button" onClick={() => jump(id)}>
              <window.Icon name={RUN_META[id].icon} size={14} /> {RUN_META[id].label}
            </button>
          )}
        </div>
      </nav>

      {layout === "board" ?
        <div className="run">
          {startBlock}
          <div className="run-grid">
            {RUN_ORDER.map((id) =>
              <RunPanel key={id} id={id} meta={RUN_META[id]} count={countFor(id, d)}
                wide={id === "secrets" || id === "notes"} innerRef={(el) => refs.current[id] = el}>
                {bodyFor(id, d, toggle, session, setSession)}
              </RunPanel>
            )}
          </div>
        </div> :

        <div className="run run-focus">
          {startBlock}
          {RUN_ORDER.map((id) =>
            <FocusSection key={id} id={id} meta={RUN_META[id]} count={countFor(id, d)}
              open={!!open[id]} onToggle={onToggle} innerRef={(el) => refs.current[id] = el}>
              {bodyFor(id, d, toggle, session, setSession)}
            </FocusSection>
          )}
        </div>
      }
    </div>
  );
}

Object.assign(window, { RunMode });
