// Main Solar System Explorer app — Three.js powered
const { useState, useEffect, useRef, useCallback } = React;

// Narration singleton lives in narration.js (loaded before any text/babel
// script in index.html) so every consumer can assume window.__narration
// exists. The hook below just adapts it to React.
function useSoundToggle() {
  const [on, setOn] = useState(() => window.__narration.isEnabled());
  useEffect(() => window.__narration.subscribe(setOn), []);
  return [on, (v) => window.__narration.setEnabled(v)];
}

function readStorageValue(key, fallback) {
  if (!key) return fallback;
  try {
    const value = localStorage.getItem(key);
    return value === null ? fallback : value;
  } catch {
    return fallback;
  }
}

function readStorageFlag(key, fallback = false) {
  const value = readStorageValue(key, fallback ? "1" : "0");
  if (value === "1" || value === true) return true;
  if (value === "0" || value === false) return false;
  return Boolean(fallback);
}

function writeStorageFlag(key, value) {
  writeStorageValue(key, value ? "1" : "0");
}

function readStorageEnum(key, allowedValues, fallback) {
  const allowed = Array.isArray(allowedValues) ? allowedValues : [];
  const value = readStorageValue(key, fallback);
  return allowed.includes(value) ? value : fallback;
}

function readStorageInt(
  key,
  fallback = 0,
  min = Number.NEGATIVE_INFINITY,
  max = Number.POSITIVE_INFINITY,
) {
  const fallbackNumber = Number.isFinite(fallback) ? fallback : 0;
  const raw = readStorageValue(key, String(fallbackNumber));
  const parsed = Number.parseInt(raw, 10);
  const safe = Number.isFinite(parsed) ? parsed : fallbackNumber;
  return Math.max(min, Math.min(max, safe));
}

function writeStorageValue(key, value) {
  if (!key) return;
  try {
    localStorage.setItem(key, value);
  } catch {}
}

function removeStorageValue(key) {
  if (!key) return;
  try {
    localStorage.removeItem(key);
  } catch {}
}

function readStorageJson(key, fallback) {
  if (!key) return fallback;
  try {
    const raw = localStorage.getItem(key);
    return raw ? JSON.parse(raw) : fallback;
  } catch {
    return fallback;
  }
}

function playNarration(file) {
  if (!window.__narration || typeof file !== "string") return;
  const clip = file.trim();
  if (!clip) return;
  window.__narration.play(clip);
}

function stopNarration() {
  if (window.__narration) window.__narration.stop();
}

// ---------- Helmet sticker decorator ----------
// Kid-picked emoji sticker that rides above the astronaut's helmet during flight.
// Persisted to localStorage so the choice carries across sessions.
const GENERATED_ASSETS = {
  helmet: "data/generated-assets/helmet-blank.png",
  sonAstronaut: "data/generated-assets/son-astronaut-character.png",
  astronautFlying: "data/sprites/astronaut-flying.png",
  comet: "data/generated-assets/comet-friend.png",
  missionBadge: "data/generated-assets/mission-badge.png",
  storyShelf: "data/generated-assets/storybook-space-shelf-six.png",
  soundBell: "data/generated-assets/sound-bell.png",
  rocket: "data/generated-assets/rocket-whoosh.png",
  sparkleStar: "data/generated-assets/sparkle-star.png",
  alien: "data/generated-assets/alien-token.png",
  moon: "data/generated-assets/moon-charm.png",
  helmetLeft: "data/generated-assets/helmet-side-left.png",
  helmetBack: "data/generated-assets/helmet-back-round.png",
  helmetRight: "data/generated-assets/helmet-side-right.png",
  rocketNose: "data/generated-assets/rocket-builder/rocket-part-nose.png",
  rocketBody: "data/generated-assets/rocket-builder/rocket-part-body.png",
  rocketFins: "data/generated-assets/rocket-builder/rocket-part-fins.png",
  rocketBooster: "data/generated-assets/rocket-builder/rocket-part-booster.png",
  rocketWindow: "data/generated-assets/rocket-builder/rocket-part-window.png",
  rocketEngine: "data/generated-assets/rocket-builder/rocket-part-engine.png",
  rocketFuelTank:
    "data/generated-assets/rocket-builder/rocket-part-fuel-tank.png",
  rocketFuelPipes:
    "data/generated-assets/rocket-builder/rocket-part-fuel-pipes.png",
  rocketIgniter: "data/generated-assets/rocket-builder/rocket-part-igniter.png",
  rocketAntenna: "data/generated-assets/rocket-builder/rocket-part-antenna.png",
  rocketBadge: "data/generated-assets/rocket-builder/rocket-part-badge.png",
  rocketFire: "data/generated-assets/rocket-builder/rocket-launch-fire.png",
  pilotIssTarget: "data/generated-assets/pilot-school/iss-docking-target.png",
  pilotControlBoard:
    "data/generated-assets/pilot-school/control-board-panel.png",
  pilotTrainingRocket:
    "data/generated-assets/pilot-school/pilot-training-rocket.png",
  asteroidDodgeBg:
    "data/generated-assets/asteroid-dodge/asteroid-dodge-level-bg.png",
  gravityAstronautJump:
    "data/generated-assets/gravity-jump/astronaut-moon-jump-sheet.png",
  gravityWorldsBg:
    "data/generated-assets/gravity-jump/gravity-worlds-full-bg.png",
  gravityBgMoon: "data/generated-assets/gravity-jump/gravity-bg-moon.png",
  gravityBgEarth: "data/generated-assets/gravity-jump/gravity-bg-earth-v2.png",
  gravityBgMars: "data/generated-assets/gravity-jump/gravity-bg-mars.png",
  gravityBgBig: "data/generated-assets/gravity-jump/gravity-bg-big.png",
  gravityAstronautJumpClean:
    "data/generated-assets/gravity-jump/astronaut-moon-jump-sheet-normalized.png",
  asteroidRock:
    "data/generated-assets/moon-rover/rocks/moon-rock-meteorite.png",
  asteroidIce: "data/generated-assets/moon-rover/rocks/moon-rock-ice.png",
};

