const KID_REWARD_EMOJIS = {
  mercury: "⚡",
  venus: "💛",
  earth: "🌎",
  mars: "🚗",
  jupiter: "🎈",
  saturn: "🪐",
  uranus: "🧊",
  neptune: "🌊",
};

const KID_TREASURES = [
  {
    id: "star",
    label: "Star treasure",
    asset: GENERATED_ASSETS.sparkleStar,
    fact: "Stars are giant glowing balls of gas.",
  },
  {
    id: "moon",
    label: "Moon charm",
    asset: GENERATED_ASSETS.moon,
    fact: "The Moon goes around Earth.",
  },
  {
    id: "rocket",
    label: "Rocket treasure",
    asset: GENERATED_ASSETS.rocket,
    fact: "Rockets push down to fly up.",
  },
  {
    id: "alien",
    label: "Alien token",
    asset: GENERATED_ASSETS.alien,
    fact: "We have not found space aliens yet.",
  },
  {
    id: "bell",
    label: "Sound bell",
    asset: GENERATED_ASSETS.soundBell,
    fact: "Astronauts use radios to talk in space.",
  },
];

const ROCKET_PARTS = [
  {
    id: "nose",
    label: "Nose",
    asset: GENERATED_ASSETS.rocketNose,
    fact: "The nose helps air slide around the rocket.",
  },
  {
    id: "body",
    label: "Body",
    asset: GENERATED_ASSETS.rocketBody,
    fact: "The body holds the rocket fuel.",
  },
  {
    id: "fins",
    label: "Fins",
    asset: GENERATED_ASSETS.rocketFins,
    fact: "Fins help the rocket stay steady.",
  },
  {
    id: "window",
    label: "Window",
    asset: GENERATED_ASSETS.rocketWindow,
    fact: "The window lets Adam peek out at the stars.",
  },
  {
    id: "engine",
    label: "Engine",
    asset: GENERATED_ASSETS.rocketEngine,
    fact: "The engine makes the big blast.",
  },
  {
    id: "fuelTank",
    label: "Fuel Tank",
    asset: GENERATED_ASSETS.rocketFuelTank,
    fact: "The fuel tank stores energy for launch.",
  },
  {
    id: "fuelPipes",
    label: "Fuel Pipes",
    asset: GENERATED_ASSETS.rocketFuelPipes,
    fact: "Fuel pipes carry fuel down to the engine.",
  },
  {
    id: "igniter",
    label: "Igniter",
    asset: GENERATED_ASSETS.rocketIgniter,
    fact: "The igniter starts the engine fire.",
  },
  {
    id: "booster",
    label: "Booster",
    asset: GENERATED_ASSETS.rocketBooster,
    fact: "Boosters give rockets extra push.",
  },
  {
    id: "antenna",
    label: "Antenna",
    asset: GENERATED_ASSETS.rocketAntenna,
    fact: "The antenna helps send hello messages.",
  },
  {
    id: "badge",
    label: "Star Badge",
    asset: GENERATED_ASSETS.rocketBadge,
    fact: "The badge shows this rocket belongs to Adam.",
  },
];

const SIMPLE_ROCKET_PART_IDS = ["nose", "body", "fins", "engine", "booster"];

const ROCKET_COLORS = [
  { id: "red", label: "Red", value: "#ff6b5f" },
  { id: "blue", label: "Blue", value: "#5db7ff" },
  { id: "yellow", label: "Yellow", value: "#ffd66b" },
  { id: "green", label: "Green", value: "#79f2a6" },
];
const KID_TREASURE_SPOTS = [
  { left: "18%", top: "28%", "--delay": "0s" },
  { left: "72%", top: "22%", "--delay": "0.5s" },
  { left: "82%", top: "58%", "--delay": "1s" },
  { left: "38%", top: "70%", "--delay": "1.4s" },
  { left: "56%", top: "38%", "--delay": "1.8s" },
];

const KID_I_SPY = {
  sun: "Can you spot the glowing light?",
  mercury: "Can you find the tiny speedy planet?",
  venus: "Can you find the warm yellow clouds?",
  earth: "Can you spot blue oceans?",
  mars: "Can you find the red planet?",
  jupiter: "Can you find the biggest planet?",
  saturn: "Can you spot the giant rings?",
  uranus: "Can you find the sideways icy world?",
  neptune: "Can you find the deep blue planet?",
};

const KID_FACT_REWARDS = {
  mercury: "Mercury is the tiny fast one.",
  venus: "Venus is wrapped in warm clouds.",
  earth: "Earth has oceans and one moon.",
  mars: "Mars is the red planet.",
  jupiter: "Jupiter is the biggest planet.",
  saturn: "Saturn has giant rings.",
  uranus: "Uranus rolls on its side.",
  neptune: "Neptune is deep blue and windy.",
};

