/* ============================================================
   BEDROCK — shared primitives & facilitation components
   Exported to window for cross-script use.
   ============================================================ */
const { useState, useEffect, useRef, useCallback } = React;

/* ---- tiny layout helpers ---- */
function Eyebrow({ children }) {
  return <div className="eyebrow">{children}</div>;
}
function Pull({ children, sig }) {
  return (
    <div className={"pull" + (sig ? " sig" : "")}>
      <div className="q">{children}</div>
    </div>
  );
}
function Stanza({ lines }) {
  return (
    <div className="stanza">
      {lines.map((l, i) => (
        <p key={i} className={"line" + (l.turn ? " turn" : "")}>{l.t}</p>
      ))}
    </div>
  );
}
function Cards({ items, cols }) {
  return (
    <div className={"cards " + (cols === 3 ? "three" : "two")}>
      {items.map((c, i) => (
        <div className="card" key={i}>
          {c.k && <div className="k">{c.k}</div>}
          <div className="t">{c.t}</div>
          {c.d && <div className="d">{c.d}</div>}
        </div>
      ))}
    </div>
  );
}

/* ---- Reveal: content discovered, not read ---- */
function Reveal({ label, mono, children, openLabel }) {
  const [open, setOpen] = useState(false);
  if (open) {
    return <div className="reveal-body">{children}</div>;
  }
  return (
    <button className="reveal-stub" onClick={() => setOpen(true)}>
      {mono && <span className="mono">{mono}</span>}
      <span className="stub-label">{label}</span>
      <span className="stub-mark">+</span>
    </button>
  );
}

/* ---- Facilitator prompt cards (prediction / interaction / what-if) ---- */
function Prompts({ items }) {
  const [flipped, setFlipped] = useState(null);
  return (
    <div className="prompts">
      {items.map((p, i) => (
        <button
          key={i}
          className="prompt"
          onClick={() => setFlipped(flipped === i ? null : i)}
        >
          <span className="kind">{p.kind}</span>
          <span className="ask">{flipped === i && p.alt ? p.alt : p.q}</span>
          <span className="hint">{flipped === i ? "tap to flip back" : (p.alt ? "tap to reframe" : "for the room")}</span>
        </button>
      ))}
    </div>
  );
}

/* ---- RankList: weigh options live (drag + up/down) ---- */
function RankList({ id, items, note, accentTop }) {
  const storeKey = "bedrock.rank." + id;
  const [order, setOrder] = useState(() => {
    try {
      const saved = JSON.parse(localStorage.getItem(storeKey));
      if (Array.isArray(saved) && saved.length === items.length) return saved;
    } catch (e) {}
    return items.map((_, i) => i);
  });
  const dragIdx = useRef(null);
  const [overIdx, setOverIdx] = useState(null);

  useEffect(() => {
    try { localStorage.setItem(storeKey, JSON.stringify(order)); } catch (e) {}
  }, [order]);

  const move = (from, to) => {
    if (to < 0 || to >= order.length) return;
    setOrder((o) => {
      const n = o.slice();
      const [m] = n.splice(from, 1);
      n.splice(to, 0, m);
      return n;
    });
  };

  return (
    <div className="rank">
      {order.map((itemIdx, pos) => {
        const it = items[itemIdx];
        return (
          <div
            key={itemIdx}
            className={"rank-row" + (overIdx === pos ? " over" : "")}
            draggable
            onDragStart={() => (dragIdx.current = pos)}
            onDragOver={(e) => { e.preventDefault(); setOverIdx(pos); }}
            onDragLeave={() => setOverIdx(null)}
            onDrop={(e) => { e.preventDefault(); move(dragIdx.current, pos); setOverIdx(null); dragIdx.current = null; }}
          >
            <div className="rank-rank">{String(pos + 1).padStart(2, "0")}</div>
            <div className="rank-main">
              <div className="t">{it.t}</div>
              {it.d && <div className="d">{it.d}</div>}
            </div>
            <div className="rank-grip">
              <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
                <button className="micro-btn" aria-label="move up" onClick={() => move(pos, pos - 1)}>▲</button>
                <button className="micro-btn" aria-label="move down" onClick={() => move(pos, pos + 1)}>▼</button>
              </div>
            </div>
          </div>
        );
      })}
      {note && <div className="rank-note">{note}</div>}
    </div>
  );
}