function getGeneratedAsset(name, fallback = "") {
  if (!name || !Object.prototype.hasOwnProperty.call(GENERATED_ASSETS, name)) {
    return fallback;
  }
  return GENERATED_ASSETS[name] || fallback;
}

function reportSpaceError(message, detail = {}) {
  try {
    if (!window.__spaceErrors) window.__spaceErrors = [];
    window.__spaceErrors.push({
      message: message || "Space Explorer error",
      source: detail.source || "app",
      detail,
      time: Date.now(),
    });
  } catch {}
}

function reportAssetLoadFailure(src, context = "asset") {
  reportSpaceError("Image asset failed to load.", {
    source: context,
    asset: src || "",
  });
}

const SafeAssetImage = React.forwardRef(function SafeAssetImage(
  {
    src,
    alt = "",
    className = "",
    fallbackClassName = "",
    fallbackText = "Picture is loading.",
    context = "SafeAssetImage",
    ...props
  },
  ref,
) {
  const [failed, setFailed] = useState(false);
  useEffect(() => {
    setFailed(false);
  }, [src]);
  if (!src || failed) {
    const fallbackClasses =
      `${className} asset-image-fallback ${fallbackClassName}`.trim();
    return (
      <span className={fallbackClasses} role="status">
        {fallbackText}
      </span>
    );
  }
  return (
    <img
      {...props}
      ref={ref}
      className={className}
      src={src}
      alt={alt}
      onError={() => {
        setFailed(true);
        reportAssetLoadFailure(src, context);
      }}
    />
  );
});

const GRAVITY_JUMP_FRAMES = [
  "data/generated-assets/gravity-jump/frames/astronaut-jump-0.png",
  "data/generated-assets/gravity-jump/frames/astronaut-jump-1.png",
  "data/generated-assets/gravity-jump/frames/astronaut-jump-2.png",
  "data/generated-assets/gravity-jump/frames/astronaut-jump-3.png",
  "data/generated-assets/gravity-jump/frames/astronaut-jump-4.png",
  "data/generated-assets/gravity-jump/frames/astronaut-jump-5.png",
];

const STAR_HOME_PAGES = [
  {
    image: "data/generated-assets/storybook/star-home-page-01.png",
    text: "Adam waved from his ship. Pip waved too.",
    audio: "story_01.mp3",
  },
  {
    image: "data/generated-assets/storybook/star-home-page-02.png",
    text: "A tiny star blinked beside the moon rocks.",
    audio: "story_02.mp3",
  },
  {
    image: "data/generated-assets/storybook/star-home-page-03.png",
    text: "Adam and Pip bounced across the quiet moon.",
    audio: "story_03.mp3",
  },
  {
    image: "data/generated-assets/storybook/star-home-page-04.png",
    text: "A golden comet showed them a sparkly path.",
    audio: "story_04.mp3",
  },
  {
    image: "data/generated-assets/storybook/star-home-page-05.png",
    text: "They zoomed past Saturn and its soft, glowing rings.",
    audio: "story_05.mp3",
  },
  {
    image: "data/generated-assets/storybook/star-home-page-06.png",
    text: "Pip shared moon cookies. The little star felt brave.",
    audio: "story_06.mp3",
  },
  {
    image: "data/generated-assets/storybook/star-home-page-07.png",
    text: "At last, the star found its bright family.",
    audio: "story_07.mp3",
  },
  {
    image: "data/generated-assets/storybook/star-home-page-08.png",
    text: "Adam and Pip smiled. Goodnight, little stars.",
    audio: "story_08.mp3",
  },
];