const SUIT_COLORS = [
  { id: "classic", label: "White", color: "#f7fbff", accent: "#76d8ff" },
  { id: "sunny", label: "Sunny", color: "#ffd66b", accent: "#ff8a5c" },
  { id: "bubble", label: "Blue", color: "#76d8ff", accent: "#d9b6ff" },
  { id: "leaf", label: "Green", color: "#8cffb0", accent: "#ffd66b" },
  { id: "berry", label: "Pink", color: "#ff9bd2", accent: "#9ad7ff" },
];

function useSuitColor() {
  const [id, setId] = useState(() => {
    try {
      return localStorage.getItem("planets_suit_color") || "classic";
    } catch {
      return "classic";
    }
  });
  const update = (next) => {
    setId(next);
    try {
      localStorage.setItem("planets_suit_color", next);
    } catch {}
  };
  return [id, update];
}

function currentSuit(id) {
  return SUIT_COLORS.find((s) => s.id === id) || SUIT_COLORS[0];
}

function playKidSound(kind = "random") {
  if (!window.__narration || !window.__narration.isEnabled()) return;
  const AudioCtx = window.AudioContext || window.webkitAudioContext;
  if (!AudioCtx) return;
  try {
    const ctx = playKidSound.ctx || new AudioCtx();
    playKidSound.ctx = ctx;
    if (ctx.state === "suspended") ctx.resume();
    const sounds = {
      chime: [660, 880, 1320],
      whoosh: [180, 260, 420],
      giggle: [520, 700, 590, 780],
      boop: [300, 520],
      // "pop" is the gentle wrong-answer cue used by the quiz games (planet-sort,
      // engine-match, asteroid bump, mission-control miss). A short two-note
      // descent reads as "not this one" without sounding scary or punishing.
      pop: [420, 280],
    };
    const names = Object.keys(sounds);
    const notes =
      sounds[kind] || sounds[names[Math.floor(Math.random() * names.length)]];
    notes.forEach((freq, i) => {
      const osc = ctx.createOscillator();
      const gain = ctx.createGain();
      osc.type = kind === "whoosh" ? "sawtooth" : "sine";
      osc.frequency.setValueAtTime(freq, ctx.currentTime + i * 0.08);
      gain.gain.setValueAtTime(0.0001, ctx.currentTime + i * 0.08);
      gain.gain.exponentialRampToValueAtTime(
        0.08,
        ctx.currentTime + i * 0.08 + 0.02,
      );
      gain.gain.exponentialRampToValueAtTime(
        0.0001,
        ctx.currentTime + i * 0.08 + 0.18,
      );
      osc.connect(gain).connect(ctx.destination);
      osc.start(ctx.currentTime + i * 0.08);
      osc.stop(ctx.currentTime + i * 0.08 + 0.2);
    });
  } catch {}
}

function useKidStickers() {
  const [stickers, setStickers] = useState(() => {
    try {
      const raw = localStorage.getItem("planets_kid_stickers");
      const parsed = raw ? JSON.parse(raw) : [];
      return Array.isArray(parsed) ? parsed : [];
    } catch {
      return [];
    }
  });
  const addSticker = (planet) => {
    const reward = KID_REWARD_EMOJIS[planet.id] || "⭐";
    setStickers((current) => {
      if (current.some((s) => s.id === planet.id)) return current;
      const next = [
        ...current,
        {
          id: planet.id,
          name: planet.name,
          reward,
          fact: KID_FACT_REWARDS[planet.id],
          color: planet.color,
        },
      ];
      try {
        localStorage.setItem("planets_kid_stickers", JSON.stringify(next));
      } catch {}
      return next;
    });
    return reward;
  };
  return [stickers, addSticker];
}

function useVisitedPlanets() {
  const [visited, setVisited] = useState(() => {
    try {
      const raw = localStorage.getItem("planets_visited_worlds");
      const parsed = raw ? JSON.parse(raw) : [];
      if (!Array.isArray(parsed)) return [];
      const known = new Set(PLANETS.map((planet) => planet.id));
      return parsed.filter(
        (id, index) => known.has(id) && parsed.indexOf(id) === index,
      );
    } catch {
      return [];
    }
  });
  const recordVisit = (planet) => {
    if (!planet) return;
    setVisited((current) => {
      if (current.includes(planet.id)) return current;
      const next = [...current, planet.id];
      try {
        localStorage.setItem("planets_visited_worlds", JSON.stringify(next));
      } catch {}
      return next;
    });
  };
  const resetVisits = () => {
    setVisited([]);
    try {
      localStorage.removeItem("planets_visited_worlds");
    } catch {}
  };
  return [visited, recordVisit, resetVisits];
}

