// physics-play-lab.jsx
// Tiny Physics Play Lab: five preschool space-physics levels with dedicated
// 3D sprite-sheet atlases and Grok narration clips.

const PHYSICS_PLAY_LEVELS = [
  {
    id: "push-force",
    title: "Rocket Push",
    concept: "Push makes motion",
    goal: "Physics word: push. Bigger push, bigger move.",
    childInstruction: "Tap to push the rocket!",
    parentPrompt: "A push is a force. Bigger push, bigger move.",
    successLine: "You pushed it! Hooray!",
    audio: "physics-play-lab/push-force-instruction.mp3",
    icon: "🚀",
    motionClass: "push",
    sheet: "data/sprites/physics-play-lab/push-force-3d-sprite-sheet.png",
    frames: physicsPlayFrames("push-force"),
    actions: [
      {
        id: "tiny",
        icon: "👆",
        label: "Tiny push",
        short: "little move",
        frame: 1,
        coach: "A tiny push makes a tiny move.",
      },
      {
        id: "big",
        icon: "✋",
        label: "Big push",
        short: "farther",
        frame: 3,
        coach: "A big push sends it farther.",
      },
      {
        id: "launch",
        icon: "🚀",
        label: "Launch",
        short: "blast off",
        frame: 5,
        coach: "Push from fire makes the rocket go.",
      },
    ],
  },
  {
    id: "gravity-drop",
    title: "Down We Go",
    concept: "Gravity pulls down",
    goal: "Physics word: gravity. It pulls things down.",
    childInstruction: "Drop the star and watch!",
    parentPrompt: "Gravity pulls the star toward the planet.",
    successLine: "It fell down! Great job!",
    audio: "physics-play-lab/gravity-drop-instruction.mp3",
    icon: "⭐",
    motionClass: "drop",
    sheet: "data/sprites/physics-play-lab/gravity-drop-3d-sprite-sheet.png",
    frames: physicsPlayFrames("gravity-drop"),
    actions: [
      {
        id: "hold",
        icon: "🙌",
        label: "Hold up",
        short: "ready",
        frame: 0,
        coach: "Hold the star up high.",
      },
      {
        id: "drop",
        icon: "⬇",
        label: "Let go",
        short: "falls down",
        frame: 2,
        coach: "Gravity pulls the star down.",
      },
      {
        id: "land",
        icon: "⭐",
        label: "Land",
        short: "soft stop",
        frame: 3,
        coach: "The planet catches it softly.",
      },
    ],
  },
  {
    id: "inertia-glide",
    title: "Keep Going",
    concept: "Moving things keep moving",
    goal: "Physics word: glide. Motion can keep going.",
    childInstruction: "Start it and watch it zoom!",
    parentPrompt: "In space, a ship can keep gliding.",
    successLine: "It keeps going! Yay you!",
    audio: "physics-play-lab/inertia-glide-instruction.mp3",
    icon: "🛸",
    motionClass: "glide",
    sheet: "data/sprites/physics-play-lab/inertia-glide-3d-sprite-sheet.png",
    frames: physicsPlayFrames("inertia-glide"),
    actions: [
      {
        id: "start",
        icon: "▶",
        label: "Start",
        short: "push once",
        frame: 1,
        coach: "One push starts the ship.",
      },
      {
        id: "coast",
        icon: "→",
        label: "Coast",
        short: "keeps going",
        frame: 3,
        coach: "No new push. It keeps gliding.",
      },
      {
        id: "stop",
        icon: "☁",
        label: "Soft stop",
        short: "slow down",
        frame: 4,
        coach: "A soft stop changes its motion.",
      },
    ],
  },
  {
    id: "orbit-curve",
    title: "Round and Round",
    concept: "Sideways motion can curve",
    goal: "Physics word: orbit. Gravity bends the path.",
    childInstruction: "Make it curve around the planet!",
    parentPrompt: "A sideways push helps make an orbit.",
    successLine: "It went around! Super!",
    audio: "physics-play-lab/orbit-curve-instruction.mp3",
    icon: "🪐",
    motionClass: "orbit",
    sheet: "data/sprites/physics-play-lab/orbit-curve-3d-sprite-sheet.png",
    frames: physicsPlayFrames("orbit-curve"),
    actions: [
      {
        id: "nudge",
        icon: "👉",
        label: "Side push",
        short: "start curve",
        frame: 1,
        coach: "A sideways push starts the curve.",
      },
      {
        id: "curve",
        icon: "↪",
        label: "Curve",
        short: "turn",
        frame: 3,
        coach: "Gravity bends the path around.",
      },
      {
        id: "circle",
        icon: "⭕",
        label: "Circle",
        short: "orbit",
        frame: 5,
        coach: "Around and around is an orbit.",
      },
    ],
  },
  {
    id: "bounce-collision",
    title: "Boing Bounce",
    concept: "Bumps can bounce",
    goal: "Physics word: collision. Bumps can bounce.",
    childInstruction: "Tap to make them bounce!",
    parentPrompt: "When things bump, they can bounce away.",
    successLine: "Boing! What a fun bounce!",
    audio: "physics-play-lab/bounce-collision-instruction.mp3",
    icon: "☄",
    motionClass: "bounce",
    sheet: "data/sprites/physics-play-lab/bounce-collision-3d-sprite-sheet.png",
    frames: physicsPlayFrames("bounce-collision"),
    actions: [
      {
        id: "meet",
        icon: "➡",
        label: "Move close",
        short: "near",
        frame: 1,
        coach: "The rocks move toward each other.",
      },
      {
        id: "bump",
        icon: "💥",
        label: "Bump",
        short: "touch",
        frame: 2,
        coach: "They bump and squish a little.",
      },
      {
        id: "boing",
        icon: "↔",
        label: "Bounce",
        short: "away",
        frame: 5,
        coach: "After a bump, they bounce away.",
      },
    ],
  },
];

