/*  === monstrance-stage.jsx ===
    Monstrance stage — Stage, RasterMonstrance, RevealCard, PartThumb, and the per-part StopPage component.
    Loaded as <script type="text/babel" src="js/monstrance-stage.jsx"></script>.
    Cross-file references work because babel-standalone evaluates each script in
    the document's global scope after compile; top-level const/let/function decls
    are visible to subsequent scripts loaded in this order.
*/

const { useState, useEffect, useRef, useCallback } = React;

function Stage({ activePart, visited, onHotspot, onClose }) {
  const activeData = PARTS.find((p) => p.id === activePart);
  return (
    <section className="stage" aria-label="Monstrance illustration">
      <div className="monstrance-wrap">
        <RasterMonstrance activePart={activePart} />
        {PARTS.map((p) => (
          <button
            key={p.id}
            className={`hotspot ${activePart === p.id ? "is-active" : ""} ${visited.has(p.id) && activePart !== p.id ? "is-visited" : ""}`}
            style={{ left: p.position.x + "%", top: p.position.y + "%" }}
            onClick={() => onHotspot(p.id)}
            aria-label={`Learn about ${p.name}`}
          >
            {p.id}
          </button>
        ))}
        {activeData && <RevealCard part={activeData} onClose={onClose} />}
      </div>
    </section>
  );
}

function RasterMonstrance({ activePart }) {
  const [missingAssets, setMissingAssets] = useState([]);
  const markMissing = (asset) => {
    setMissingAssets((prev) =>
      prev.includes(asset.key) ? prev : [...prev, asset.key],
    );
  };
  return (
    <div className="monstrance-assembly" aria-hidden="true">
      {PART_ASSETS.map((asset) => (
        <img
          key={asset.id}
          className={`part-image ${asset.key} ${
            activePart === asset.id ? "is-active" : ""
          }`}
          src={asset.src}
          alt=""
          onError={() => markMissing(asset)}
        />
      ))}
      {missingAssets.length > 0 && (
        <div className="asset-warning">
          Missing monstrance art: {missingAssets.join(", ")}
        </div>
      )}
    </div>
  );
}

function RevealCard({ part, onClose }) {
  const cardStyle = (() => {
    const s = {};
    const x = part.position.x;
    const y = part.position.y;
    if (part.cardSide === "right") {
      s.left = `min(${x + 12}%, calc(100% - 296px))`;
    } else {
      s.right = `min(${100 - x + 12}%, calc(100% - 296px))`;
    }
    s.top = `clamp(8px, ${y - 8}%, calc(100% - 220px))`;
    return s;
  })();
  return (
    <div
      className="reveal-card"
      style={cardStyle}
      role="dialog"
      aria-label={part.name}
    >
      <button className="close" onClick={onClose} aria-label="Close">
        ×
      </button>
      <span className="num">
        Part {part.id} · {part.hint}
      </span>
      <h3>{part.name}</h3>
      <p>
        {part.short} {part.long}
      </p>
      <div className="verse">{part.verse}</div>
      <a className="btn btn-secondary" href={stopPathForPart(part)}>
        Open this stop
      </a>
    </div>
  );
}

function PartThumb({ id }) {
  const asset = PART_ASSET_BY_ID[id];
  if (!asset) return null;
  return (
    <img
      className={`part-thumb-img ${asset.key}`}
      src={asset.src}
      alt=""
    />
  );
}

function StopPage({
  part,
  speak,
  stopSpeech,
  onShowAudioSetup,
  palette,
  onCyclePalette,
}) {
  const activity = STOP_ACTIVITIES[part.slug];
  const [progress, setProgress] = useState(0);
  const done = progress >= activity.target;
  const partAudio = audioForPart(part);
  const nextPart = PARTS[(part.id % PARTS.length)];
  const prevPart = PARTS[(part.id + PARTS.length - 2) % PARTS.length];

  useEffect(() => {
    setProgress(0);
    speak(narrationForPart(part, { includeVerse: true }), {
      force: true,
      audioSrc: partAudio,
    });
  }, [part.id]);

  return (
    <div className="sky-scene">
      <CloudComposition showSparkles={true} />
      <div className="stop-page">
        <TopBar
          readAloud={true}
          onToggleReadAloud={() =>
            speak(narrationForPart(part, { includeVerse: true }), {
              force: true,
              audioSrc: partAudio,
            })
          }
          onShowAudioSetup={onShowAudioSetup}
          palette={palette}
          onCyclePalette={onCyclePalette}
        />
        <div className="stop-shell">
          <div className="stop-art">
            <div className="monstrance-wrap">
              <RasterMonstrance activePart={part.id} />
            </div>
          </div>
          <article className="stop-card">
            <div className="stop-kicker">Stop {part.id} of 5</div>
            <h1 className="stop-title">{part.name}</h1>
            <p className="stop-copy">
              {part.short} {part.long}
            </p>
            <div className="part-verse" style={{ marginTop: 14 }}>
              {part.verse}
            </div>
            <div className="mini-game">
              <h3>{activity.title}</h3>
              <p className="stop-copy">{activity.prompt}</p>
              <div className="mini-progress" aria-label={`${progress} of ${activity.target} complete`}>
                {Array.from({ length: activity.target }).map((_, i) => (
                  <span key={i} className={i < progress ? "is-on" : ""} />
                ))}
              </div>
              <button
                className="btn btn-primary"
                onClick={() =>
                  setProgress((value) =>
                    Math.min(value + 1, activity.target),
                  )
                }
              >
                {done ? "Completed ✦" : activity.action}
              </button>
              <button
                className="btn btn-secondary"
                onClick={() => setProgress(0)}
                style={{ marginLeft: 10 }}
              >
                Reset
              </button>
            </div>
            <div className="stop-nav">
              <a className="btn btn-secondary" href="#">
                Home
              </a>
              <a className="btn btn-secondary" href={stopPathForPart(prevPart)}>
                ← {prevPart.name}
              </a>
              <a className="btn btn-primary" href={stopPathForPart(nextPart)}>
                {nextPart.name} →
              </a>
              <button
                className="btn btn-secondary"
                onClick={() =>
                  speak(narrationForPart(part, { includeVerse: true }), {
                    force: true,
                    audioSrc: partAudio,
                  })
                }
              >
                <SpeakerIcon on={true} /> Listen
              </button>
              <button className="btn btn-secondary" onClick={stopSpeech}>
                Stop
              </button>
            </div>
          </article>
        </div>
      </div>
    </div>
  );
}