const MOON_SEED_PAGES = [
  {
    image: "data/generated-assets/storybook-moon-seed/moon-seed-page-01.png",
    text: "Adam found a moon seed glowing in the dust.",
    audio: "moonseed_01.mp3",
  },
  {
    image: "data/generated-assets/storybook-moon-seed/moon-seed-page-02.png",
    text: "He carried it carefully in his little bucket.",
    audio: "moonseed_02.mp3",
  },
  {
    image: "data/generated-assets/storybook-moon-seed/moon-seed-page-03.png",
    text: "Pip helped pat the soft moon soil.",
    audio: "moonseed_03.mp3",
  },
  {
    image: "data/generated-assets/storybook-moon-seed/moon-seed-page-04.png",
    text: "They watered the seed with drops of starlight.",
    audio: "moonseed_04.mp3",
  },
  {
    image: "data/generated-assets/storybook-moon-seed/moon-seed-page-05.png",
    text: "A tiny glowing sprout peeked up and smiled.",
    audio: "moonseed_05.mp3",
  },
  {
    image: "data/generated-assets/storybook-moon-seed/moon-seed-page-06.png",
    text: "Soon, star flowers twinkled in the garden.",
    audio: "moonseed_06.mp3",
  },
  {
    image: "data/generated-assets/storybook-moon-seed/moon-seed-page-07.png",
    text: "The planets came close to see the shine.",
    audio: "moonseed_07.mp3",
  },
  {
    image: "data/generated-assets/storybook-moon-seed/moon-seed-page-08.png",
    text: "Adam and Pip rested under the star flower.",
    audio: "moonseed_08.mp3",
  },
];

const COMET_HAT_PAGES = [
  {
    image: "data/generated-assets/storybook-comet-hat/comet-hat-page-01.png",
    text: "I found a tiny comet by my boots.",
    audio: "comethat_01.mp3",
  },
  {
    image: "data/generated-assets/storybook-comet-hat/comet-hat-page-02.png",
    text: "I put it on my helmet like a hat.",
    audio: "comethat_02.mp3",
  },
  {
    image: "data/generated-assets/storybook-comet-hat/comet-hat-page-03.png",
    text: "Hop, hop! My comet hat sparkled.",
    audio: "comethat_03.mp3",
  },
  {
    image: "data/generated-assets/storybook-comet-hat/comet-hat-page-04.png",
    text: "I waved hello to Earth.",
    audio: "comethat_04.mp3",
  },
  {
    image: "data/generated-assets/storybook-comet-hat/comet-hat-page-05.png",
    text: "My alien friend laughed with me.",
    audio: "comethat_05.mp3",
  },
  {
    image: "data/generated-assets/storybook-comet-hat/comet-hat-page-06.png",
    text: "We followed little star crumbs.",
    audio: "comethat_06.mp3",
  },
  {
    image: "data/generated-assets/storybook-comet-hat/comet-hat-page-07.png",
    text: "I helped the comet fly back home.",
    audio: "comethat_07.mp3",
  },
  {
    image: "data/generated-assets/storybook-comet-hat/comet-hat-page-08.png",
    text: "Then I smiled under the stars.",
    audio: "comethat_08.mp3",
  },
];

const ROCKET_PAJAMAS_COVER =
  "data/generated-assets/storybook-rocket-pajamas/rocket-pajamas-cover.png";
const ROCKET_PAJAMAS_PAGE_IMAGES = [
  "data/generated-assets/storybook-rocket-pajamas/rocket-pajamas-page-01.png",
  "data/generated-assets/storybook-rocket-pajamas/rocket-pajamas-page-02.png",
  "data/generated-assets/storybook-rocket-pajamas/rocket-pajamas-page-03.png",
  "data/generated-assets/storybook-rocket-pajamas/rocket-pajamas-page-04.png",
];
const SATURN_RING_COVER =
  "data/generated-assets/storybook-saturn-ring/saturn-ring-cover.png";