function physicsPlayFrames(levelId) {
  return Array.from(
    { length: 6 },
    (_, index) =>
      `data/sprites/physics-play-lab/frames/${levelId}-frame-${index}.png`,
  );
}

const PHYSICS_PLAY_ATLAS = {
  columns: 3,
  rows: 2,
  frameCount: 6,
};

function physicsPlayLevelProgress(level, triedActions) {
  const tried = triedActions || {};
  const triedCount = level.actions.filter((action) => tried[action.id]).length;
  const nextAction =
    level.actions.find((action) => !tried[action.id]) ||
    level.actions[level.actions.length - 1];
  return {
    tried,
    triedCount,
    complete: triedCount >= level.actions.length,
    nextAction,
  };
}

function PhysicsPlaySprite({ level, frameIndex }) {
  const safeFrameIndex = Math.max(
    0,
    Math.min(PHYSICS_PLAY_ATLAS.frameCount - 1, frameIndex || 0),
  );
  const frame = level.frames[safeFrameIndex] || level.sheet;
  return (
    <div
      className={`physics-play-sprite ${level.motionClass}`}
      role="img"
      aria-label={`${level.title} animated space toy`}
    >
      <SafeAssetImage
        className="physics-play-atlas-source"
        src={level.sheet}
        alt=""
        context={`PhysicsPlayLab:atlas:${level.id}`}
        fallbackText=""
      />
      <SafeAssetImage
        className="physics-play-sprite-frame"
        src={frame}
        alt=""
        context={`PhysicsPlayLab:frame:${level.id}:${safeFrameIndex}`}
        fallbackText=""
      />
    </div>
  );
}

