// quiz-gravity-jump.jsx
// Gravity Jump: feel-the-gravity hop on Moon/Earth/Mars/Jupiter.
// Owns GRAVITY_JUMP_WORLDS (pulled from quiz-games.jsx alongside the level).

const GRAVITY_JUMP_WORLDS = [
  {
    id: "moon",
    label: "Moon",
    short: "Moon",
    x: 50,
    bg: GENERATED_ASSETS.gravityBgMoon,
    bgPosition: "center center",
    jump: 235,
    duration: 1.55,
    pull: 0.17,
    targetX: 68,
    note: "Moon jump! Less gravity makes a slow, high float.",
  },
  {
    id: "earth",
    label: "Earth",
    short: "Earth",
    x: 50,
    bg: GENERATED_ASSETS.gravityBgEarth,
    bgPosition: "left center",
    jump: 105,
    duration: 0.82,
    pull: 1,
    targetX: 38,
    note: "Earth jump! Strong gravity pulls Adam down faster.",
  },
  {
    id: "mars",
    label: "Mars",
    short: "Mars",
    x: 50,
    bg: GENERATED_ASSETS.gravityBgMars,
    bgPosition: "center center",
    jump: 165,
    duration: 1.18,
    pull: 0.38,
    targetX: 62,
    note: "Mars jump! Mars pulls less than Earth, so Adam hops higher.",
  },
  {
    id: "jupiter",
    label: "Jupiter",
    short: "Jupiter",
    x: 50,
    bg: GENERATED_ASSETS.gravityBgBig,
    bgPosition: "center center",
    jump: 58,
    duration: 0.7,
    pull: 2.53,
    targetX: 46,
    note: "Jupiter jump! Its strong gravity makes a tiny hop.",
  },
];

const GRAVITY_JUMP_TARGET_LANDINGS = 3;
const GRAVITY_JUMP_TARGET_THRESHOLD = 9;

const GRAVITY_JUMP_PREDICTIONS = [
  { id: "high", label: "High float", test: (world) => world.jump >= 190 },
  {
    id: "middle",
    label: "Middle hop",
    test: (world) => world.jump >= 120 && world.jump < 190,
  },
  { id: "tiny", label: "Tiny hop", test: (world) => world.jump < 120 },
];

function gravityJumpPredictionFor(world) {
  return (
    GRAVITY_JUMP_PREDICTIONS.find((prediction) => prediction.test(world)) ||
    GRAVITY_JUMP_PREDICTIONS[1]
  );
}