const SATURN_RING_PAGE_IMAGES = [
  "data/generated-assets/storybook-saturn-ring/saturn-ring-page-01.png",
  "data/generated-assets/storybook-saturn-ring/saturn-ring-page-02.png",
  "data/generated-assets/storybook-saturn-ring/saturn-ring-page-03.png",
  "data/generated-assets/storybook-saturn-ring/saturn-ring-page-04.png",
];
const BLACK_HOLE_PEEKABOO_COVER =
  "data/generated-assets/storybook-black-hole-peekaboo/black-hole-peekaboo-cover.png";
const BLACK_HOLE_PEEKABOO_PAGE_IMAGES = [
  "data/generated-assets/storybook-black-hole-peekaboo/black-hole-peekaboo-page-01.png",
  "data/generated-assets/storybook-black-hole-peekaboo/black-hole-peekaboo-page-02.png",
  "data/generated-assets/storybook-black-hole-peekaboo/black-hole-peekaboo-page-03.png",
  "data/generated-assets/storybook-black-hole-peekaboo/black-hole-peekaboo-page-04.png",
];

const ROCKET_PAJAMAS_PAGES = [
  {
    image: ROCKET_PAJAMAS_PAGE_IMAGES[0],
    text: "My rocket put on stripey pajamas.",
  },
  {
    image: ROCKET_PAJAMAS_PAGE_IMAGES[1],
    text: "It yawned a tiny puff of fire.",
  },
  {
    image: ROCKET_PAJAMAS_PAGE_IMAGES[2],
    text: "We tucked it under a moon blanket.",
  },
  {
    image: ROCKET_PAJAMAS_PAGE_IMAGES[3],
    text: "Goodnight, rocket. Dream of stars.",
  },
];

const SATURN_RING_PAGES = [
  {
    image: SATURN_RING_PAGE_IMAGES[0],
    text: "Saturn looked around. One ring was missing.",
  },
  {
    image: SATURN_RING_PAGE_IMAGES[1],
    text: "The ring rolled past a sleepy moon.",
  },
  {
    image: SATURN_RING_PAGE_IMAGES[2],
    text: "Adam helped it loop back home.",
  },
  {
    image: SATURN_RING_PAGE_IMAGES[3],
    text: "Saturn spun a happy thank-you dance.",
  },
];

const BLACK_HOLE_PEEKABOO_PAGES = [
  {
    image: BLACK_HOLE_PEEKABOO_PAGE_IMAGES[0],
    text: "A little swirl whispered, peekaboo.",
  },
  {
    image: BLACK_HOLE_PEEKABOO_PAGE_IMAGES[1],
    text: "Stars hid behind its purple curls.",
  },
  {
    image: BLACK_HOLE_PEEKABOO_PAGE_IMAGES[2],
    text: "Adam counted one, two, three lights.",
  },
  {
    image: BLACK_HOLE_PEEKABOO_PAGE_IMAGES[3],
    text: "The swirl smiled and let them shine.",
  },
];

const STORY_NARRATION_DELAY_MS = 1400;

const STORYBOOKS = {
  starHome: {
    title: "The Little Star Home",
    shelfTitle: "Little Star",
    shelfCue: "Help a tiny star get home.",
    shelfIcon: "⭐",
    pages: STAR_HOME_PAGES,
    characterAsset: GENERATED_ASSETS.sonAstronaut,
    imageShape: "star-home",
  },
  moonSeed: {
    title: "The Moon Seed",
    shelfTitle: "Moon Seed",
    shelfCue: "Grow a glowing space flower.",
    shelfIcon: "🌱",
    pages: MOON_SEED_PAGES,
    characterAsset: GENERATED_ASSETS.sonAstronaut,
    imageShape: "moon-seed",
  },
  cometHat: {
    title: "The Comet Hat",
    shelfTitle: "Comet Hat",
    shelfCue: "Wear a silly comet helmet.",
    shelfIcon: "☄",
    pages: COMET_HAT_PAGES,
    characterAsset: GENERATED_ASSETS.sonAstronaut,
    imageShape: "comet-hat",
  },
  rocketPajamas: {
    title: "Rocket Pajamas",
    shelfTitle: "Rocket PJs",
    shelfCue: "Tuck a sleepy rocket into bed.",
    shelfIcon: "🚀",
    coverImage: ROCKET_PAJAMAS_COVER,
    pages: ROCKET_PAJAMAS_PAGES,
    imageShape: "rocket-pajamas",
  },
  saturnRing: {
    title: "Saturn's Lost Ring",
    shelfTitle: "Lost Ring",
    shelfCue: "Roll Saturn's ring back home.",
    shelfIcon: "🪐",
    coverImage: SATURN_RING_COVER,
    pages: SATURN_RING_PAGES,
    imageShape: "saturn-ring",
  },
  blackHolePeekaboo: {
    title: "Black Hole Peekaboo",
    shelfTitle: "Peekaboo",
    shelfCue: "Play gently with a starry swirl.",
    shelfIcon: "🌀",
    coverImage: BLACK_HOLE_PEEKABOO_COVER,
    pages: BLACK_HOLE_PEEKABOO_PAGES,
    imageShape: "black-hole-peekaboo",
  },
};