function PhysicsPlayLab({ onExit }) {
  const [levelIndex, setLevelIndex] = useState(0);
  const [frameIndex, setFrameIndex] = useState(0);
  const [message, setMessage] = useState(
    PHYSICS_PLAY_LEVELS[0].childInstruction,
  );
  const [triedActions, setTriedActions] = useState({});
  const [completed, setCompleted] = useState({});
  const allDoneAnnouncedRef = useRef(false);
  const level = PHYSICS_PLAY_LEVELS[levelIndex] || PHYSICS_PLAY_LEVELS[0];
  const levelProgress = physicsPlayLevelProgress(
    level,
    triedActions[level.id],
  );
  const completedCount = PHYSICS_PLAY_LEVELS.filter(
    (item) => completed[item.id],
  ).length;
  const allDone = completedCount >= PHYSICS_PLAY_LEVELS.length;
  const progressPercent =
    (levelProgress.triedCount / Math.max(1, level.actions.length)) * 100;
  const nextLevelIndex = (levelIndex + 1) % PHYSICS_PLAY_LEVELS.length;
  const nextLevel = PHYSICS_PLAY_LEVELS[nextLevelIndex];

  const readInstruction = () => {
    playKidSound("boop");
    playNarration(level.audio);
  };

  const chooseLevel = (nextIndex) => {
    const safeIndex = Math.max(
      0,
      Math.min(PHYSICS_PLAY_LEVELS.length - 1, nextIndex),
    );
    const nextLevel = PHYSICS_PLAY_LEVELS[safeIndex];
    const nextProgress = physicsPlayLevelProgress(
      nextLevel,
      triedActions[nextLevel.id],
    );
    setLevelIndex(safeIndex);
    setFrameIndex(nextProgress.complete ? PHYSICS_PLAY_ATLAS.frameCount - 1 : 0);
    setMessage(
      nextProgress.complete ? nextLevel.successLine : nextLevel.childInstruction,
    );
    playKidSound("boop");
    playNarration(nextLevel.audio);
  };

  const resetLevel = () => {
    setFrameIndex(0);
    setMessage(level.childInstruction);
    setTriedActions((current) => ({ ...current, [level.id]: {} }));
    setCompleted((current) => ({ ...current, [level.id]: false }));
    allDoneAnnouncedRef.current = false;
    playKidSound("pop");
  };

  const advanceLevel = (action) => {
    if (!action) return;
    const nextLevelTried = {
      ...(triedActions[level.id] || {}),
      [action.id]: true,
    };
    const nextComplete = level.actions.every((item) => nextLevelTried[item.id]);
    setFrameIndex(
      Math.max(
        0,
        Math.min(PHYSICS_PLAY_ATLAS.frameCount - 1, action.frame || 0),
      ),
    );
    setTriedActions((current) => ({
      ...current,
      [level.id]: {
        ...(current[level.id] || {}),
        [action.id]: true,
      },
    }));
    if (nextComplete) {
      setMessage(level.successLine);
      setCompleted((currentCompleted) => ({
        ...currentCompleted,
        [level.id]: true,
      }));
      playKidSound("success");
    } else {
      setMessage(action.coach || level.parentPrompt);
      playKidSound("chime");
    }
  };

  useEffect(() => {
    if (!allDone || allDoneAnnouncedRef.current) return;
    allDoneAnnouncedRef.current = true;
    window.SpaceExplorerFoundation?.celebrateGameFinish?.({
      gameId: "physics-play-lab",
      emoji: "🧪",
      title: "Physics badge!",
      message: "You played push, gravity, glide, orbit, and bounce.",
    });
  }, [allDone]);

  useWindowKeyHandler((event) => {
    if (event.key === "ArrowLeft") {
      event.preventDefault();
      chooseLevel(levelIndex - 1);
    } else if (event.key === "ArrowRight") {
      event.preventDefault();
      chooseLevel(levelIndex + 1);
    } else if (event.key === " " || event.key === "Enter") {
      event.preventDefault();
      advanceLevel(levelProgress.nextAction);
    }
  });

  useEffect(() => () => stopNarration(), []);

  return (
    <section
      className={`physics-play-lab full ${level.motionClass} step-${frameIndex}`}
      aria-label="Tiny Physics Play Lab"
      style={{
        "--physics-play-progress": `${progressPercent}%`,
        "--physics-play-step": frameIndex,
      }}
    >
      <div className="physics-play-preload" aria-hidden="true">
        {PHYSICS_PLAY_LEVELS.flatMap((item) =>
          item.frames.map((frame) => (
            <SafeAssetImage
              key={frame}
              src={frame}
              alt=""
              context={`PhysicsPlayLab:preload:${item.id}`}
              fallbackText=""
            />
          )),
        )}
      </div>
      <Starfield count={180} />
      <button className="planet-sort-back" onClick={onExit}>
        Back to planets
      </button>

      <div className="physics-play-shell">
        <div className="physics-play-copy">
          <span>Tiny Physics Lab</span>
          <h1>{level.title}</h1>
          <p>{level.concept}</p>
          <div className="physics-play-lesson-strip" aria-label="Physics focus">
            <strong>Watch:</strong>
            <span>{level.parentPrompt}</span>
          </div>
          <div className="physics-play-action-track" aria-label="Action tracker">
            {level.actions.map((action, index) => (
              <button
                key={action.id}
                type="button"
                className={levelProgress.tried[action.id] ? "done" : ""}
                onClick={() => advanceLevel(action)}
                aria-label={`${action.label}: ${levelProgress.tried[action.id] ? "tried" : "not tried yet"}`}
              >
                <span>{index + 1}</span>
                <strong>{action.short}</strong>
              </button>
            ))}
          </div>
          <div className="physics-play-message" role="status" aria-live="polite">
            <strong>{message}</strong>
            <span>{level.goal}</span>
          </div>
          <div className="physics-play-toolbar">
            <button onClick={readInstruction}>Hear</button>
            <button onClick={resetLevel}>Reset</button>
          </div>
          <div className="physics-play-next-card">
            <span>{levelProgress.nextAction.icon}</span>
            <div>
              <strong>
                {levelProgress.complete
                  ? `Next: ${nextLevel.title}`
                  : `Next: ${levelProgress.nextAction.label}`}
              </strong>
              <em>
                {levelProgress.complete
                  ? nextLevel.concept
                  : levelProgress.nextAction.short}
              </em>
            </div>
          </div>
          <div className="physics-play-badges" aria-label="Physics badges">
            {PHYSICS_PLAY_LEVELS.map((item, index) => (
              <button
                key={item.id}
                className={`${index === levelIndex ? "on" : ""} ${
                  completed[item.id] ? "done" : ""
                }`}
                onClick={() => chooseLevel(index)}
                aria-pressed={index === levelIndex ? "true" : "false"}
              >
                <span>{item.icon}</span>
                <strong>{index + 1}</strong>
              </button>
            ))}
          </div>
        </div>

        <button
          className="physics-play-stage"
          onClick={() =>
            levelProgress.complete
              ? chooseLevel(nextLevelIndex)
              : advanceLevel(levelProgress.nextAction)
          }
          aria-label={
            levelProgress.complete
              ? `Go to ${nextLevel.title}`
              : `Tap picture to try ${levelProgress.nextAction.label}`
          }
        >
          <div className="physics-play-orbit-ring" aria-hidden="true" />
          <PhysicsPlaySprite level={level} frameIndex={frameIndex} />
          <div className="physics-play-progress">
            <i />
          </div>
          <div className="physics-play-tap-cue">
            {levelProgress.complete ? "Next physics idea" : "Tap and watch"}
          </div>
          <div className="physics-play-frame-note">
            {levelProgress.complete
              ? "Try the next one"
              : `Try ${levelProgress.triedCount + 1} of ${level.actions.length}`}
          </div>
        </button>

        <div className="physics-play-controls" aria-label={`${level.title} actions`}>
          {level.actions.map((action) => (
            <button
              key={action.id}
              className={levelProgress.tried[action.id] ? "tried" : ""}
              onClick={() => advanceLevel(action)}
            >
              <span>{action.icon}</span>
              <strong>{action.label}</strong>
              <em>{levelProgress.tried[action.id] ? "tried" : action.short}</em>
            </button>
          ))}
          <button
            onClick={() =>
              chooseLevel(nextLevelIndex)
            }
          >
            Next physics idea
          </button>
        </div>
      </div>

      {allDone ? (
        <div className="physics-play-complete" role="status">
          <strong>Physics badge earned!</strong>
          <span>Push, gravity, glide, orbit, bounce.</span>
          <button onClick={onExit}>Done</button>
        </div>
      ) : null}
    </section>
  );
}

window.SpaceExplorerPhysicsPlay = {
  levels: PHYSICS_PLAY_LEVELS,
  atlas: PHYSICS_PLAY_ATLAS,
};