function useFavoritePlanet() {
  const [favorite, setFavorite] = useState(() => {
    try {
      const saved = localStorage.getItem("planets_favorite_world");
      return PLANETS.some((planet) => planet.id === saved) ? saved : "";
    } catch {
      return "";
    }
  });
  const updateFavorite = (planetId) => {
    setFavorite(planetId);
    try {
      if (planetId) {
        localStorage.setItem("planets_favorite_world", planetId);
      } else {
        localStorage.removeItem("planets_favorite_world");
      }
    } catch {}
  };
  return [favorite, updateFavorite];
}

function planetAbbrev(planet) {
  const custom = {
    mercury: "Me",
    venus: "V",
    earth: "E",
    mars: "Ma",
    jupiter: "J",
    saturn: "Sa",
    uranus: "U",
    neptune: "N",
    sun: "Su",
  };
  return custom[planet.id] || planet.name.slice(0, 2);
}

function dailyMissionPlanet() {
  const now = new Date();
  const dayNumber = Math.floor(
    Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()) / 86400000,
  );
  return PLANETS[dayNumber % PLANETS.length];
}

function FieldJournalStrip({ visited, favoritePlanetId, onSelect, onReset }) {
  const worlds = PLANETS;
  const nextWorld = worlds.find((planet) => !visited.includes(planet.id));
  const favoriteWorld = worlds.find((planet) => planet.id === favoritePlanetId);
  const dailyWorld = dailyMissionPlanet();
  const dailyDone = visited.includes(dailyWorld.id);
  return (
    <div className="field-journal" aria-label="Visited worlds">
      <span className="field-journal-label">
        Field journal {visited.length}/{worlds.length}
      </span>
      <div className="field-journal-worlds">
        {worlds.map((planet) => {
          const seen = visited.includes(planet.id);
          return (
            <button
              type="button"
              key={planet.id}
              className={`field-journal-dot ${seen ? "seen" : ""}`}
              style={{ "--world-color": planet.color }}
              title={seen ? `${planet.name} visited` : planet.name}
              aria-label={
                seen ? `Open ${planet.name}, visited` : `Open ${planet.name}`
              }
              onClick={() => onSelect(planet)}
            >
              {planetAbbrev(planet)}
            </button>
          );
        })}
      </div>
      {nextWorld ? (
        <button
          type="button"
          className="field-journal-next"
          aria-label={`Open next unvisited world, ${nextWorld.name}`}
          onClick={() => onSelect(nextWorld)}
        >
          Next: {nextWorld.name}
        </button>
      ) : (
        <span className="field-journal-complete">All worlds logged</span>
      )}
      {visited.length ? (
        <button
          type="button"
          className="field-journal-reset"
          aria-label="Reset field journal"
          title="Reset field journal"
          onClick={onReset}
        >
          Reset
        </button>
      ) : null}
      {favoriteWorld ? (
        <button
          type="button"
          className="field-journal-favorite"
          aria-label={`Open favorite world, ${favoriteWorld.name}`}
          title={`Favorite world: ${favoriteWorld.name}`}
          onClick={() => onSelect(favoriteWorld)}
        >
          ★ {favoriteWorld.name}
        </button>
      ) : null}
      <button
        type="button"
        className={`field-journal-daily ${dailyDone ? "done" : ""}`}
        aria-pressed={dailyDone ? "true" : "false"}
        aria-label={`Open today's mission world, ${dailyWorld.name}`}
        title={`Today's mission: ${dailyWorld.name}`}
        onClick={() => onSelect(dailyWorld)}
      >
        {dailyDone ? "Today done" : `Today: ${dailyWorld.name}`}
      </button>
    </div>
  );
}

function KidMissionPanel({
  active,
  target,
  stickers,
  starScore,
  onToggle,
  onSkip,
}) {
  return (
    <div className={`kid-panel ${active ? "active" : ""}`}>
      <button
        className={`kid-toggle ${active ? "on" : ""}`}
        onClick={onToggle}
        aria-pressed={active ? "true" : "false"}
      >
        <SafeAssetImage
          src={GENERATED_ASSETS.missionBadge}
          alt=""
          context="KidPanel:missionBadge"
          fallbackText=""
        />
        <span>Little Explorer</span>
      </button>
      {active ? (
        <div className="kid-mission">
          <div className="kid-mission-text">
            <span className="kid-mission-kicker">Mission</span>
            <strong>Find {target.name}</strong>
          </div>
          <button
            className="kid-skip"
            onClick={onSkip}
            title="Try another planet"
          >
            ↻
          </button>
          <div className="kid-sticker-row" aria-label="Collected stickers">
            {stickers.length ? (
              stickers.map((s) => (
                <span
                  key={s.id}
                  className="kid-sticker-chip"
                  title={s.fact || `${s.name} sticker`}
                  style={{ "--sticker-color": s.color }}
                >
                  {s.reward}
                </span>
              ))
            ) : (
              <span className="kid-sticker-empty">
                Tap planets to win stickers
              </span>
            )}
          </div>
        </div>
      ) : null}
    </div>
  );
}