const STORY_SHELF_ORDER = [
  "starHome",
  "moonSeed",
  "cometHat",
  "rocketPajamas",
  "saturnRing",
  "blackHolePeekaboo",
];

const HELMET_STICKERS = [
  { id: "none", glyph: "", label: "None" },
  {
    id: "alien",
    glyph: "👽",
    label: "Alien ears",
    asset: GENERATED_ASSETS.alien,
  },
  { id: "dino", glyph: "🦖", label: "Dinosaur" },
  { id: "crown", glyph: "👑", label: "Crown" },
  { id: "flower", glyph: "🌸", label: "Flower" },
  {
    id: "star",
    glyph: "⭐",
    label: "Star",
    asset: GENERATED_ASSETS.sparkleStar,
  },
  {
    id: "rocket",
    glyph: "🚀",
    label: "Rocket",
    asset: GENERATED_ASSETS.rocket,
  },
  { id: "heart", glyph: "💛", label: "Heart" },
  { id: "moon", glyph: "🌙", label: "Moon", asset: GENERATED_ASSETS.moon },
  {
    id: "sparkle",
    glyph: "✨",
    label: "Sparkle",
    asset: GENERATED_ASSETS.sparkleStar,
  },
  { id: "smile", glyph: "🙂", label: "Smile" },
];
function useHelmetSticker() {
  const [id, setId] = useState(() => {
    try {
      return localStorage.getItem("planets_helmet") || "none";
    } catch {
      return "none";
    }
  });
  const update = (next) => {
    setId(next);
    try {
      localStorage.setItem("planets_helmet", next);
    } catch {}
  };
  return [id, update];
}

function useStickerPlacement() {
  const [place, setPlace] = useState(() => {
    try {
      return localStorage.getItem("planets_sticker_place") || "helmet";
    } catch {
      return "helmet";
    }
  });
  const update = (next) => {
    setPlace(next);
    try {
      localStorage.setItem("planets_sticker_place", next);
    } catch {}
  };
  return [place, update];
}

const HELMET_SIDES = [
  { id: "front", label: "Front", asset: GENERATED_ASSETS.helmet },
  { id: "right", label: "Right", asset: GENERATED_ASSETS.helmetRight },
  { id: "back", label: "Back", asset: GENERATED_ASSETS.helmetBack },
  { id: "left", label: "Left", asset: GENERATED_ASSETS.helmetLeft },
];

function useHelmetSide(key, fallback = "front") {
  const [side, setSide] = useState(() => {
    try {
      return localStorage.getItem(key) || fallback;
    } catch {
      return fallback;
    }
  });
  const update = (next) => {
    setSide(next);
    try {
      localStorage.setItem(key, next);
    } catch {}
  };
  return [side, update];
}

function useStickerPosition() {
  const [pos, setPos] = useState(() => {
    try {
      const parsed = JSON.parse(
        localStorage.getItem("planets_sticker_pos") || "{}",
      );
      return {
        x: Number.isFinite(parsed.x) ? parsed.x : 50,
        y: Number.isFinite(parsed.y) ? parsed.y : 50,
      };
    } catch {
      return { x: 50, y: 50 };
    }
  });
  const update = (next) => {
    setPos(next);
    try {
      localStorage.setItem("planets_sticker_pos", JSON.stringify(next));
    } catch {}
  };
  return [pos, update];
}

