// gravity-goldfish.jsx
// Preschool physics mini-game: tap gravity pulses to arc a space goldfish through moon bubbles.

const GRAVITY_GOLDFISH_SWIM_SHEET =
  "data/generated-assets/physics/gravity-goldfish-swim-sheet.png?v=swim-sheet-20260601";
const GRAVITY_GOLDFISH_SWIM_COLUMNS = 4;
const GRAVITY_GOLDFISH_SWIM_IDLE_FRAMES = 6;

const GRAVITY_GOLDFISH_PULSES = [
  {
    id: "soft",
    label: "Soft pulse",
    force: 0.72,
    icon: "·",
    line: "Soft gravity makes a small curve.",
  },
  {
    id: "medium",
    label: "Moon pulse",
    force: 1.05,
    icon: "○",
    line: "Moon gravity pulls a medium curve.",
  },
  {
    id: "strong",
    label: "Big pulse",
    force: 1.42,
    icon: "●",
    line: "A big pull makes a bigger arc.",
  },
];

const GRAVITY_GOLDFISH_TARGETS = [
  { id: "low", x: 31, y: 70, label: "low bubble" },
  { id: "middle", x: 53, y: 48, label: "middle bubble" },
  { id: "high", x: 75, y: 30, label: "high bubble" },
];

function gravityGoldfishClamp(value, min, max) {
  return Math.max(min, Math.min(max, value));
}

function playGravityGoldfishTone(kind) {
  try {
    const AudioContext = window.AudioContext || window.webkitAudioContext;
    if (!AudioContext) return;
    const context = new AudioContext();
    const oscillator = context.createOscillator();
    const gain = context.createGain();
    const tone =
      kind === "complete"
        ? { start: 660, end: 990, duration: 0.34, volume: 0.12 }
        : kind === "hit"
          ? { start: 540, end: 760, duration: 0.2, volume: 0.1 }
          : { start: 260, end: 390, duration: 0.16, volume: 0.075 };
    oscillator.type = kind === "pulse" ? "triangle" : "sine";
    oscillator.frequency.setValueAtTime(tone.start, context.currentTime);
    oscillator.frequency.exponentialRampToValueAtTime(
      tone.end,
      context.currentTime + tone.duration,
    );
    gain.gain.setValueAtTime(0.0001, context.currentTime);
    gain.gain.exponentialRampToValueAtTime(
      tone.volume,
      context.currentTime + 0.025,
    );
    gain.gain.exponentialRampToValueAtTime(
      0.0001,
      context.currentTime + tone.duration,
    );
    oscillator.connect(gain);
    gain.connect(context.destination);
    oscillator.start();
    oscillator.stop(context.currentTime + tone.duration + 0.02);
    window.setTimeout(() => context.close?.(), (tone.duration + 0.08) * 1000);
  } catch {}
}