function KidStarSurprises({
  active,
  collected,
  collectingId,
  nextTreasureId,
  busy,
  onTap,
}) {
  if (!active) return null;
  return (
    <div className="kid-stars">
      {KID_TREASURE_SPOTS.map((spot, i) => {
        const treasure = KID_TREASURES[i % KID_TREASURES.length];
        if (collected.includes(treasure.id)) return null;
        return (
          <button
            key={treasure.id}
            className={`kid-star-button ${collectingId === treasure.id ? "collecting" : ""} ${nextTreasureId === treasure.id ? "next" : ""}`}
            style={spot}
            disabled={busy}
            onClick={(event) => {
              if (busy) return;
              const rect = event.currentTarget.getBoundingClientRect();
              onTap(treasure, {
                x: rect.left + rect.width / 2,
                y: rect.top + rect.height / 2,
              });
            }}
            aria-label={`Collect ${treasure.label}`}
          >
            <SafeAssetImage
              src={treasure.asset}
              alt=""
              context="KidPanel:treasure"
              fallbackText=""
            />
          </button>
        );
      })}
    </div>
  );
}

function TreasureAstronautPickup({ flight, onComplete }) {
  const ref = useRef(null);
  const cargoRef = useRef(null);
  const startRef = useRef(0);

  useEffect(() => {
    if (!flight || !ref.current) return;
    startRef.current = performance.now();
    const FRAME_W = 126;
    const FRAME_H = 90;
    const frames = [0, 1, 2, 3, 4, 5, 6, 7];
    let raf;
    let completed = false;

    function tick(now) {
      const elapsed = now - startRef.current;
      const dur = 1450;
      const t = Math.min(1, elapsed / dur);
      const e = t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
      const lift = Math.sin(Math.PI * e) * 84;
      const x = flight.from.x + (flight.to.x - flight.from.x) * e;
      const y = flight.from.y + (flight.to.y - flight.from.y) * e - lift;
      const dx = flight.to.x - flight.from.x;
      const dy = flight.to.y - flight.from.y;
      const angle = (Math.atan2(dy, dx) * 180) / Math.PI;
      const scale = 0.72 + 0.22 * Math.sin(Math.PI * e);
      const frame = frames[Math.floor(elapsed / 110) % frames.length];
      const col = frame % 4;
      const row = Math.floor(frame / 4);
      const el = ref.current;
      if (el) {
        el.style.left = x - FRAME_W / 2 + "px";
        el.style.top = y - FRAME_H / 2 + "px";
        el.style.transform = `rotate(${angle}deg) scale(${scale})`;
        el.style.backgroundPosition = `-${col * FRAME_W}px -${row * FRAME_H}px`;
        el.style.opacity = String(Math.min(1, t * 3));
      }
      if (cargoRef.current) {
        const grabbed = t > 0.52;
        cargoRef.current.style.opacity = grabbed ? "1" : "0";
        cargoRef.current.style.transform = grabbed
          ? "translate(-50%, -50%) scale(1)"
          : "translate(-50%, -50%) scale(0.4)";
      }
      if (t >= 1 && !completed) {
        completed = true;
        onComplete(flight.treasure);
      }
      if (!completed) raf = requestAnimationFrame(tick);
    }

    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [flight && flight.id]);

  if (!flight) return null;
  return (
    <div
      ref={ref}
      className="treasure-astronaut-pickup"
      style={{ opacity: 0 }}
      aria-hidden="true"
    >
      <span className="treasure-pickup-beam" />
      <SafeAssetImage
        ref={cargoRef}
        className="treasure-pickup-cargo"
        src={flight.treasure.asset}
        alt=""
        context="TreasurePickup:cargo"
        fallbackText=""
      />
    </div>
  );
}

function KidRewardBurst({ reward }) {
  if (!reward) return null;
  return (
    <div className="kid-reward-burst" role="status" aria-live="polite">
      <span className="kid-reward-emoji">
        {reward.asset ? (
          <SafeAssetImage
            src={reward.asset}
            alt=""
            context="KidReward:burst"
            fallbackText=""
          />
        ) : (
          reward.emoji
        )}
      </span>
      <span className="kid-reward-text">{reward.text}</span>
    </div>
  );
}
