// quiz-asteroid-dodge.jsx
// Asteroid Dodge: keyboard/touch dodging game with requestAnimationFrame loop.
// Owns ASTEROID_DODGE_PATHS plus ROCKS/DIFFICULTY/CORE_TARGET constants.

const ASTEROID_DODGE_PATHS = [18, 66, 42, 78, 29, 58, 12, 84];
const ASTEROID_DODGE_ROCKS = [
  GENERATED_ASSETS.asteroidRock,
  GENERATED_ASSETS.asteroidIce,
  GENERATED_ASSETS.asteroidRock,
];

const ASTEROID_DODGE_DIFFICULTY = [
  { name: "Scout", asteroidEvery: 900, pickupEvery: 1180, speedBonus: 0 },
  { name: "Pilot", asteroidEvery: 760, pickupEvery: 1080, speedBonus: 0.006 },
  { name: "Comet", asteroidEvery: 630, pickupEvery: 980, speedBonus: 0.011 },
  { name: "Meteor", asteroidEvery: 520, pickupEvery: 900, speedBonus: 0.017 },
];

const ASTEROID_DODGE_CORE_TARGET = 12;
const ASTEROID_DODGE_SHIELD_MAX = 3;

function AsteroidDodgeLevel({ onExit }) {
  const stageRef = useRef(null);
  const loopRef = useRef(null);
  const lastFrameRef = useRef(0);
  const spawnClockRef = useRef(0);
  const coreClockRef = useRef(0);
  const keysRef = useRef({ left: false, right: false });
  const dragActiveRef = useRef(false);
  const rocketXRef = useRef(50);
  const rocketTargetXRef = useRef(50);
  const asteroidsRef = useRef([]);
  const coresRef = useRef([]);
  const secretWaveTimerRef = useRef(null);
  const dodgeRef = useRef(0);
  const collectedRef = useRef(0);
  const difficultyIndexRef = useRef(0);
  const hitRef = useRef(0);
  const shieldChargeRef = useRef(ASTEROID_DODGE_SHIELD_MAX);
  const [screen, setScreen] = useState("intro");
  const [dodges, setDodges] = useState(0);
  const [collected, setCollected] = useState(0);
  const [difficultyIndex, setDifficultyIndex] = useState(0);
  const [hits, setHits] = useState(0);
  const [shieldCharge, setShieldCharge] = useState(ASTEROID_DODGE_SHIELD_MAX);
  const [shieldFlash, setShieldFlash] = useState(false);
  const [rocketX, setRocketX] = useState(50);
  const [asteroids, setAsteroids] = useState([]);
  const [cores, setCores] = useState([]);
  const [message, setMessage] = useState(
    "Catch star cores between the falling asteroids.",
  );
  const [won, setWon] = useState(false);
  const [secretTaps, setSecretTaps] = useState(0);
  const [secretFriendRevealed, setSecretFriendRevealed] = useState(false);
  const [secretFriendWaving, setSecretFriendWaving] = useState(false);
  const difficulty = ASTEROID_DODGE_DIFFICULTY[difficultyIndex];
  const motionRule =
    difficultyIndex >= 3
      ? "Very fast rocks need early moves."
      : difficultyIndex >= 2
        ? "Faster rocks leave smaller gaps."
        : difficultyIndex >= 1
          ? "More speed means less thinking time."
          : "Watch the path before you move.";

  const objectOverlapsShip = (object, objectSize, padding = 0.28) => {
    const stage = stageRef.current;
    const ship = stage?.querySelector?.(".asteroid-ship");
    if (!stage || !ship) return false;
    const stageRect = stage.getBoundingClientRect();
    const shipRect = ship.getBoundingClientRect();
    const objectCenterX = stageRect.width * (object.x / 100);
    const objectCenterY = window.innerHeight * (object.y / 100) + objectSize * 0.5;
    const shipCenterX = stageRect.width * (rocketXRef.current / 100);
    const shipCenterY = shipRect.top - stageRect.top + shipRect.height * 0.5;
    const hitWidth = shipRect.width * padding + objectSize * padding;
    const hitHeight = shipRect.height * padding + objectSize * padding;
    return (
      Math.abs(objectCenterX - shipCenterX) < hitWidth &&
      Math.abs(objectCenterY - shipCenterY) < hitHeight
    );
  };

  const stopLoop = () => {
    window.cancelAnimationFrame(loopRef.current);
    loopRef.current = null;
    lastFrameRef.current = 0;
    keysRef.current.left = false;
    keysRef.current.right = false;
    dragActiveRef.current = false;
  };

  useEffect(() => {
    const unregisterRuntimeCleanup =
      window.SpaceExplorerFoundation?.registerGameRuntimeCleanup?.(stopLoop) ||
      (() => {});
    return unregisterRuntimeCleanup;
  }, []);

  const clampShipX = (value) => Math.max(7, Math.min(93, value));

  const snapShipPosition = (value) => {
    const next = clampShipX(value);
    rocketTargetXRef.current = next;
    rocketXRef.current = next;
    setRocketX(next);
  };

  const setShipTarget = (value) => {
    const next = Math.max(7, Math.min(93, value));
    rocketTargetXRef.current = next;
  };

  const setShipFromPointer = (event) => {
    const rect = stageRef.current?.getBoundingClientRect();
    if (!rect) return;
    setShipTarget(((event.clientX - rect.left) / rect.width) * 100);
  };

  const reset = () => {
    stopLoop();
    asteroidsRef.current = [];
    coresRef.current = [];
    dodgeRef.current = 0;
    collectedRef.current = 0;
    difficultyIndexRef.current = 0;
    hitRef.current = 0;
    shieldChargeRef.current = ASTEROID_DODGE_SHIELD_MAX;
    spawnClockRef.current = 780;
    coreClockRef.current = 520;
    setDodges(0);
    setCollected(0);
    setDifficultyIndex(0);
    setHits(0);
    setShieldCharge(ASTEROID_DODGE_SHIELD_MAX);
    setShieldFlash(false);
    setAsteroids([]);
    setCores([]);
    snapShipPosition(50);
    setMessage("Catch star cores between the falling asteroids.");
    setWon(false);
    setScreen("intro");
  };

  const beginLevel = () => {
    stopLoop();
    asteroidsRef.current = [];
    coresRef.current = [];
    dodgeRef.current = 0;
    collectedRef.current = 0;
    difficultyIndexRef.current = 0;
    hitRef.current = 0;
    shieldChargeRef.current = ASTEROID_DODGE_SHIELD_MAX;
    spawnClockRef.current = 780;
    coreClockRef.current = 520;
    setDodges(0);
    setCollected(0);
    setDifficultyIndex(0);
    setHits(0);
    setShieldCharge(ASTEROID_DODGE_SHIELD_MAX);
    setShieldFlash(false);
    setAsteroids([]);
    setCores([]);
    snapShipPosition(50);
    setMessage("Catch the glowing cores. Dodge the rocks.");
    setWon(false);
    setScreen("level");
    playKidSound("boop");
  };

  const waveSecretFriend = () => {
    window.clearTimeout(secretWaveTimerRef.current);
    setSecretFriendWaving(true);
    secretWaveTimerRef.current = window.setTimeout(() => {
      setSecretFriendWaving(false);
    }, 1700);
  };

  const pokeSecretFriend = () => {
    if (secretFriendRevealed) {
      waveSecretFriend();
      playKidSound("chime");
      return;
    }
    setSecretTaps((current) => {
      const next = Math.min(3, current + 1);
      if (next >= 3) {
        setSecretFriendRevealed(true);
        waveSecretFriend();
        playKidSound("chime");
      } else {
        playKidSound("boop");
      }
      return next;
    });
  };

  useWindowKeyHandler((event) => {
    if (event.key === "ArrowLeft") {
      event.preventDefault();
      keysRef.current.left = event.type === "keydown";
    } else if (event.key === "ArrowRight") {
      event.preventDefault();
      keysRef.current.right = event.type === "keydown";
    } else if (event.key === " " || event.key === "Enter") {
      event.preventDefault();
      if (screen === "intro") {
        beginLevel();
      }
    }
  });

  useEffect(() => {
    if (screen !== "level" || won) return undefined;
    const runFrame = (now) => {
      const last = lastFrameRef.current || now;
      const delta = Math.min(34, now - last);
      lastFrameRef.current = now;
      const keyMove =
        (keysRef.current.right ? 1 : 0) - (keysRef.current.left ? 1 : 0);
      if (keyMove) {
        setShipTarget(rocketTargetXRef.current + keyMove * delta * 0.065);
      }
      const easing = 1 - Math.exp(-delta / (dragActiveRef.current ? 58 : 82));
      const nextRocketX =
        rocketXRef.current + (rocketTargetXRef.current - rocketXRef.current) * easing;
      if (Math.abs(nextRocketX - rocketXRef.current) > 0.01) {
        rocketXRef.current = nextRocketX;
        setRocketX(nextRocketX);
      }
      spawnClockRef.current += delta;
      coreClockRef.current += delta;
      const activeDifficulty =
        ASTEROID_DODGE_DIFFICULTY[
          Math.min(
            ASTEROID_DODGE_DIFFICULTY.length - 1,
            Math.floor(collectedRef.current / 3),
          )
        ];
      const activeDifficultyIndex =
        ASTEROID_DODGE_DIFFICULTY.indexOf(activeDifficulty);
      if (activeDifficultyIndex !== difficultyIndexRef.current) {
        difficultyIndexRef.current = activeDifficultyIndex;
        setDifficultyIndex(activeDifficultyIndex);
        setMessage(`${activeDifficulty.name} level. The asteroid shower is faster.`);
      }
      let resetFlightPath = false;
      let nextAsteroids = asteroidsRef.current
        .map((rock) => ({
          ...rock,
          y: rock.y + rock.speed * delta,
          spin: rock.spin + rock.spinSpeed * delta,
        }))
        .filter((rock) => {
          if (objectOverlapsShip(rock, rock.size, 0.26)) {
            if (shieldChargeRef.current >= ASTEROID_DODGE_SHIELD_MAX) {
              shieldChargeRef.current = 0;
              setShieldCharge(0);
              setShieldFlash(true);
              setMessage("Bubble shield popped! Catch cores to grow it again.");
              playKidSound("chime");
              return false;
            }
            hitRef.current += 1;
            const nextHits = hitRef.current;
            setHits(nextHits);
            setMessage(
              nextHits >= 3
                ? "Too many bumps. Resetting the flight path."
                : "Bump! Glide away before the next asteroid.",
            );
            playKidSound("pop");
            if (nextHits >= 3) {
              hitRef.current = 0;
              dodgeRef.current = 0;
              collectedRef.current = Math.max(0, collectedRef.current - 2);
              setHits(0);
              setDodges(0);
              setCollected(collectedRef.current);
              snapShipPosition(50);
              resetFlightPath = true;
            }
            return false;
          }
          if (rock.y > 112) {
            dodgeRef.current += 1;
            const nextDodges = dodgeRef.current;
            setDodges(nextDodges);
            if (nextDodges % 5 === 0) {
              setMessage("Nice flying. Keep reading the falling rocks.");
              playKidSound("chime");
            }
            return false;
          }
          return true;
        });
      let nextCores = coresRef.current
        .map((core) => ({
          ...core,
          y: core.y + core.speed * delta,
          pulse: core.pulse + delta * 0.006,
        }))
        .filter((core) => {
          if (objectOverlapsShip(core, 62, 0.34)) {
            collectedRef.current += 1;
            const nextCollected = collectedRef.current;
            if (shieldChargeRef.current < ASTEROID_DODGE_SHIELD_MAX) {
              const nextShieldCharge = Math.min(
                ASTEROID_DODGE_SHIELD_MAX,
                shieldChargeRef.current + 1,
              );
              shieldChargeRef.current = nextShieldCharge;
              setShieldCharge(nextShieldCharge);
              if (nextShieldCharge >= ASTEROID_DODGE_SHIELD_MAX) {
                setMessage("Bubble shield ready. One asteroid bump is safe.");
              }
            }
            setCollected(nextCollected);
            playKidSound("chime");
            if (nextCollected >= ASTEROID_DODGE_CORE_TARGET) {
              setWon(true);
              setMessage("Cargo full! You caught the cores and cleared the shower.");
            } else if (shieldChargeRef.current >= ASTEROID_DODGE_SHIELD_MAX) {
              setMessage("Bubble shield ready. One asteroid bump is safe.");
            } else if (nextCollected % 3 === 0) {
              setMessage("Level up. Faster rocks, brighter cores.");
            } else {
              setMessage("Core caught! Keep threading the gaps.");
            }
            return false;
          }
          return core.y <= 112;
        });
      if (resetFlightPath) {
        nextAsteroids = [];
        nextCores = [];
        spawnClockRef.current = -420;
        coreClockRef.current = -120;
      }
      if (!won && spawnClockRef.current > activeDifficulty.asteroidEvery) {
        spawnClockRef.current = 0;
        const rockIndex = Math.floor(Math.random() * ASTEROID_DODGE_ROCKS.length);
        nextAsteroids.push({
          id: `${now}-${Math.random().toString(16).slice(2)}`,
          x: 10 + Math.random() * 80,
          y: -14 - Math.random() * 16,
          size: 70 + Math.random() * 54,
          speed:
            0.031 +
            Math.random() * 0.022 +
            activeDifficulty.speedBonus +
            dodgeRef.current * 0.00045,
          spin: Math.random() * 80 - 40,
          spinSpeed: Math.random() * 0.13 - 0.065,
          hitRadius: 7.8 + Math.random() * 3.8,
          rock: ASTEROID_DODGE_ROCKS[rockIndex],
        });
      }
      if (!won && coreClockRef.current > activeDifficulty.pickupEvery) {
        coreClockRef.current = 0;
        nextCores.push({
          id: `core-${now}-${Math.random().toString(16).slice(2)}`,
          x: 12 + Math.random() * 76,
          y: -8 - Math.random() * 18,
          speed: 0.022 + Math.random() * 0.012 + activeDifficulty.speedBonus * 0.45,
          pulse: Math.random() * 6,
        });
      }
      asteroidsRef.current = nextAsteroids;
      coresRef.current = nextCores;
      setAsteroids(nextAsteroids);
      setCores(nextCores);
      loopRef.current = window.requestAnimationFrame(runFrame);
    };
    loopRef.current = window.requestAnimationFrame(runFrame);
    return stopLoop;
  }, [screen, won]);

  useEffect(() => {
    if (screen !== "level" || !won) return undefined;
    stopLoop();
    return undefined;
  }, [screen, won]);

  useEffect(() => {
    const releaseKeys = () => {
      keysRef.current.left = false;
      keysRef.current.right = false;
      dragActiveRef.current = false;
    };
    window.addEventListener("keyup", releaseKeys);
    window.addEventListener("blur", releaseKeys);
    return () => {
      window.removeEventListener("keyup", releaseKeys);
      window.removeEventListener("blur", releaseKeys);
    };
  }, []);

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

  useEffect(
    () => () => {
      window.clearTimeout(secretWaveTimerRef.current);
    },
    [],
  );

  const nudgeShip = (step) => {
    if (screen !== "level" || won) return;
    setShipTarget(rocketTargetXRef.current + step);
    playKidSound("boop");
  };

  const handlePointerDown = (event) => {
    if (screen !== "level" || won) return;
    dragActiveRef.current = true;
    event.currentTarget.setPointerCapture?.(event.pointerId);
    setShipFromPointer(event);
  };

  const handlePointerMove = (event) => {
    if (!dragActiveRef.current || screen !== "level" || won) return;
    setShipFromPointer(event);
  };

  const handlePointerUp = () => {
    dragActiveRef.current = false;
  };

  useEffect(() => {
    if (!won) return undefined;
    window.SpaceExplorerFoundation?.celebrateGameFinish?.({
      gameId: "asteroid-dodge",
      emoji: "☄",
      title: "Clear flight!",
      message: "You caught the cores and dodged the rocks.",
    });
    const winTimer = window.setTimeout(() => {
      setMessage("Clear flight! You dodged the asteroid shower.");
    }, 100);
    return () => window.clearTimeout(winTimer);
  }, [won]);

  useEffect(() => {
    if (screen !== "level") return undefined;
    const messageTimer = window.setTimeout(() => {
      if (!won) setMessage("Asteroids fall from above. Keep the rocket moving.");
    }, 2400);
    return () => window.clearTimeout(messageTimer);
  }, [screen, won]);

  useEffect(() => {
    if (hits === 0 || won) return undefined;
    const hitTimer = window.setTimeout(() => setHits(0), 620);
    return () => window.clearTimeout(hitTimer);
  }, [hits, won]);

  useEffect(() => {
    if (!shieldFlash) return undefined;
    const shieldTimer = window.setTimeout(() => setShieldFlash(false), 520);
    return () => window.clearTimeout(shieldTimer);
  }, [shieldFlash]);

  useEffect(() => {
    if (!won) return undefined;
    asteroidsRef.current = [];
    coresRef.current = [];
    setAsteroids([]);
    setCores([]);
    return undefined;
  }, [won]);

  return (
    <section
      className={`asteroid-dodge-level ${screen === "level" ? "playing" : ""}`}
      aria-label="Asteroid dodge game"
    >
      <Starfield count={240} />
      <button className="planet-sort-back" onClick={onExit}>
        ← Back to planets
      </button>
      {screen === "intro" ? (
        <div className="asteroid-dodge-opening">
          <div className="asteroid-dodge-opening-art">
            <SafeAssetImage
              className="asteroid-opening-bg"
              src={GENERATED_ASSETS.asteroidDodgeBg}
              alt=""
              context="AsteroidDodge:openingBg"
              fallbackText=""
            />
            <SafeAssetImage
              className="asteroid-opening-rock rock-one"
              src={GENERATED_ASSETS.asteroidRock}
              alt=""
              context="AsteroidDodge:openingRock"
              fallbackText=""
            />
            <SafeAssetImage
              className="asteroid-opening-rock rock-two"
              src={GENERATED_ASSETS.asteroidIce}
              alt=""
              context="AsteroidDodge:openingIce"
              fallbackText=""
            />
            <button
              className="asteroid-secret-hotspot"
              type="button"
              tabIndex="-1"
              aria-label="Hidden helper asteroid"
              onClick={pokeSecretFriend}
            />
            {secretFriendRevealed ? (
              <div
                className={`asteroid-secret-friend ${
                  secretFriendWaving ? "wave" : ""
                }`}
                aria-live="polite"
              >
                <SafeAssetImage
                  className="asteroid-secret-alien"
                  src={GENERATED_ASSETS.alien}
                  alt="Tiny alien helper waving"
                  context="AsteroidDodge:secretAlien"
                  fallbackText=""
                />
                <SafeAssetImage
                  className="asteroid-secret-star star-one"
                  src={GENERATED_ASSETS.sparkleStar}
                  alt=""
                  aria-hidden="true"
                  context="AsteroidDodge:secretStarOne"
                  fallbackText=""
                />
                <SafeAssetImage
                  className="asteroid-secret-star star-two"
                  src={GENERATED_ASSETS.sparkleStar}
                  alt=""
                  aria-hidden="true"
                  context="AsteroidDodge:secretStarTwo"
                  fallbackText=""
                />
              </div>
            ) : null}
            <SafeAssetImage
              className="asteroid-opening-rocket"
              src={GENERATED_ASSETS.asteroidDodgeRocket}
              alt=""
              context="AsteroidDodge:openingRocket"
              fallbackText=""
            />
          </div>
          <div className="asteroid-dodge-opening-copy">
            <span>Asteroid Dodge</span>
            <h1>Dodge the rock.</h1>
            <p>
              Catch glowing star cores while you glide between falling space
              rocks.
            </p>
            <button className="asteroid-begin-button" onClick={beginLevel}>
              Start flight
            </button>
          </div>
        </div>
      ) : (
        <div
          ref={stageRef}
          className={`asteroid-dodge-stage ${won ? "phase-done" : ""}`}
          style={{ "--rocket-x": `${rocketX}%` }}
          onPointerDown={handlePointerDown}
          onPointerMove={handlePointerMove}
          onPointerUp={handlePointerUp}
          onPointerCancel={handlePointerUp}
        >
          <SafeAssetImage
            className="asteroid-stage-bg"
            src={GENERATED_ASSETS.asteroidDodgeBg}
            alt=""
            context="AsteroidDodge:stageBg"
            fallbackText=""
          />
          <span className="asteroid-speed-line line-one" aria-hidden="true" />
          <span className="asteroid-speed-line line-two" aria-hidden="true" />
          <span className="asteroid-speed-line line-three" aria-hidden="true" />
          <div className="asteroid-dodge-hud">
            <div>
              <span>Asteroid Dodge</span>
              <strong>{won ? "Cargo full!" : `Level ${difficultyIndex + 1}`}</strong>
              <small>{difficulty.name}</small>
            </div>
            <div className="asteroid-dodge-progress" aria-label="Star cores caught">
              {Array.from({ length: ASTEROID_DODGE_CORE_TARGET }, (_, index) => (
                <span key={index} className={index < collected || won ? "on" : ""} />
              ))}
            </div>
          </div>
          <div className="asteroid-cargo-counter" aria-label="Cargo collected">
            <strong>{collected}</strong>
            <span>/ {ASTEROID_DODGE_CORE_TARGET} cores · {difficulty.name}</span>
          </div>
          <div className="asteroid-shield-meter" aria-label={`Bubble shield charge ${shieldCharge} of ${ASTEROID_DODGE_SHIELD_MAX}`}>
            <span>Bubble shield</span>
            <div>
              {Array.from({ length: ASTEROID_DODGE_SHIELD_MAX }, (_, index) => (
                <i key={index} className={index < shieldCharge ? "on" : ""} />
              ))}
            </div>
          </div>
          <div className="asteroid-motion-rule" aria-label="Motion rule">
            <span>Motion rule</span>
            <strong>{motionRule}</strong>
          </div>
          <div className={`asteroid-dodge-message ${won ? "done" : ""}`} role="status" aria-live="polite">
            <strong>{message}</strong>
          </div>
          {cores.map((core) => (
            <SafeAssetImage
              key={core.id}
              className="asteroid-core"
              src={GENERATED_ASSETS.sparkleStar}
              alt=""
              aria-hidden="true"
              context={`AsteroidDodge:core:${core.id}`}
              fallbackText=""
              style={{
                "--core-x": `${core.x}%`,
                "--core-y": `${core.y}vh`,
                "--core-pulse": `${core.pulse}`,
              }}
            />
          ))}
          {asteroids.map((rock) => (
            <SafeAssetImage
              key={rock.id}
              className="asteroid-rock"
              src={rock.rock}
              alt=""
              aria-hidden="true"
              context={`AsteroidDodge:rock:${rock.id}`}
              fallbackText=""
              style={{
                "--asteroid-x": `${rock.x}%`,
                "--asteroid-y": `${rock.y}vh`,
                "--asteroid-size": `${rock.size}px`,
                "--asteroid-spin": `${rock.spin}deg`,
              }}
            />
          ))}
          <div
            className={`asteroid-bubble-shield ${
              shieldCharge >= ASTEROID_DODGE_SHIELD_MAX ? "charged" : "charging"
            } ${shieldFlash ? "popped" : ""}`}
            aria-hidden="true"
          />
          <SafeAssetImage
            className="asteroid-ship"
            src={GENERATED_ASSETS.asteroidDodgeRocket}
            alt="rocket"
            context="AsteroidDodge:ship"
            fallbackText=""
          />
          <div className="asteroid-dodge-controls" aria-label="Rocket controls">
            <button onClick={() => nudgeShip(-14)} disabled={rocketX <= 7 || won}>
              ← Left
            </button>
            <button onClick={() => nudgeShip(14)} disabled={rocketX >= 93 || won}>
              Right →
            </button>
          </div>
          {won ? (
            <div className="asteroid-dodge-win">
              <button onClick={reset}>Play again</button>
              <button onClick={onExit}>Done</button>
            </div>
          ) : null}
        </div>
      )}
    </section>
  );
}