function GravityGoldfishLevel({ onExit }) {
  const [fish, setFish] = React.useState({ x: 12, y: 70, arc: 0 });
  const [collected, setCollected] = React.useState([]);
  const [pulse, setPulse] = React.useState(GRAVITY_GOLDFISH_PULSES[1]);
  const [swimFrame, setSwimFrame] = React.useState(0);
  const [message, setMessage] = React.useState(
    "Tap a gravity pulse. The pull changes the goldfish path.",
  );
  const [trail, setTrail] = React.useState([]);
  const [fishMood, setFishMood] = React.useState("");
  const moodTimerRef = React.useRef(null);
  const moodFrameRef = React.useRef(null);
  const collectedSet = React.useMemo(() => new Set(collected), [collected]);
  const nextTarget =
    GRAVITY_GOLDFISH_TARGETS.find((target) => !collectedSet.has(target.id)) ||
    GRAVITY_GOLDFISH_TARGETS[GRAVITY_GOLDFISH_TARGETS.length - 1];
  const complete = collected.length >= GRAVITY_GOLDFISH_TARGETS.length;
  const missionLine = complete
    ? "All 3 moon bubbles are safe. Tap any pulse for another swim."
    : `Bubble ${collected.length + 1}/${GRAVITY_GOLDFISH_TARGETS.length}: aim for the ${nextTarget.label}.`;
  const displayedSwimFrame =
    pulse.force > 1.25
      ? GRAVITY_GOLDFISH_SWIM_IDLE_FRAMES + (swimFrame % 2)
      : swimFrame % GRAVITY_GOLDFISH_SWIM_IDLE_FRAMES;
  const swimFrameColumn = displayedSwimFrame % GRAVITY_GOLDFISH_SWIM_COLUMNS;
  const swimFrameRow = Math.floor(displayedSwimFrame / GRAVITY_GOLDFISH_SWIM_COLUMNS);
  const swimFrameX =
    (swimFrameColumn / (GRAVITY_GOLDFISH_SWIM_COLUMNS - 1)) * 100;
  const swimFrameY = swimFrameRow * 100;

  React.useEffect(() => {
    const delay = pulse.force > 1.25 ? 92 : pulse.force < 0.9 ? 165 : 128;
    setSwimFrame(0);
    const timer = window.setInterval(() => {
      setSwimFrame(
        (frame) =>
          (frame + 1) %
          (pulse.force > 1.25 ? 2 : GRAVITY_GOLDFISH_SWIM_IDLE_FRAMES),
      );
    }, delay);
    return () => window.clearInterval(timer);
  }, [pulse.force]);

  React.useEffect(
    () => () => {
      if (moodFrameRef.current) window.cancelAnimationFrame(moodFrameRef.current);
      if (moodTimerRef.current) window.clearTimeout(moodTimerRef.current);
    },
    [],
  );

  const showFishMood = (mood, duration = 420) => {
    if (moodFrameRef.current) window.cancelAnimationFrame(moodFrameRef.current);
    if (moodTimerRef.current) window.clearTimeout(moodTimerRef.current);
    setFishMood("");
    moodFrameRef.current = window.requestAnimationFrame(() => {
      moodFrameRef.current = null;
      setFishMood(mood);
    });
    moodTimerRef.current = window.setTimeout(() => {
      setFishMood("");
      moodTimerRef.current = null;
    }, duration);
  };

  const reset = () => {
    setFish({ x: 12, y: 70, arc: 0 });
    setCollected([]);
    setPulse(GRAVITY_GOLDFISH_PULSES[1]);
    setTrail([]);
    setFishMood("");
    setMessage("Try again. Choose a pull, then watch the path curve.");
  };

  const launchPulse = (nextPulse) => {
    if (complete) {
      reset();
      return;
    }
    const dx = nextTarget.x - fish.x;
    const lift = 18 * nextPulse.force;
    const nextY = gravityGoldfishClamp(nextTarget.y + (1.15 - nextPulse.force) * 18, 24, 76);
    const nextFish = {
      x: gravityGoldfishClamp(fish.x + Math.max(12, dx * 0.88), 10, nextTarget.x),
      y: nextY,
      arc: nextPulse.force,
    };
    const hit =
      Math.abs(nextFish.x - nextTarget.x) <= 6 &&
      Math.abs(nextFish.y - nextTarget.y) <= 13;
    setPulse(nextPulse);
    setFish(nextFish);
    playGravityGoldfishTone("pulse");
    showFishMood("dart");
    setTrail((current) =>
      [
        ...current,
        {
          id: `${Date.now()}-${nextPulse.id}`,
          x: fish.x + dx * 0.46,
          y: gravityGoldfishClamp(fish.y - lift, 16, 78),
          force: nextPulse.force,
        },
      ].slice(-4),
    );
    if (hit) {
      const nextCollected = [...collected, nextTarget.id];
      setCollected(nextCollected);
      playGravityGoldfishTone(
        nextCollected.length >= GRAVITY_GOLDFISH_TARGETS.length
          ? "complete"
          : "hit",
      );
      showFishMood(
        nextCollected.length >= GRAVITY_GOLDFISH_TARGETS.length
          ? "celebrate"
          : "happy",
        nextCollected.length >= GRAVITY_GOLDFISH_TARGETS.length ? 880 : 520,
      );
      setMessage(`${nextPulse.line} You caught the ${nextTarget.label}!`);
      if (nextCollected.length >= GRAVITY_GOLDFISH_TARGETS.length) {
        setMessage("Three moon bubbles! The goldfish does a happy loop.");
        window.SpaceExplorerFoundation?.celebrateGameFinish?.({
          gameId: "gravity-goldfish",
          emoji: "🐠",
          title: "Gravity path mapped!",
          message: "The goldfish used soft, moon, and big pulls.",
        });
      }
      return;
    }
    setMessage(`${nextPulse.line} Aim for the ${nextTarget.label}.`);
  };

  return (
    <section className="gravity-goldfish-level" aria-label="Gravity Goldfish game">
      <button type="button" className="gravity-goldfish-back" onClick={onExit}>
        Back
      </button>
      <div className="gravity-goldfish-shell">
        <div className="gravity-goldfish-copy">
          <span>Moon Physics</span>
          <h1>Gravity Goldfish</h1>
          <p>
            Help the space goldfish swim across the Moon. Different gravity
            pulses make different paths.
          </p>
          <div className="gravity-goldfish-message" role="status" aria-live="polite">
            {message}
          </div>
          <div className="gravity-goldfish-status" aria-live="polite">
            {missionLine}
          </div>
          <div className="gravity-goldfish-controls" aria-label="Gravity pulse controls">
            {GRAVITY_GOLDFISH_PULSES.map((option) => (
              <button
                key={option.id}
                type="button"
                className={pulse.id === option.id ? "on" : ""}
                onClick={() => launchPulse(option)}
                aria-pressed={pulse.id === option.id ? "true" : "false"}
              >
                <span aria-hidden="true">{option.icon}</span>
                <strong>{complete ? "Try again" : option.label}</strong>
              </button>
            ))}
          </div>
        </div>
        <div className="gravity-goldfish-stage">
          <div className="gravity-goldfish-moon" aria-hidden="true" />
          {trail.map((mark) => (
            <span
              key={mark.id}
              className="gravity-goldfish-trail"
              style={{
                left: `${mark.x}%`,
                top: `${mark.y}%`,
                transform: `scale(${mark.force})`,
              }}
            />
          ))}
          {GRAVITY_GOLDFISH_TARGETS.map((target, index) => (
            <span
              key={target.id}
              className={
                collectedSet.has(target.id)
                  ? "gravity-goldfish-target collected"
                  : "gravity-goldfish-target"
              }
              style={{ left: `${target.x}%`, top: `${target.y}%` }}
              aria-label={`${target.label}${collectedSet.has(target.id) ? " collected" : ""}`}
            >
              {collectedSet.has(target.id) ? "✓" : index + 1}
            </span>
          ))}
          <div
            className={
              fishMood
                ? `gravity-goldfish-fish ${fishMood}`
                : "gravity-goldfish-fish"
            }
            role="img"
            aria-label="Space goldfish swimming"
            style={{
              left: `${fish.x}%`,
              top: `${fish.y}%`,
              "--goldfish-frame-x": `${swimFrameX}%`,
              "--goldfish-frame-y": `${swimFrameY}%`,
              "--goldfish-swim-sheet": `url("${GRAVITY_GOLDFISH_SWIM_SHEET}")`,
              "--goldfish-rotate": `${(pulse.force - 1) * -10}deg`,
            }}
          />
        </div>
      </div>
    </section>
  );
}

window.GravityGoldfishLevel = GravityGoldfishLevel;