/* ---- Score grid: rate candidates against criteria ---- */
function ScoreRow({ label, max = 5, id }) {
  const storeKey = "bedrock.score." + id;
  const [val, setVal] = useState(() => {
    const s = parseInt(localStorage.getItem(storeKey), 10);
    return isNaN(s) ? 0 : s;
  });
  useEffect(() => { try { localStorage.setItem(storeKey, String(val)); } catch (e) {} }, [val]);
  return (
    <div className="score-row">
      <span className="lbl">{label}</span>
      <div className="score-dots">
        {Array.from({ length: max }).map((_, i) => (
          <button
            key={i}
            className={"score-dot" + (i < val ? " on" : "")}
            aria-label={`score ${i + 1}`}
            onClick={() => setVal(i + 1 === val ? 0 : i + 1)}
          />
        ))}
      </div>
    </div>
  );
}

/* ---- Capture: the room's answer, persisted ---- */
function Capture({ id, label, placeholder }) {
  const storeKey = "bedrock.capture." + id;
  const [val, setVal] = useState(() => localStorage.getItem(storeKey) || "");
  const [saved, setSaved] = useState(false);
  const t = useRef(null);
  const onChange = (e) => {
    setVal(e.target.value);
    try { localStorage.setItem(storeKey, e.target.value); } catch (err) {}
    setSaved(true);
    clearTimeout(t.current);
    t.current = setTimeout(() => setSaved(false), 1400);
  };
  return (
    <div className="capture">
      <div className="capture-label"><span className="dot" />{label}</div>
      <textarea value={val} onChange={onChange} placeholder={placeholder} rows={2} />
      <div className={"saved" + (saved ? " show" : "")}>captured to the field notebook</div>
    </div>
  );
}

/* ---- SensoryLoop: the live "feel through the machine" demo ---- */
function SensoryLoop() {
  const STAGES = ["contact", "sense", "transmit", "infer", "perceive", "act"];
  const [active, setActive] = useState(-1);
  const [latency, setLatency] = useState(20); // ms-feel, scaled
  const [felt, setFelt] = useState(false);
  const [pressure, setPressure] = useState(0.6);
  const running = useRef(false);

  const fire = useCallback(() => {
    if (running.current) return;
    running.current = true;
    setFelt(false);
    const step = Math.max(60, latency * 4); // visual pacing scaled from latency
    STAGES.forEach((_, i) => {
      setTimeout(() => setActive(i), i * step);
    });
    setTimeout(() => {
      setFelt(true);
      setActive(-1);
      running.current = false;
    }, STAGES.length * step + 120);
  }, [latency]);

  const below = latency <= 20;

  return (
    <div className="loop">
      <div className="loop-track">
        {STAGES.map((s, i) => (
          <React.Fragment key={s}>
            <div className={"loop-node" + (active === i ? " on" : "") + (s === "contact" || s === "act" ? " end" : "")}>
              <span className="loop-dot" />
              <span className="loop-name">{s}</span>
            </div>
            {i < STAGES.length - 1 && <div className={"loop-link" + (active > i ? " lit" : "")} />}
          </React.Fragment>
        ))}
      </div>

      <div className="loop-controls">
        <button className="loop-fire" onClick={fire}>
          <span className="loop-press"
            onMouseDown={() => setPressure(1)} onMouseUp={() => setPressure(0.6)}>↯</span>
          send one contact
        </button>
        <div className="loop-lat">
          <label className="mono">round-trip latency<b>{latency} ms</b></label>
          <input type="range" min="6" max="120" value={latency} onChange={(e) => setLatency(+e.target.value)} />
          <div className={"loop-thresh" + (below ? " ok" : "")}>
            {below
              ? "below the 20 ms sensorimotor threshold — felt as embodied"
              : "above threshold — felt as delayed information"}
          </div>
        </div>
      </div>

      <div className={"loop-feel" + (felt ? " felt" : "")}>
        <span className="loop-feel-mark" />
        <span className="loop-feel-text">
          {felt ? "“I felt that.”" : "the operator waits…"}
        </span>
      </div>
    </div>
  );
}

/* ---- The Stone: the climax ---- */
function Stone({ lit, onLight }) {
  return (
    <div className={"stone-scene" + (lit ? " lit" : "")}>
      <div className="stone-city" aria-hidden="true">
        {Array.from({ length: 46 }).map((_, i) => {
          const h = 12 + ((i * 37) % 70);
          return <span key={i} className="stone-buildEl" style={{ height: h + "%", animationDelay: (i * 28) + "ms" }} />;
        })}
      </div>
      <button className="stone-core" onClick={onLight} aria-label="illuminate the stone">
        <span className="stone-glow" />
        <span className="stone-rock" />
      </button>
      <div className="stone-caption">
        {lit ? (
          <p className="serif">The city does not create the stone.<br/>The stone creates the city.</p>
        ) : (
          <p className="mono">point the light · one stone</p>
        )}
      </div>
    </div>
  );
}

Object.assign(window, {
  Eyebrow, Pull, Stanza, Cards, Reveal, Prompts, RankList, ScoreRow, Capture, SensoryLoop, Stone,
});