function useHelmetDecorations() {
  const [items, setItems] = useState(() => {
    try {
      const raw = localStorage.getItem("planets_helmet_decorations_v2");
      const parsed = raw ? JSON.parse(raw) : [];
      if (!Array.isArray(parsed)) return [];
      return parsed.filter(
        (item) =>
          item &&
          typeof item.uid === "string" &&
          HELMET_STICKERS.some((s) => s.id === item.stickerId) &&
          HELMET_SIDES.some((s) => s.id === item.side),
      );
    } catch {
      return [];
    }
  });
  const update = (next) => {
    setItems((current) => {
      const resolved = typeof next === "function" ? next(current) : next;
      try {
        localStorage.setItem(
          "planets_helmet_decorations_v2",
          JSON.stringify(resolved),
        );
      } catch {}
      return resolved;
    });
  };
  return [items, update];
}

// ---------- Escape-stack helper ----------
// Round-3 cleanup. Before this, App.jsx had its own window keydown handler
// that called closeInteractiveSurfaces() on Escape, AND every per-level
// component (maze, storybook) also registered its own window keydown
// handler that called onExit() on Escape. Both fired on the same press —
// idempotent in practice but architecturally ambiguous about WHO closes
// what when modals are stacked. This helper lets the deepest registered
// handler win (LIFO), which is the conventional modal-stack contract.
const __escapeStack = [];
let __escapeListenerInstalled = false;
function __ensureEscapeListener() {
  if (__escapeListenerInstalled || typeof window === "undefined") return;
  __escapeListenerInstalled = true;
  window.addEventListener(
    "keydown",
    (e) => {
      if (e.key !== "Escape") return;
      const top = __escapeStack[__escapeStack.length - 1];
      if (!top) return;
      try {
        top();
      } catch (err) {
        // Surface to __spaceErrors for diagnostics if available.
        if (window.__spaceErrors)
          window.__spaceErrors.push({
            message: err && (err.message || String(err)),
          });
      }
    },
    true,
  );
}
function pushEscapeHandler(handler) {
  if (typeof handler !== "function") return () => {};
  __ensureEscapeListener();
  __escapeStack.push(handler);
  let popped = false;
  return () => {
    if (popped) return;
    popped = true;
    const idx = __escapeStack.lastIndexOf(handler);
    if (idx >= 0) __escapeStack.splice(idx, 1);
  };
}
function useEscapeHandler(handler, enabled = true) {
  // Wrap with useEffect so a parent unmount cleans up. The handler is
  // captured fresh on every render via a ref so callers don't have to
  // memoize.
  const ref = useRef(handler);
  useEffect(() => {
    ref.current = handler;
  });
  useEffect(() => {
    if (!enabled) return undefined;
    const dispose = pushEscapeHandler(() => {
      const fn = ref.current;
      if (typeof fn === "function") fn();
    });
    return dispose;
  }, [enabled]);
}

function useWindowKeyHandler(handler, enabled = true) {
  const ref = useRef(handler);
  useEffect(() => {
    ref.current = handler;
  });
  useEffect(() => {
    if (!enabled) return undefined;
    const onKey = (event) => {
      const fn = ref.current;
      if (typeof fn === "function") fn(event);
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [enabled]);
}

// ---------- One-time legacy localStorage migration ----------
// Round-3 cleanup. The helmet-decorations storage was migrated v1 → v2
// at some point in the project's history; the new key is read/written
// but the old "planets_helmet_decorations" key was left orphaned in
// users' localStorage. Same pattern for any future migrations: extend
// the LEGACY_KEYS_TO_REMOVE list and bump the migration marker.
const LEGACY_KEYS_TO_REMOVE = [
  "planets_helmet_decorations", // pre-v2; replaced by planets_helmet_decorations_v2
];
const STORAGE_MIGRATION_KEY = "planets_storage_migration_v1";
function migrateLegacyStorageKeys() {
  try {
    if (typeof localStorage === "undefined") return;
    if (localStorage.getItem(STORAGE_MIGRATION_KEY) === "done") return;
    for (const key of LEGACY_KEYS_TO_REMOVE) {
      try {
        localStorage.removeItem(key);
      } catch {}
    }
    localStorage.setItem(STORAGE_MIGRATION_KEY, "done");
  } catch {}
}
migrateLegacyStorageKeys();

window.SpaceExplorerFoundation = {
  readStorageValue,
  readStorageFlag,
  writeStorageFlag,
  readStorageEnum,
  readStorageInt,
  writeStorageValue,
  removeStorageValue,
  readStorageJson,
  playNarration,
  stopNarration,
  pushEscapeHandler,
  useEscapeHandler,
  useWindowKeyHandler,
  migrateLegacyStorageKeys,
  getGeneratedAsset,
  reportSpaceError,
  reportAssetLoadFailure,
  SafeAssetImage,
};