function GravityJumpLevel({ onExit }) {
  const worlds = GRAVITY_JUMP_WORLDS;
  const [worldId, setWorldId] = useState("moon");
  const frameTimersRef = useRef([]);
  const celebrationShownRef = useRef(false);
  const [jumpKey, setJumpKey] = useState(0);
  const [frameIndex, setFrameIndex] = useState(0);
  const [playerX, setPlayerX] = useState(50);
  const [lastMove, setLastMove] = useState(0);
  const [jumpArc, setJumpArc] = useState(0);
  const [jumping, setJumping] = useState(false);
  const [targetLands, setTargetLands] = useState(0);
  const [targetStatus, setTargetStatus] = useState("ready");
  const [message, setMessage] = useState(
    "Start on the Moon. Move to the glowing pad, then jump.",
  );
  const [moonJumpSeen, setMoonJumpSeen] = useState(false);
  const [selectedPrediction, setSelectedPrediction] = useState(null);
  const world = worlds.find((item) => item.id === worldId) || worlds[0];
  const actualPrediction = gravityJumpPredictionFor(world);
  const currentWorldIndex = worlds.findIndex((item) => item.id === world.id);
  const nextCompareWorld =
    worlds[(Math.max(0, currentWorldIndex) + 1) % worlds.length];
  const biggestJumpWorld = worlds.reduce((best, item) =>
    item.jump > best.jump ? item : best,
  );
  const missionComplete = targetLands >= GRAVITY_JUMP_TARGET_LANDINGS;
  const aimOffset = Math.round(world.targetX - playerX);
  const aimReady = Math.abs(aimOffset) <= GRAVITY_JUMP_TARGET_THRESHOLD;
  const aimDirection = aimOffset > 0 ? "right" : "left";
  const aimGuideTitle = jumping
    ? "Watch the landing"
    : aimReady
      ? "Ready to jump"
      : `Move ${aimDirection}`;
  const aimGuideDetail = jumping
    ? "Adam is testing this world's gravity pull."
    : aimReady
      ? "Adam is close enough to the glowing pad."
      : `The glowing pad is to the ${aimDirection}.`;

  const clearFrameTimers = () => {
    frameTimersRef.current.forEach((timer) => window.clearTimeout(timer));
    frameTimersRef.current = [];
  };

  const movePlayer = (direction) => {
    setPlayerX((current) => Math.max(14, Math.min(86, current + direction * 8)));
    setLastMove(direction);
    if (!jumping) setTargetStatus("ready");
  };

  const jump = () => {
    if (jumping) return;
    const landingGap = Math.abs(playerX - world.targetX);
    const landingHit = landingGap <= GRAVITY_JUMP_TARGET_THRESHOLD;
    const nextLandingCount = landingHit
      ? Math.min(GRAVITY_JUMP_TARGET_LANDINGS, targetLands + 1)
      : targetLands;
    const predicted = GRAVITY_JUMP_PREDICTIONS.find(
      (item) => item.id === selectedPrediction,
    );
    const predictionLine = predicted
      ? predicted.id === actualPrediction.id
        ? `Good prediction: ${actualPrediction.label}. `
        : `You picked ${predicted.label}; this was ${actualPrediction.label}. `
      : `Try predicting the next jump. This was ${actualPrediction.label}. `;
    clearFrameTimers();
    setJumpKey((key) => key + 1);
    setFrameIndex(0);
    setJumping(true);
    setTargetStatus("waiting");
    setJumpArc(lastMove * 12);
    if (world.id === "moon" && !moonJumpSeen) {
      setMoonJumpSeen(true);
    }
    setMessage(`${predictionLine}Aim for the glowing landing pad.`);
    setSelectedPrediction(null);
    playKidSound("chime");
    GRAVITY_JUMP_FRAMES.forEach((_, index) => {
      const timer = window.setTimeout(() => {
        setFrameIndex(index);
      }, (world.duration * 1000 * index) / (GRAVITY_JUMP_FRAMES.length - 1));
      frameTimersRef.current.push(timer);
    });
    frameTimersRef.current.push(
      window.setTimeout(() => {
        setJumping(false);
        setJumpArc(0);
        setFrameIndex(0);
        if (landingHit) {
          setTargetStatus("hit");
          setTargetLands((count) =>
            Math.min(GRAVITY_JUMP_TARGET_LANDINGS, count + 1),
          );
          setMessage(
            nextLandingCount >= GRAVITY_JUMP_TARGET_LANDINGS
              ? `${predictionLine}Landing badge earned! You landed on the platform.`
              : `${predictionLine}Platform landing! ${world.note}`,
          );
          playKidSound(
            nextLandingCount >= GRAVITY_JUMP_TARGET_LANDINGS ? "success" : "pop",
          );
        } else {
          setTargetStatus("miss");
          setMessage(
            `${predictionLine}Move closer to the glowing pad, then jump again.`,
          );
          playKidSound("boop");
        }
      }, world.duration * 1000 + 80),
    );
  };

  const chooseWorld = (nextWorld) => {
    clearFrameTimers();
    setWorldId(nextWorld.id);
    setFrameIndex(0);
    setJumping(false);
    setJumpArc(0);
    setPlayerX(50);
    setLastMove(0);
    setTargetStatus("ready");
    setSelectedPrediction(null);
    setMessage(
      nextWorld.id === "earth" && moonJumpSeen
        ? "Earth pulls harder. Move to the pad, then watch the tiny hop."
        : `${nextWorld.label}: move to the glowing pad, then jump.`,
    );
    playKidSound("boop");
  };

  const reset = () => {
    clearFrameTimers();
    setJumpKey(0);
    setFrameIndex(0);
    setPlayerX(50);
    setLastMove(0);
    setJumpArc(0);
    setJumping(false);
    setTargetLands(0);
    setTargetStatus("ready");
    setWorldId("moon");
    setMoonJumpSeen(false);
    setSelectedPrediction(null);
    celebrationShownRef.current = false;
    setMessage("Start on the Moon. Move to the glowing pad, then jump.");
  };

  useEffect(() => {
    if (!missionComplete || celebrationShownRef.current) return;
    celebrationShownRef.current = true;
    window.SpaceExplorerFoundation?.celebrateGameFinish?.({
      gameId: "gravity-jump",
      emoji: "🦘",
      title: "Gravity badge!",
      message: "You tested jumps on space worlds.",
    });
  }, [missionComplete]);

  useWindowKeyHandler((event) => {
    if (event.key === "ArrowLeft") {
      event.preventDefault();
      movePlayer(-1);
    } else if (event.key === "ArrowRight") {
      event.preventDefault();
      movePlayer(1);
    } else if (event.key === "ArrowUp" || event.key === " ") {
      event.preventDefault();
      jump();
    }
  });

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

  return (
    <section
      className="gravity-jump-level full"
      aria-label="Gravity jump game"
      style={{
        "--gravity-x": `${playerX}%`,
        "--gravity-target-x": `${world.targetX}%`,
        "--gravity-jump": `${world.jump}px`,
        "--gravity-arc": `${jumpArc}vw`,
        "--gravity-arc-start": `${jumpArc * 0.15}vw`,
        "--gravity-arc-fall": `${jumpArc * 0.72}vw`,
        "--gravity-facing": lastMove < 0 ? "-1" : "1",
        "--gravity-bg-position": world.bgPosition,
        "--gravity-time": `${world.duration}s`,
      }}
    >
      <SafeAssetImage
        className="gravity-full-bg"
        src={world.bg}
        alt=""
        context={`GravityJump:bg:${world.id}`}
        fallbackText=""
      />
      <button className="planet-sort-back" onClick={onExit}>
        ← Back to planets
      </button>
      <div className="gravity-jump-shell full">
        <div className="gravity-jump-copy">
          <span>Gravity Jump</span>
          <h1>{missionComplete ? "Gravity explorer!" : "Jump on worlds."}</h1>
          <p>Small pull means a high float. Strong pull means a tiny hop.</p>
          <div
            className="gravity-jump-message"
            role="status"
            aria-live="polite"
          >
            <strong>{message}</strong>
          </div>
          <div className="gravity-best-jump">
            Biggest jump: <strong>{biggestJumpWorld.label}</strong>
          </div>
          <div className="gravity-target-card">
            <strong>Goal: land on the glowing pad</strong>
            <span>
              {targetLands}/{GRAVITY_JUMP_TARGET_LANDINGS} platform landings
            </span>
          </div>
          <div
            className={`gravity-aim-guide ${aimReady ? "ready" : ""} ${
              jumping ? "watching" : ""
            }`}
            aria-label="Landing aim helper"
          >
            <span>Aim helper</span>
            <strong>{aimGuideTitle}</strong>
            <small>{aimGuideDetail}</small>
          </div>
          <div className="gravity-prediction-strip" aria-label="Predict the jump height">
            <strong>Predict the jump</strong>
            <div>
              {GRAVITY_JUMP_PREDICTIONS.map((prediction) => (
                <button
                  key={prediction.id}
                  type="button"
                  className={selectedPrediction === prediction.id ? "on" : ""}
                  onClick={() => {
                    setSelectedPrediction(prediction.id);
                    setMessage(`Prediction set: ${prediction.label}. Now tap Jump.`);
                    playKidSound("boop");
                  }}
                  aria-pressed={
                    selectedPrediction === prediction.id ? "true" : "false"
                  }
                >
                  {prediction.label}
                </button>
              ))}
            </div>
          </div>
          <div className="gravity-compare-card">
            <strong>Compare the pull</strong>
            <span>
              Jump here, then try {nextCompareWorld.label}. Watch if the hop gets
              higher or smaller.
            </span>
          </div>
          <div
            className="gravity-pull-meter"
            aria-label={`${world.label} gravity pull is ${world.pull} times Earth`}
          >
            <div>
              <span>Pull</span>
              <strong>{world.pull}x Earth</strong>
            </div>
            <i>
              <b
                style={{
                  width: `${Math.min(100, (world.pull / 2.53) * 100)}%`,
                }}
              />
            </i>
          </div>
          <div className="gravity-jump-progress" aria-label="Platform landings">
            {[0, 1, 2].map((index) => (
              <span key={index} className={index < targetLands ? "on" : ""} />
            ))}
          </div>
          {missionComplete ? (
            <div className="gravity-jump-complete" role="status">
              <strong>Gravity badge earned!</strong>
              <span>You landed on platforms while gravity changed the jump.</span>
              <div>
                <button onClick={reset}>Play again</button>
                <button onClick={onExit}>Done</button>
              </div>
            </div>
          ) : null}
        </div>
        <div
          className={`gravity-jump-stage full ${world.id} ${jumping ? "jumping" : ""}`}
        >
          <div className="gravity-world-label">{world.label} gravity</div>
          <div
            className={`gravity-landing-target ${targetStatus}`}
            role="img"
            aria-label="glowing landing platform target"
          >
            <span>Land here</span>
          </div>
          <SafeAssetImage
            key={`${world.id}-${jumpKey}`}
            className="gravity-astronaut"
            src={GRAVITY_JUMP_FRAMES[frameIndex] || GRAVITY_JUMP_FRAMES[0]}
            role="img"
            aria-label="astronaut jumping"
            context={`GravityJump:astronaut:${frameIndex}`}
            fallbackText=""
          />
          <div className="gravity-float-trail" aria-hidden="true">
            <i />
            <i />
            <i />
          </div>
          <div
            className="gravity-world-picker"
            aria-label="Choose gravity world"
          >
            {worlds.map((item) => (
              <button
                key={item.id}
                className={
                  (item.id === world.id ? "on " : "") +
                  (moonJumpSeen && item.id === "earth" ? "hint" : "")
                }
                onClick={() => chooseWorld(item)}
                aria-pressed={item.id === world.id ? "true" : "false"}
              >
                {item.short}
              </button>
            ))}
          </div>
          <div className="gravity-key-hint" aria-hidden="true">
            Move to the glowing pad · tap Jump · compare the height
          </div>
          <div className="gravity-jump-controls">
            <button
              className="gravity-move-button"
              onClick={() => movePlayer(-1)}
            >
              ←
            </button>
            <button className="gravity-jump-button" onClick={jump}>
              Jump
            </button>
            <button
              className="gravity-move-button"
              onClick={() => movePlayer(1)}
            >
              →
            </button>
            <button className="gravity-reset-button" onClick={reset}>
              Reset
            </button>
          </div>
        </div>
      </div>
    </section>
  );
}
