import React from "react";
import { createRoot } from "react-dom/client";
import "../style.css";

const STORAGE_KEYS = {
  artifacts: "home_artifacts",
  collectibles: "home_collectibles",
  plants: "home_plants",
  visits: "home_visits",
  notes: "home-memory-notes",
  unlocks: "home-progression-unlocks",
};

const LEGACY_STORAGE_KEYS = {
  artifacts: "home-artifacts",
  collectibles: "home-collectibles",
  plants: "home-garden-plants",
  visits: "home-visit-count",
};

const sceneVideos = {
  arrival: "/media/arrival-cottage.mp4",
  kitchen: "/media/kitchen-late-night.mp4",
  kitchenOriginal: "/media/kitchen-mom.mp4",
  backyardA: "/media/backyard-cat-a.mp4",
  backyardB: "/media/backyard-cat-b.mp4",
};

const fallbackNotes = [
  { title: "Urdu", body: "Tum mehfooz ho. Yeh ghar tumhari awaaz sunta hai." },
  { title: "Ammi", body: "Bring your tiredness inside. We will fold it gently." },
  { title: "Recipe", body: "Tea first. Then decisions. Then maybe more tea." },
  { title: "Tiny Win", body: "You came back. That counts more than you think." },
  { title: "Window", body: "Rain writes on the glass when no one knows what to say." },
];

const radioStations = [
  { id: "rain", icon: "🌧️", name: "Rain", caption: "generated pink-noise rainfall" },
  { id: "fireplace", icon: "🔥", name: "Fireplace", caption: "generated low crackle" },
  { id: "piano", icon: "🎹", name: "Night Piano", caption: "generated slow pentatonic melody" },
  { id: "forest", icon: "🌲", name: "Forest", caption: "generated forest bed and chirps" },
  { id: "lofi", icon: "🎧", name: "LoFi", caption: "generated pulsing warm noise" },
];

const drawerItems = [
  { icon: "🥣", title: "Old Recipe", body: "Soup for nights when the world asks too much." },
  { icon: "🍪", title: "Cookie Recipe", body: "Add extra chocolate when courage feels thin." },
  { icon: "💌", title: "Encouragement Letter", body: "You have survived every hard day so far." },
  { icon: "🗓️", title: "Study Plan", body: "Twenty-five minutes. Tea. Five-minute window break. Repeat." },
  { icon: "🖼️", title: "Childhood Photograph", body: "A small you laughing at a kitchen table." },
  { icon: "📄", title: "Recipe Page", body: "How to begin again: warm water, clean page, tiny step." },
];
const drawerSlips = [
  "Old Recipe: Cardamom chai for difficult days",
  "Cookie Recipe: The one that fixes most things",
  "Encouragement Letter: You were always going to be okay.",
  "Study Plan: One concept. One hour. One cup of tea.",
  "Note: The hard part is already behind you.",
];

const catCollectibles = [
  { icon: "🪶", title: "Feather", unlock: "greenhouse", body: "Found near the wet porch." },
  { icon: "📖", title: "Recipe Page", unlock: "study", body: "A page with flour on the corner." },
  { icon: "🌻", title: "Pressed Flower", unlock: "greenhouse", body: "Still golden after being hidden." },
  { icon: "🗝", title: "Small Key", unlock: "attic", body: "It fits something upstairs." },
  { icon: "📜", title: "Old Letter", unlock: "attic", body: "Folded twice and kept safe." },
  { icon: "🎀", title: "Lost Ribbon", unlock: "study", body: "A bookmark from another season." },
];

const reflectionQuestions = [
  "What are you proud of today?",
  "What do you wish your future self knew?",
  "What are you ready to let go of?",
  "What made today worth remembering?",
  "What small thing deserves more credit?",
];

const momWelcomeMessage = `Welcome home.

The kettle is warm and I'm listening.

Tell me what happened today, what you're worried about, what you're proud of, or what you're trying to learn.

The house will remember what matters.`;

const momPromptChips = [
  { icon: "☕", label: "I had a difficult day", text: "I had a difficult day today." },
  { icon: "🌻", label: "I achieved something today", text: "I achieved something today." },
  { icon: "📚", label: "Help me understand a topic", text: "Help me understand " },
  { icon: "📝", label: "I want to remember something", text: "I want to remember " },
  { icon: "🎯", label: "I have a goal I'm working on", text: "I have a goal I'm working on: " },
];

const kitchenRoomHints = [
  { icon: "🍵", label: "Kitchen", text: "talk with Mom" },
  { icon: "💌", label: "Hallway", text: "keeps memories" },
  { icon: "🌱", label: "Backyard", text: "grows goals" },
  { icon: "🗝️", label: "Attic", text: "opens discoveries" },
  { icon: "🐈", label: "Cat", text: "finds hidden things" },
];

const objectMap = {
  candle: { icon: "🎟️", title: "Recovery Recipe Card", line: "Rest, one honest review, one kinder retry." },
  sunflower: { icon: "🌻", title: "Celebration Card", line: "The house saves the date in bright yellow ink." },
  lesson: { icon: "📚", title: "Handwritten Lesson Page", line: "Mom turns the question into a page you can keep." },
  compass: { icon: "🧭", title: "Next-Step Map", line: "Confusion becomes a path with one visible step." },
  meal: { icon: "🍚", title: "Kitchen Meal Card", line: "A small warm meal appears on the kitchen table." },
  tea: { icon: "☕", title: "Comfort Tea Note", line: "A warm note appears where worry was sitting." },
};

const plantStages = [
  { name: "Seed", icon: "🌱" },
  { name: "Sprout", icon: "🌿" },
  { name: "Flower", icon: "🌸" },
  { name: "Tree", icon: "🌳" },
];

const itemStories = {
  "Small Key": "The little key is not a trophy. It opens the locked trunk upstairs, and the trunk changes the hallway.",
  "Old Letter": "The old letter remembers someone being proud before anyone knew how the story would end.",
  "Recipe Page": "This flour-dusted page belongs in the kitchen recipe book. It can create a table recipe.",
  "Pressed Flower": "The pressed flower can be planted in the greenhouse as proof that soft things can survive pressure.",
  Feather: "The feather marks where the cat has walked. It unlocks a greenhouse clue.",
  "Lost Ribbon": "The ribbon becomes a bookmark in the study, saving one useful page for later.",
};

function readStorage(key, fallback) {
  try {
    const parsed = JSON.parse(localStorage.getItem(key) || "null");
    if (parsed !== null) return parsed;
    const legacyKey = Object.values(LEGACY_STORAGE_KEYS).find((legacy) => {
      if (key === STORAGE_KEYS.artifacts) return legacy === LEGACY_STORAGE_KEYS.artifacts;
      if (key === STORAGE_KEYS.collectibles) return legacy === LEGACY_STORAGE_KEYS.collectibles;
      if (key === STORAGE_KEYS.plants) return legacy === LEGACY_STORAGE_KEYS.plants;
      return false;
    });
    if (legacyKey) {
      const legacyParsed = JSON.parse(localStorage.getItem(legacyKey) || "null");
      if (legacyParsed !== null) {
        localStorage.setItem(key, JSON.stringify(legacyParsed));
        return legacyParsed;
      }
    }
    return fallback;
  } catch {
    return fallback;
  }
}

function writeStorage(key, value) {
  localStorage.setItem(key, JSON.stringify(value));
}

function dayOfYear(date = new Date()) {
  const start = new Date(date.getFullYear(), 0, 0);
  return Math.floor((date - start) / 86400000);
}

function classifyMessage(text) {
  const lower = text.toLowerCase();
  if (/^(hi|hello|hey|salam|assalamualaikum|assalamu alaikum|mom|mama|ammi)\b/.test(lower)) return "greeting";
  if (lower.length < 2) return "unclear";
  if (/(recursion|algorithm|explain|teach|lesson|learn|study|code|python|math|operating system|os\b|scheduling|data structure|linked list|stack|queue|tree|graph)/.test(lower)) return "lesson";
  if (/(hungry|rice|food|eat|dinner|lunch|breakfast|roti|biryani|meal|cook|khana|bhook)/.test(lower)) return "meal";
  if (/(selected|intern|offer|won|win|passed|success|achieved|achievement|got selected)/.test(lower)) return "sunflower";
  if (/(confused|lost|don't know|dont know|direction|stuck)/.test(lower)) return "compass";
  if (/(failed|fail|exam|sad|cry|broken|hard day)/.test(lower)) return "candle";
  if (/(overwhelmed|tired|worked hard|anxious|scared|afraid|panic|exhausted)/.test(lower)) return "tea";
  return "tea";
}

function artifactFromMessage(message, kind) {
  const base = objectMap[kind] || objectMap.tea;
  const hallwayMap = {
    candle: { icon: "🎟️", title: "Recovery Recipe Card", description: "The house remembered a hard moment and made a way back." },
    sunflower: { icon: "💌", title: "Acceptance Letter", description: "A bright milestone pinned where future-you can see it." },
    lesson: { icon: "📓", title: /recursion/i.test(message) ? "Algorithm Notebook" : "Handwritten Lesson Page", description: "A question became something you can revisit." },
    compass: { icon: "🧭", title: "Next-Step Map", description: "The hallway kept the first direction you found." },
    meal: { icon: "🍚", title: "Kitchen Meal Card", description: "The kitchen remembered that care can be practical: something warm to eat." },
    tea: { icon: "☕", title: "Tea-Stained Note", description: "A tired day was not thrown away. It was cared for." },
  };
  const hall = hallwayMap[kind] || hallwayMap.tea;
  return {
    id: `${Date.now()}-${Math.random().toString(16).slice(2)}`,
    kind,
    icon: hall.icon,
    tableIcon: base.icon,
    title: hall.title,
    tableTitle: base.title,
    description: hall.description,
    tableLine: base.line,
    source: message.slice(0, 150),
    date: new Date().toLocaleDateString(undefined, { month: "short", day: "numeric", year: "numeric" }),
  };
}

function requestedArtifactFromMessage(message) {
  const lower = message.toLowerCase();
  let artifact = { icon: "🕯️", title: "A Small Note", description: "Left on the table while the candle was still lit.", kind: "note" };
  if (/(failed|sad|broken)/.test(lower)) {
    artifact = { icon: "📖", title: "Recovery Note", description: "A page torn gently from Mom's notebook.", kind: "candle" };
  } else if (/(selected|won|internship|intern)/.test(lower)) {
    artifact = { icon: "🌻", title: "Celebration Card", description: "The kitchen smelled like sunflowers that day.", kind: "sunflower" };
  } else if (/(explain|learn|study|recursion)/.test(lower)) {
    artifact = { icon: "📚", title: "Lesson Page", description: "Written in careful handwriting, twice underlined.", kind: "lesson" };
  } else if (/(tired|overwhelmed|scared)/.test(lower)) {
    artifact = { icon: "☕", title: "Comfort Tea Note", description: "Steep for 3 minutes. Breathe for 3 more.", kind: "tea" };
  }
  return {
    id: Date.now(),
    type: "artifact",
    kind: artifact.kind,
    icon: artifact.icon,
    tableIcon: artifact.icon,
    title: artifact.title,
    tableTitle: artifact.title,
    date: new Date().toLocaleDateString(),
    description: artifact.description,
    tableLine: artifact.description,
    source: message.slice(0, 150),
  };
}

function makeArtifact(kind, source, overrides = {}) {
  const base = objectMap[kind] || objectMap.tea;
  return {
    id: `${Date.now()}-${Math.random().toString(16).slice(2)}`,
    kind,
    icon: overrides.icon || base.icon,
    tableIcon: overrides.tableIcon || overrides.icon || base.icon,
    title: overrides.title || base.title,
    tableTitle: overrides.tableTitle || overrides.title || base.title,
    description: overrides.description || base.line,
    tableLine: overrides.tableLine || overrides.description || base.line,
    source,
    date: new Date().toLocaleDateString(undefined, { month: "short", day: "numeric", year: "numeric" }),
  };
}

const CORRUPT_GLYPH_CODES = new Set([0x00f0, 0x00c3, 0x00e2, 0x00d8, 0x00d9, 0x00db, 0xfffd]);

function isCorruptGlyph(value) {
  return !value || Array.from(String(value)).some((char) => CORRUPT_GLYPH_CODES.has(char.charCodeAt(0)));
}

function inferArtifactIcon(artifact, forTable = false) {
  const title = forTable ? artifact.tableTitle || artifact.title : artifact.title || artifact.tableTitle;
  const text = `${title || ""} ${artifact.kind || ""} ${artifact.description || ""} ${artifact.tableLine || ""}`.toLowerCase();
  if (/feather/.test(text)) return "🪶";
  if (/pressed flower|celebration|acceptance|sunflower/.test(text)) return "🌻";
  if (/small key|key/.test(text)) return "🗝";
  if (/old letter|letter/.test(text)) return "📜";
  if (/lost ribbon|ribbon/.test(text)) return "🎀";
  if (/cookie/.test(text)) return "🍪";
  if (/drawer|note/.test(text) && !/tea/.test(text)) return "📝";
  if (/meal|rice|food/.test(text)) return "🍚";
  if (/tea|comfort|steaming/.test(text)) return forTable ? "🍵" : "☕";
  if (/lesson|study|recursion|algorithm|notebook/.test(text)) return "📚";
  if (/recipe|recovery|page|book/.test(text)) return "📖";
  if (/candle|small note/.test(text)) return "🕯️";
  if (/compass|map|confusion|direction/.test(text)) return "🧭";
  return "📌";
}

function normalizeArtifact(artifact, index = 0) {
  const safeArtifact = artifact && typeof artifact === "object" ? artifact : {};
  const title = safeArtifact.title || safeArtifact.tableTitle || "House Memory";
  const description = safeArtifact.description || safeArtifact.tableLine || safeArtifact.body || safeArtifact.source || "The house kept this memory.";
  const icon = isCorruptGlyph(safeArtifact.icon) ? inferArtifactIcon({ ...safeArtifact, title, description }) : safeArtifact.icon;
  const tableIcon = isCorruptGlyph(safeArtifact.tableIcon)
    ? inferArtifactIcon({ ...safeArtifact, title, description }, true)
    : safeArtifact.tableIcon;
  return {
    ...safeArtifact,
    id: safeArtifact.id || `${Date.now()}-${index}`,
    icon,
    tableIcon: tableIcon || icon,
    title,
    tableTitle: safeArtifact.tableTitle || title,
    date: safeArtifact.date || new Date().toLocaleDateString(),
    description,
    tableLine: safeArtifact.tableLine || description,
  };
}

function dedupeArtifactsByTitleDate(list) {
  const seen = new Set();
  return list.filter((item) => {
    const key = `${item.title || ""}-${item.date || ""}`;
    if (seen.has(key)) return false;
    seen.add(key);
    return true;
  });
}

function readArtifacts() {
  try {
    const saved = JSON.parse(localStorage.getItem("home_artifacts") || "[]");
    if (Array.isArray(saved) && saved.length) {
      const cleaned = dedupeArtifactsByTitleDate(saved.map(normalizeArtifact));
      if (cleaned.length !== saved.length) {
        writeStorage(STORAGE_KEYS.artifacts, cleaned);
      }
      return cleaned;
    }
  } catch {
    // Fall back to the shared migration path below.
  }
  return dedupeArtifactsByTitleDate(readStorage(STORAGE_KEYS.artifacts, []).map(normalizeArtifact));
}

function plantFromReflection(text) {
  const lower = text.toLowerCase();
  if (/(achieved|goal|finished|selected|passed|won)/.test(lower)) return { title: "Memory Tree", body: text };
  if (/(presentation|shared|brave|proud|confidence)/.test(lower)) return { title: "Courage Flower", body: text };
  if (/(let go|release|forgive|rest)/.test(lower)) return { title: "Letting-Go Leaf", body: text };
  return { title: "Small Seed", body: text };
}

function normalizePlant(plant) {
  const stageIndex = Math.max(0, Math.min(3, Number.isFinite(Number(plant.stage)) ? Number(plant.stage) : Number(plant.growth || 0)));
  const stage = plantStages[stageIndex];
  return {
    ...plant,
    text: plant.text || plant.body || "",
    body: plant.body || plant.text || "",
    growth: stageIndex,
    stage: stageIndex,
    stageName: stage.name,
    icon: stage.icon,
    history: plant.history || [`Started as ${stage.name.toLowerCase()}.`],
  };
}

function growPlant(plant, reason) {
  const nextGrowth = Math.min(3, Number(plant.stage ?? plant.growth ?? 0) + 1);
  const stage = plantStages[nextGrowth];
  return {
    ...plant,
    growth: nextGrowth,
    stage: nextGrowth,
    stageName: stage.name,
    icon: stage.icon,
    history: [...(plant.history || []), `${stage.name}: ${reason}`].slice(-8),
  };
}

function houseContextLine(context) {
  const pieces = [];
  if (context?.lastExamMemory) pieces.push("Last time, the hallway kept your exam worry safe.");
  if (context?.confidencePlant) pieces.push(`Your ${context.confidencePlant.title} is now a ${context.confidencePlant.stageName || plantStages[context.confidencePlant.stage]?.name || "new stage"}.`);
  if (context?.lastCollectible) pieces.push(`The cat recently brought ${context.lastCollectible.title}.`);
  if (context?.lastLesson) pieces.push(`Your study room saved ${context.lastLesson.title}.`);
  return pieces.length ? `${pieces[0]} ` : "";
}

function lessonFallback(text) {
  const lower = text.toLowerCase();
  if (/scheduling|operating system|os\b/.test(lower)) {
    return "Come sit beside me. I made a scheduling note: an operating system chooses which process gets the CPU next, a little like deciding which pot on the stove needs attention first.";
  }
  if (/data structure|stack|queue|linked list|tree|graph/.test(lower)) {
    return "Come sit beside me. I made a data structures note: choose the container that matches the job. A stack remembers last-in first-out, a queue remembers first-in first-out, and a tree keeps related choices branching neatly.";
  }
  return "Come sit beside me. I made a handwritten lesson page. Recursion is when a problem calls a smaller version of itself until it reaches a simple stopping point.";
}

function localMomReply(text, kind, context = {}) {
  const prefix = houseContextLine(context);
  const replies = {
    greeting:
      `${prefix}Hi jaan. I am here. Tell me what you need: comfort, food, study help, or just someone to sit with you.`,
    unclear:
      "I am listening, honey, but I only caught a tiny piece of that. Say a little more and I will understand.",
    candle:
      "Bring your bags inside, honey. I made a recovery recipe card for the table. A failed exam is a moment, not your whole story.",
    sunflower:
      "Oh, my bright one. I put a celebration card on the table. Tell me the little details so the house can remember them properly.",
    lesson: lessonFallback(text),
    compass:
      "Confused is not lost forever. I placed a next-step map on the table. We only need one honest step at a time.",
    meal:
      "Hungry? Come to the kitchen. I can make rice warm and soft first, then we can add something simple beside it. Sit down, drink water, and tell me if you want plain rice, spicy rice, or comfort food.",
    tea:
      "You sound full to the brim. I made a comfort tea note. Let the room be quiet around you for a minute.",
  };
  return { object: kind, reply: replies[kind] || `${prefix}${replies.tea}` };
}

function getMomFallback(text) {
  const t = text.toLowerCase();
  if (/(fail|exam|sad|broken|cry)/.test(t))
    return "Sit down, beta. A hard day is not a lost one. The tea is warm and so is this chair.";
  if (/(selected|internship|won|passed|got)/.test(t))
    return "Oh, my heart. I knew this was coming. Tell me everything - even the small parts.";
  if (/(confused|stuck|lost|direction)/.test(t))
    return "Confused means you are still thinking. That is not lost. That is searching.";
  if (/(scared|afraid|anxious|panic)/.test(t))
    return "Come closer to the light. Fear gets smaller near warm things. You are safe here.";
  if (/(tired|exhausted|overwhelmed)/.test(t))
    return "You have done enough for today. The house will still be here when you wake up rested.";
  return "The house heard you. Whatever you are carrying, you can set it down here for a moment.";
}

async function askMom(userMessage, history = [], houseState = {}) {
  try {
    const response = await fetch("/api/chat", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        message: userMessage,
        history,
        house_state: houseState,
      }),
    });
    if (!response.ok) throw new Error(`Mom API returned ${response.status}`);
    const data = await response.json();
    if (data.reply) {
      return {
        reply: data.reply.trim(),
        source: data.source || "api",
        model: data.model || "unknown",
      };
    }
  } catch (e) {
    console.log("Mom API error, using local fallback", e);
  }
  return {
    reply: getMomFallback(userMessage),
    source: "local",
    model: "local-mom-fallback",
  };
}

function makeAudio() {
  const AudioContext = window.AudioContext || window.webkitAudioContext;
  return AudioContext ? new AudioContext() : null;
}

function playDoorSound() {
  const audio = makeAudio();
  if (!audio) return;
  const gain = audio.createGain();
  const low = audio.createOscillator();
  const high = audio.createOscillator();
  low.type = "sawtooth";
  high.type = "triangle";
  low.frequency.setValueAtTime(95, audio.currentTime);
  low.frequency.exponentialRampToValueAtTime(48, audio.currentTime + 0.62);
  high.frequency.setValueAtTime(380, audio.currentTime);
  high.frequency.exponentialRampToValueAtTime(150, audio.currentTime + 0.38);
  gain.gain.setValueAtTime(0.0001, audio.currentTime);
  gain.gain.exponentialRampToValueAtTime(0.045, audio.currentTime + 0.04);
  gain.gain.exponentialRampToValueAtTime(0.0001, audio.currentTime + 0.68);
  low.connect(gain);
  high.connect(gain);
  gain.connect(audio.destination);
  low.start();
  high.start();
  low.stop(audio.currentTime + 0.7);
  high.stop(audio.currentTime + 0.5);
}

function playMeow() {
  const audio = makeAudio();
  if (!audio) return;
  const osc = audio.createOscillator();
  const gain = audio.createGain();
  osc.type = "sine";
  osc.frequency.setValueAtTime(640, audio.currentTime);
  osc.frequency.exponentialRampToValueAtTime(330, audio.currentTime + 0.22);
  gain.gain.setValueAtTime(0.0001, audio.currentTime);
  gain.gain.exponentialRampToValueAtTime(0.075, audio.currentTime + 0.03);
  gain.gain.exponentialRampToValueAtTime(0.0001, audio.currentTime + 0.34);
  osc.connect(gain);
  gain.connect(audio.destination);
  osc.start();
  osc.stop(audio.currentTime + 0.36);
}

const radioEngineRef = { current: null };

function noiseBuffer(context, seconds = 2) {
  const buffer = context.createBuffer(1, context.sampleRate * seconds, context.sampleRate);
  const data = buffer.getChannelData(0);
  let pink = 0;
  for (let i = 0; i < data.length; i += 1) {
    const white = Math.random() * 2 - 1;
    pink = 0.96 * pink + 0.04 * white;
    data[i] = pink * 0.9;
  }
  return buffer;
}

function clearRadioNodes(engine) {
  engine.timers.forEach((timer) => window.clearInterval(timer));
  engine.timers = [];
  engine.nodes.forEach((node) => {
    try {
      if (node.stop) node.stop();
    } catch {
      // Already stopped.
    }
    try {
      node.disconnect();
    } catch {
      // Already disconnected.
    }
  });
  engine.nodes = [];
}

function getRadioEngine() {
  if (radioEngineRef.current) return radioEngineRef.current;
  const AudioContext = window.AudioContext || window.webkitAudioContext;
  if (!AudioContext) return null;
  const context = new AudioContext();
  const master = context.createGain();
  master.gain.value = 0.7;
  master.connect(context.destination);
  radioEngineRef.current = { context, master, nodes: [], timers: [], station: null };
  return radioEngineRef.current;
}

function connectLoopingNoise(engine, filterType, frequency, q = 0.7, gainValue = 0.18) {
  const { context, master } = engine;
  const source = context.createBufferSource();
  source.buffer = noiseBuffer(context, 3);
  source.loop = true;
  const filter = context.createBiquadFilter();
  filter.type = filterType;
  filter.frequency.value = frequency;
  filter.Q.value = q;
  const gain = context.createGain();
  gain.gain.value = gainValue;
  source.connect(filter);
  filter.connect(gain);
  gain.connect(master);
  source.start();
  engine.nodes.push(source, filter, gain);
  return { source, filter, gain };
}

function playTone(engine, frequency, start, duration, gainValue = 0.06, type = "sine") {
  const { context, master } = engine;
  const osc = context.createOscillator();
  const gain = context.createGain();
  osc.type = type;
  osc.frequency.setValueAtTime(frequency, start);
  gain.gain.setValueAtTime(0.0001, start);
  gain.gain.linearRampToValueAtTime(gainValue, start + 0.04);
  gain.gain.exponentialRampToValueAtTime(0.0001, start + duration);
  osc.connect(gain);
  gain.connect(master);
  osc.start(start);
  osc.stop(start + duration + 0.04);
  engine.nodes.push(osc, gain);
}

async function startAmbientStation(stationId, volume) {
  const engine = getRadioEngine();
  if (!engine) throw new Error("Web Audio API unavailable");
  if (engine.context.state === "suspended") await engine.context.resume();
  clearRadioNodes(engine);
  engine.station = stationId;
  engine.master.gain.value = volume;

  if (stationId === "rain") {
    connectLoopingNoise(engine, "lowpass", 1400, 0.45, 0.4);
    connectLoopingNoise(engine, "highpass", 1800, 0.2, 0.08);
  }

  if (stationId === "fireplace") {
    connectLoopingNoise(engine, "lowpass", 520, 0.9, 0.35);
    const crackle = () => {
      for (let i = 0; i < 4; i += 1) {
        const when = engine.context.currentTime + Math.random() * 0.8;
        playTone(engine, 140 + Math.random() * 900, when, 0.035 + Math.random() * 0.06, 0.08, "triangle");
      }
    };
    crackle();
    engine.timers.push(window.setInterval(crackle, 720));
  }

  if (stationId === "piano") {
    const notes = [261.63, 293.66, 329.63, 392.0, 440.0, 392.0, 329.63, 293.66];
    let index = 0;
    const melody = () => {
      playTone(engine, notes[index % notes.length], engine.context.currentTime, 1.15, 0.3, "sine");
      playTone(engine, notes[(index + 2) % notes.length] / 2, engine.context.currentTime, 1.6, 0.12, "sine");
      index += 1;
    };
    melody();
    engine.timers.push(window.setInterval(melody, 1450));
  }

  if (stationId === "forest") {
    connectLoopingNoise(engine, "bandpass", 760, 0.65, 0.35);
    const chirp = () => {
      const now = engine.context.currentTime;
      playTone(engine, 1600 + Math.random() * 1200, now, 0.12, 0.07, "sine");
      playTone(engine, 2100 + Math.random() * 900, now + 0.14, 0.1, 0.05, "sine");
    };
    engine.timers.push(window.setInterval(chirp, 2600 + Math.random() * 1800));
  }

  if (stationId === "lofi") {
    const bed = connectLoopingNoise(engine, "bandpass", 620, 0.5, 0.3);
    const pulse = () => {
      const now = engine.context.currentTime;
      bed.gain.gain.cancelScheduledValues(now);
      bed.gain.gain.setValueAtTime(0.18, now);
      bed.gain.gain.linearRampToValueAtTime(0.35, now + 0.08);
      bed.gain.gain.exponentialRampToValueAtTime(0.18, now + 0.45);
      playTone(engine, 98, now, 0.18, 0.12, "sine");
    };
    pulse();
    engine.timers.push(window.setInterval(pulse, 850));
  }
}

function stopAmbientRadio() {
  const engine = radioEngineRef.current;
  if (!engine) return;
  clearRadioNodes(engine);
  engine.station = null;
}

function setAmbientRadioVolume(volume) {
  const engine = radioEngineRef.current;
  if (engine) engine.master.gain.value = volume;
}

function App() {
  const [scene, setScene] = React.useState("arrival");
  const [transitioning, setTransitioning] = React.useState(false);
  const [notes, setNotes] = React.useState(fallbackNotes);
  const [selectedMemory, setSelectedMemory] = React.useState(null);
  const [selectedNote, setSelectedNote] = React.useState(fallbackNotes[0]);
  const [noteDraft, setNoteDraft] = React.useState("");
  const [artifacts, setArtifacts] = React.useState(() => readArtifacts());
  const [collectibles, setCollectibles] = React.useState(() => readStorage(STORAGE_KEYS.collectibles, []));
  const [plants, setPlants] = React.useState(() => readStorage(STORAGE_KEYS.plants, []).map(normalizePlant));
  const [progression, setProgression] = React.useState(() => readStorage(STORAGE_KEYS.unlocks, {
    trunkOpen: false,
    atticLetterRevealed: false,
    kitchenRecipeUnlocked: false,
    hiddenMemoryUnlocked: false,
    studyBookmarkUnlocked: false,
  }));
  const [visits, setVisits] = React.useState(() => Number(localStorage.getItem(STORAGE_KEYS.visits) || localStorage.getItem(LEGACY_STORAGE_KEYS.visits) || "0"));
  const [tableArtifacts, setTableArtifacts] = React.useState(() => readArtifacts().slice(-5));
  const [drawerItem, setDrawerItem] = React.useState(null);
  const [catState, setCatState] = React.useState("sleeping");
  const [catBubble, setCatBubble] = React.useState("");
  const [momInput, setMomInput] = React.useState("");
  const [messages, setMessages] = React.useState([
    { role: "mom", text: momWelcomeMessage },
  ]);
  const [momDraft, setMomDraft] = React.useState("");
  const [momThinking, setMomThinking] = React.useState(false);
  const [chatOpen, setChatOpen] = React.useState(true);
  const [apiStatus, setApiStatus] = React.useState({ state: "checking", label: "Checking Mom API..." });
  const [radioStation, setRadioStation] = React.useState(0);
  const [radioPlaying, setRadioPlaying] = React.useState(false);
  const [radioVolume, setRadioVolume] = React.useState(0.7);
  const [backyardVideo, setBackyardVideo] = React.useState(sceneVideos.backyardA);
  const [benchOpen, setBenchOpen] = React.useState(true);
  const [reflection, setReflection] = React.useState("");
  const [questionIndex] = React.useState(() => dayOfYear() % reflectionQuestions.length);
  const [sceneNote, setSceneNote] = React.useState("");
  const [parallax, setParallax] = React.useState({ x: 0, y: 0 });
  const streamRef = React.useRef(null);
  const momThreadRef = React.useRef(null);

  const unlocks = React.useMemo(
    () => ({
      attic: progression.trunkOpen || collectibles.some((item) => item.title === "Small Key") || visits >= 3,
      study: progression.studyBookmarkUnlocked || artifacts.length >= 3 || collectibles.some((item) => item.unlock === "study"),
      greenhouse: plants.length >= 3 || collectibles.some((item) => item.unlock === "greenhouse") || progression.hiddenMemoryUnlocked,
    }),
    [artifacts.length, collectibles, plants.length, progression, visits]
  );

  React.useEffect(() => {
    const nextVisits = Number(localStorage.getItem(STORAGE_KEYS.visits) || localStorage.getItem(LEGACY_STORAGE_KEYS.visits) || visits || "0") + 1;
    setVisits(nextVisits);
    localStorage.setItem(STORAGE_KEYS.visits, String(nextVisits));
    const todayKey = String(dayOfYear());
    const lastGrowthDay = localStorage.getItem("home_plants_last_growth_day");
    if (lastGrowthDay !== todayKey) {
      const storedPlants = readStorage(STORAGE_KEYS.plants, []).map(normalizePlant);
      if (storedPlants.length) {
        const grownPlants = storedPlants.map((plant) => growPlant(plant, "A new day opened Home."));
        setPlants(grownPlants);
        writeStorage(STORAGE_KEYS.plants, grownPlants);
      }
      localStorage.setItem("home_plants_last_growth_day", todayKey);
    }
    async function loadApiStatus() {
      try {
        const response = await fetch("/api/status");
        if (!response.ok) throw new Error("status unavailable");
        const data = await response.json();
        setApiStatus({
          state: data.groq_ready ? "online" : "local",
          label: data.groq_ready ? "Mom is listening" : "Mom is nearby",
        });
      } catch {
        setApiStatus({ state: "offline", label: "Mom is nearby" });
      }
    }
    async function loadNotes() {
      const stored = readStorage(STORAGE_KEYS.notes, []);
      try {
        const response = await fetch("/api/notes");
        if (!response.ok) throw new Error("No notes endpoint");
        const data = await response.json();
        const loaded = data.notes?.length ? data.notes : stored;
        if (loaded.length) {
          const next = [...fallbackNotes, ...loaded].slice(-12);
          setNotes(next);
          setSelectedNote(next[0]);
        }
      } catch {
        if (stored.length) {
          const next = [...fallbackNotes, ...stored].slice(-12);
          setNotes(next);
          setSelectedNote(next[0]);
        }
      }
    }
    loadApiStatus();
    loadNotes();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  React.useEffect(() => {
    if (momThreadRef.current && chatOpen) {
      momThreadRef.current.scrollTop = momThreadRef.current.scrollHeight;
    }
  }, [messages, momDraft, momThinking, chatOpen]);

  React.useEffect(() => {
    if (scene !== "memory") return;
    const cleaned = dedupeArtifactsByTitleDate(artifacts);
    if (cleaned.length !== artifacts.length) {
      persistArtifacts(cleaned);
      setSelectedMemory(cleaned.at(-1) || null);
    }
  }, [scene, artifacts]);

  React.useEffect(() => {
    let timeout;
    function scheduleCat() {
      timeout = window.setTimeout(() => {
        const states = ["sleeping", "walking", "sitting", "looking"];
        setCatState(states[Math.floor(Math.random() * states.length)]);
        scheduleCat();
      }, 30000 + Math.random() * 30000);
    }
    scheduleCat();
    return () => window.clearTimeout(timeout);
  }, []);

  React.useEffect(() => () => {
    window.clearInterval(streamRef.current);
    stopRadio();
  }, []);

  React.useEffect(() => {
    setAmbientRadioVolume(radioVolume);
  }, [radioVolume]);

  function persistArtifacts(next) {
    const normalized = dedupeArtifactsByTitleDate(next.map(normalizeArtifact));
    setArtifacts(normalized);
    setTableArtifacts(normalized.slice(-5));
    writeStorage(STORAGE_KEYS.artifacts, normalized);
  }

  function persistCollectibles(next) {
    setCollectibles(next);
    writeStorage(STORAGE_KEYS.collectibles, next);
  }

  function persistPlants(next) {
    const normalized = next.map(normalizePlant);
    setPlants(normalized);
    writeStorage(STORAGE_KEYS.plants, normalized);
  }

  function persistProgression(next) {
    setProgression(next);
    writeStorage(STORAGE_KEYS.unlocks, next);
  }

  function rememberArtifact(artifact, toast = "The house placed something on the table.") {
    const nextArtifacts = [...artifacts, artifact].slice(-30);
    persistArtifacts(nextArtifacts);
    setSelectedMemory(artifact);
    setSceneNote(toast);
  }

  function buildHouseState() {
    const examMemory = [...artifacts].reverse().find((item) => /exam|fail|recovery/i.test(`${item.title} ${item.source} ${item.description}`));
    const confidencePlant = [...plants].reverse().find((plant) => /confidence|courage|presentation|proud/i.test(`${plant.title} ${plant.body}`));
    const lastLesson = [...artifacts].reverse().find((item) => item.kind === "lesson");
    return {
      memory_count: artifacts.length,
      plant_count: plants.length,
      collectible_count: collectibles.length,
      last_memory: artifacts.at(-1)?.title || "",
      last_collectible: collectibles.at(-1)?.title || "",
      last_plant: plants.at(-1)?.title || "",
      lastExamMemory: examMemory,
      confidencePlant,
      lastCollectible: collectibles.at(-1),
      lastLesson,
      progression,
    };
  }

  function createHiddenMemory(title, description, source, kind = "attic") {
    const artifact = makeArtifact(kind, source, {
      icon: "📜",
      tableIcon: "📜",
      title,
      tableTitle: title,
      description,
      tableLine: description,
    });
    rememberArtifact(artifact, `${title} joined the hallway.`);
    return artifact;
  }

  function unlockKitchenRecipe(source = "The recipe page clicked into the counter book.") {
    persistProgression({ ...progression, kitchenRecipeUnlocked: true });
    const artifact = makeArtifact("meal", source, {
      icon: "📖",
      tableIcon: "🍚",
      title: "Unlocked Kitchen Recipe",
      tableTitle: "Warm Rice Recipe",
      description: "The recipe book now knows: rice, water, salt, patience, and one small kindness.",
      tableLine: "Warm Rice Recipe unlocked from an attic page.",
    });
    rememberArtifact(artifact, "The recipe page unlocked a kitchen recipe.");
    travel("kitchen", "The recipe page fluttered down to the kitchen.");
  }

  function revealOldLetter(source = "Opened the attic trunk with the small key.") {
    const alreadyHasLetter = collectibles.some((item) => item.title === "Old Letter");
    const nextProgression = { ...progression, trunkOpen: true, atticLetterRevealed: true };
    persistProgression(nextProgression);
    if (!alreadyHasLetter) {
      const letter = {
        ...catCollectibles.find((item) => item.title === "Old Letter"),
        id: `${Date.now()}-attic-letter`,
        date: new Date().toLocaleDateString(undefined, { month: "short", day: "numeric" }),
      };
      persistCollectibles([...collectibles, letter].slice(-24));
    }
    createHiddenMemory(
      "Old Letter Memory",
      "The trunk revealed a letter about being loved before you had proof you would succeed.",
      source
    );
  }

  function inspectCollectible(item, location = "shelf") {
    if (!item) return;
    setSelectedMemory(null);
    const title = item.title;
    if (location === "hallway") {
      setSelectedMemory({
        id: item.id || `${title}-collectible`,
        kind: "collectible",
        icon: item.icon,
        title,
        date: item.date || "Found by the cat",
        description: itemStories[title] || item.body || "The cat brought this home because it matters.",
        source: "Cat discovery",
        relatedRoom: title === "Recipe Page" ? "Kitchen" : title === "Pressed Flower" ? "Backyard garden" : "House Memory Hallway",
      });
      return;
    }
    if (title === "Small Key") {
      if (!progression.trunkOpen) {
        revealOldLetter("Small Key opened the locked attic trunk.");
        setSceneNote("The Small Key opened the locked trunk and revealed an Old Letter.");
      } else {
        setSceneNote("The Small Key already opened the trunk. The attic remembers the turn of the lock.");
      }
      travel("attic", "The key pulls your attention upstairs.");
      return;
    }
    if (title === "Old Letter") {
      persistProgression({ ...progression, hiddenMemoryUnlocked: true, atticLetterRevealed: true });
      createHiddenMemory(
        "Letter From Before",
        "The old letter became a hallway memory: someone believed in you before the result arrived.",
        item.body
      );
      travel("memory", "The Old Letter pinned itself to the hallway.");
      return;
    }
    if (title === "Recipe Page") {
      unlockKitchenRecipe(item.body);
      return;
    }
    if (title === "Pressed Flower" || title === "Dried Flower") {
      const flower = normalizePlant({
        id: `${Date.now()}-pressed-flower`,
        title: "Pressed Flower",
        body: "Planted from the cat's discovery.",
        growth: 2,
        date: new Date().toLocaleDateString(undefined, { month: "short", day: "numeric" }),
        history: ["Found by the cat.", "Flower: planted in the greenhouse."],
      });
      persistPlants([...plants, flower].slice(-24));
      persistProgression({ ...progression, hiddenMemoryUnlocked: true });
      setSceneNote("The pressed flower became a greenhouse bloom.");
      travel("greenhouse", "The pressed flower takes root.");
      return;
    }
    if (title === "Lost Ribbon") {
      persistProgression({ ...progression, studyBookmarkUnlocked: true });
      const artifact = makeArtifact("lesson", item.body, {
        icon: "🎀",
        tableIcon: "📚",
        title: "Ribbon Bookmark",
        tableTitle: "Saved Study Page",
        description: "The ribbon marked a study page so the lesson can be found again.",
      });
      rememberArtifact(artifact, "The lost ribbon became a study bookmark.");
      travel("study", "The ribbon saves a page in the study.");
      return;
    }
    if (title === "Feather") {
      persistProgression({ ...progression, hiddenMemoryUnlocked: true });
      setSceneNote(`${title}: ${itemStories[title] || item.body}`);
      travel("greenhouse", "The feather points toward the greenhouse.");
      return;
    }
    setSceneNote(`${title}: ${itemStories[title] || item.body || "The house has not decoded this yet."}`);
    if (location === "attic") {
      createHiddenMemory(`${title} Story`, itemStories[title] || item.body, `Inspected ${title} in the attic.`);
    }
  }

  function inspectMemory(artifact) {
    setSelectedMemory(artifact);
    if (!artifact || artifact.id === "empty") return;
    const sourceText = artifact.source ? ` Created from: "${artifact.source}"` : "";
    const action = artifact.kind === "meal"
      ? " The recipe can return to the kitchen table."
      : artifact.kind === "lesson"
        ? " This can be opened in the study as a learning note."
        : artifact.kind === "candle"
          ? " Mom can turn this into a recovery plan."
          : " It can be returned to the kitchen table or discussed with Mom.";
    setSceneNote(`${artifact.title}: ${artifact.description}${sourceText}${action}`);
  }

  function tendPlant(plantId) {
    const target = plants.find((plant) => plant.id === plantId);
    if (!target) return;
    const nextPlants = plants.map((plant) =>
      plant.id === plantId ? growPlant(plant, "Tended by hand.") : plant
    );
    persistPlants(nextPlants);
    const updated = nextPlants.find((plant) => plant.id === plantId);
    setSceneNote(`${updated.title} is now a ${updated.stageName}.`);
  }

  function restoreArtifactToTable(artifact = selectedMemory) {
    if (!artifact?.id) return;
    const withoutDuplicate = tableArtifacts.filter((item) => item.id !== artifact.id);
    setTableArtifacts([...withoutDuplicate, artifact].slice(-5));
    setSceneNote(`${artifact.tableTitle || artifact.title} returned to the kitchen table.`);
  }

  function removeSelectedMemory() {
    if (!selectedMemory?.id || selectedMemory.id === "empty") return;
    const nextArtifacts = artifacts.filter((item) => item.id !== selectedMemory.id);
    persistArtifacts(nextArtifacts);
    setSelectedMemory(nextArtifacts.at(-1) || null);
    setSceneNote("The hallway took down one old note.");
  }

  function tidyHallway() {
    const nextArtifacts = dedupeArtifactsByTitleDate(artifacts);
    persistArtifacts(nextArtifacts);
    setSelectedMemory(nextArtifacts.at(-1) || null);
    setSceneNote("The hallway kept the first copy of each memory and cleared duplicate clutter.");
  }

  function askMomAboutMemory() {
    if (!selectedMemory?.id || selectedMemory.id === "empty") {
      setSceneNote("Choose a hallway memory first, then Mom can help turn it into a plan.");
      return;
    }
    setChatOpen(true);
    setMomInput(`Help me understand this memory: ${selectedMemory.title}. ${selectedMemory.description}`);
    travel("kitchen", "Mom pulls that memory back to the kitchen table.");
  }

  function fillMomPrompt(text) {
    setMomInput(text);
    setChatOpen(true);
    window.setTimeout(() => {
      document.getElementById("mom-message")?.focus();
    }, 30);
  }

  function travel(nextScene, note = "") {
    const safeScene = ["attic", "study", "greenhouse"].includes(nextScene) ? "kitchen" : nextScene;
    if (safeScene === scene) return;
    setTransitioning(true);
    setSceneNote(safeScene === nextScene ? note : "That room is resting for the demo, so Mom brings you back to the kitchen.");
    window.setTimeout(() => {
      setScene(safeScene);
      if (safeScene === "backyard") {
        setBenchOpen(true);
        setBackyardVideo((current) =>
          current === sceneVideos.backyardA ? sceneVideos.backyardB : sceneVideos.backyardA
        );
      }
    }, 360);
    window.setTimeout(() => {
      setTransitioning(false);
      setSceneNote("");
    }, 920);
  }

  function enterHome() {
    playDoorSound();
    travel("kitchen", "The door opens. The kitchen is awake.");
  }

  function handleMouseMove(event) {
    const rect = event.currentTarget.getBoundingClientRect();
    setParallax({
      x: ((event.clientX - rect.left) / rect.width - 0.5) * 18,
      y: ((event.clientY - rect.top) / rect.height - 0.5) * 18,
    });
  }

  function streamReply(reply, nextMessages, onComplete) {
    window.clearInterval(streamRef.current);
    setMomThinking(true);
    setMomDraft("Mom is writing");
    window.setTimeout(() => {
      setMomThinking(false);
      const words = reply.split(" ");
      let index = 0;
      setMomDraft("");
      streamRef.current = window.setInterval(() => {
        index += 1;
        setMomDraft(words.slice(0, index).join(" "));
        if (index >= words.length) {
          window.clearInterval(streamRef.current);
          setMomDraft("");
          setMessages([...nextMessages, { role: "mom", text: reply }]);
          if (onComplete) onComplete();
          setCatState("walking");
          window.setTimeout(() => setCatState("sleeping"), 4300);
        }
      }, 72);
    }, 720);
  }

  async function sendToMom(event) {
    event.preventDefault();
    const clean = momInput.trim();
    if (!clean) return;
    const kind = classifyMessage(clean);
    const nextMessages = [...messages, { role: "you", text: clean }];
    setMessages(nextMessages);
    setMomInput("");
    setApiStatus({ state: "online", label: "Mom is listening" });
    const momResult = await askMom(clean, nextMessages, buildHouseState());
    setApiStatus({
      state: momResult.source === "local" ? "local" : "online",
      label: momResult.source === "local" ? "Mom is using house memory" : "Mom is listening",
    });
    streamReply(momResult.reply, nextMessages, () => {
      const artifact = requestedArtifactFromMessage(clean);
      rememberArtifact(artifact, `${artifact.title} appeared on the kitchen table.`);
    });
    if (kind === "tea") {
      setSceneNote("Mom recommends the Fireplace Ambience station.");
      setRadioStation(1);
    }
    if (kind === "meal") {
      setSceneNote("The kitchen table makes room for a warm meal.");
    }
    if (kind === "lesson") {
      persistProgression({ ...progression, studyBookmarkUnlocked: true });
    }
  }

  function openDrawer() {
    const slip = drawerSlips[Math.floor(Math.random() * drawerSlips.length)];
    const [title, ...bodyParts] = slip.split(":");
    const item = {
      icon: title.includes("Recipe") ? "📜" : title.includes("Letter") ? "💌" : title.includes("Study") ? "🗓️" : "📝",
      title: title.trim(),
      body: bodyParts.join(":").trim(),
    };
    const artifact = {
      id: Date.now(),
      type: "artifact",
      kind: "drawer",
      icon: item.icon,
      tableIcon: item.icon,
      title: item.title,
      tableTitle: item.title,
      description: item.body,
      tableLine: item.body,
      source: "Found in Mom's drawer.",
      date: new Date().toLocaleDateString(undefined, { month: "short", day: "numeric", year: "numeric" }),
    };
    setDrawerItem(item);
    rememberArtifact(artifact, `${item.title} found in Mom's drawer.`);
  }

  function openRecipeBook() {
    const artifact = makeArtifact("lesson", "Opened the recipe book on the kitchen counter.", {
      icon: "📖",
      title: "Counter Recipe Book",
      description: "A page marked: study gently, revise twice, ask one clear question.",
    });
    rememberArtifact(artifact, "The recipe book opened to a useful page.");
  }

  function pourTea() {
    const artifact = makeArtifact("tea", "Poured tea before saying anything.", {
      icon: "🍵",
      title: "Steaming Tea Note",
      description: "The cup says: you can pause before you solve everything.",
    });
    rememberArtifact(artifact, "The tea cup left a warm note on the table.");
  }

  function clickCat() {
    playMeow();
    const item = catCollectibles[Math.floor(Math.random() * catCollectibles.length)];
    const collectible = {
      ...item,
      id: `${Date.now()}-${Math.random().toString(16).slice(2)}`,
      date: new Date().toLocaleDateString(undefined, { month: "short", day: "numeric" }),
    };
    const next = [...collectibles, collectible].slice(-24);
    persistCollectibles(next);
    setCatState("looking");
    setCatBubble(`${item.icon} ${item.title}`);
    setSceneNote(`The cat brought you: ${item.title}. Click it in the shelf or attic to use it.`);
    window.setTimeout(() => setCatBubble(""), 2200);
  }

  async function startRadio(stationIndex = radioStation) {
    const resolvedStationIndex = Number.isInteger(stationIndex) ? stationIndex : radioStation;
    const station = radioStations[resolvedStationIndex] || radioStations[0];
    setRadioPlaying(true);
    setSceneNote("");
    try {
      await startAmbientStation(station.id, radioVolume);
    } catch (error) {
      setRadioPlaying(false);
      setSceneNote(`Browser blocked the radio for a moment (${error?.name || "audio policy"}). Click Play again after entering Home.`);
    }
  }

  function stopRadio() {
    stopAmbientRadio();
    setRadioPlaying(false);
  }
  function changeStation(index) {
    setRadioStation(index);
    if (radioPlaying) {
      window.setTimeout(() => startRadio(index), 40);
    }
  }

  async function saveBackyardNote(event) {
    event.preventDefault();
    const clean = reflection.trim();
    if (!clean) return;
    const plant = normalizePlant({
      id: `${Date.now()}-${Math.random().toString(16).slice(2)}`,
      ...plantFromReflection(clean),
      text: clean,
      stage: 0,
      date: new Date().toLocaleDateString(undefined, { month: "short", day: "numeric" }),
      history: [`Seed: ${clean}`],
    });
    const nextPlants = [...plants, plant].slice(-24);
    persistPlants(nextPlants);
    setReflection("");
    const note = { title: plant.title, body: clean };
    const stored = readStorage(STORAGE_KEYS.notes, []);
    const nextStored = [...stored, note].slice(-10);
    writeStorage(STORAGE_KEYS.notes, nextStored);
    setNotes([...fallbackNotes, ...nextStored].slice(-12));
    setSelectedNote(note);
    setSceneNote(`${plant.title} was planted as a seed.`);
    try {
      await fetch("/api/notes", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(note),
      });
    } catch {
      // Local storage keeps the garden alive during frontend-only previews.
    }
  }

  async function saveHallwayNote(event) {
    event.preventDefault();
    const clean = noteDraft.trim();
    if (!clean) return;
    const note = {
      title: clean.split(/\s+/).slice(0, 3).join(" ") || "Visitor",
      body: clean,
    };
    const stored = readStorage(STORAGE_KEYS.notes, []);
    const nextStored = [...stored, note].slice(-10);
    writeStorage(STORAGE_KEYS.notes, nextStored);
    const nextNotes = [...fallbackNotes, ...nextStored].slice(-12);
    setNotes(nextNotes);
    setSelectedNote(note);
    setNoteDraft("");
    setSceneNote("Your note joined the hallway.");
    try {
      const response = await fetch("/api/notes", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(note),
      });
      if (response.ok) {
        const data = await response.json();
        if (data.notes?.length) setNotes([...fallbackNotes, ...data.notes].slice(-12));
      }
    } catch {
      // Local storage keeps notes working when the Python backend is not running.
    }
  }

  const station = radioStations[radioStation];

  if (scene === "memory") {
    console.log("Home hallway artifacts", artifacts);
  }

  return (
    <main
      className={`home-stage scene-${scene} ${transitioning ? "is-traveling" : ""}`}
      onMouseMove={handleMouseMove}
      style={{ "--px": `${parallax.x}px`, "--py": `${parallax.y}px` }}
    >
      <SceneVideo src={sceneVideos.arrival} active={scene === "arrival"} label="Exterior of Home" />
      <SceneVideo src={sceneVideos.kitchen} active={scene === "kitchen"} label="Mother preparing food late at night" />
      <SceneVideo src={backyardVideo} active={scene === "backyard"} label="Cat in backyard garden" />

      {scene === "memory" && <MemoryBackdrop />}

      <div className="weather-layer" aria-hidden="true"><i /><i /><i /><i /><i /><i /><i /><i /></div>
      <div className="firefly-layer" aria-hidden="true"><i /><i /><i /><i /><i /><i /></div>
      <div className="scene-vignette" aria-hidden="true" />
      <div className="travel-warmth" aria-hidden="true" />

      <nav className="room-nav" aria-label="Move through Home">
        <button type="button" onClick={() => travel("arrival", "You step back onto the wet porch.")}>Porch</button>
        <button type="button" onClick={() => travel("kitchen", "The kitchen gathers around you.")}>Kitchen</button>
        <button type="button" onClick={() => travel("memory", "The hallway remembers what happened here.")}>Hallway</button>
        <button type="button" onClick={() => travel("backyard", "The garden air is cool and kind.")}>Backyard</button>
      </nav>

      {scene === "arrival" && (
        <section className="arrival-scene scene-layer">
          <div className="arrival-title">
            <p>rain on the roof · light in the windows</p>
            <h1>🏡 Home</h1>
            <span>A place that is always glad you came back.</span>
            <button type="button" onClick={enterHome}>Enter Home</button>
          </div>
          <button className="door-hotspot" type="button" onClick={enterHome} aria-label="Open the front door" />
        </section>
      )}

      {scene === "kitchen" && (
        <section className="kitchen-scene scene-layer">
          {chatOpen ? (
            <form className="mom-desk" onSubmit={sendToMom}>
              <div className="mom-desk-header">
                <label htmlFor="mom-message">👩 Mom, caretaker of the house</label>
                <button type="button" aria-label="Close Mom conversation" onClick={() => setChatOpen(false)}>Close</button>
              </div>
              <div className="mom-thread" ref={momThreadRef} aria-live="polite">
                {messages.map((message, index) => (
                  <p className={`speech-slip ${message.role}`} key={`${message.role}-${index}-${message.text}`}>
                    {message.text}
                  </p>
                ))}
                {(momDraft || momThinking) && (
                  <p className={`speech-slip mom ${momThinking ? "thinking" : ""}`}>
                    {momDraft}
                    {!momThinking && <span className="cursor" />}
                  </p>
                )}
              </div>
              {messages.length === 1 && !momDraft && !momThinking && (
                <section className="mom-onboarding" aria-label="Ways to begin with Mom">
                  <div className="mom-prompt-chips">
                    {momPromptChips.map((chip) => (
                      <button key={chip.label} type="button" onClick={() => fillMomPrompt(chip.text)}>
                        <span>{chip.icon}</span>
                        {chip.label}
                      </button>
                    ))}
                  </div>
                  <p className="house-reacts-note">
                    The house reacts to your stories. Memories, keepsakes, discoveries, and plants may appear.
                  </p>
                  <div className="room-hint-row" aria-label="What each part of Home does">
                    {kitchenRoomHints.map((hint) => (
                      <span key={hint.label}>
                        <b>{hint.icon} {hint.label}</b>
                        {hint.text}
                      </span>
                    ))}
                  </div>
                </section>
              )}
              <textarea
                id="mom-message"
                value={momInput}
                onChange={(event) => setMomInput(event.target.value)}
                placeholder="What happened today?"
              />
              <p className="mom-input-helper">
                Try: I failed my exam today. · I got an internship. · Explain recursion. · I want to become more confident. · I had a fight with a friend.
              </p>
              <button type="submit" disabled={momThinking || Boolean(momDraft)}>
                {momThinking || momDraft ? "Mom is writing..." : "Ask Mom to make something"}
              </button>
            </form>
          ) : (
            <button className="mom-reopen" type="button" onClick={() => setChatOpen(true)}>
              <span>👩</span>
              Talk to Mom
            </button>
          )}

          <section className="kitchen-object-rail" aria-label="Kitchen objects">
            <button type="button" onClick={openRecipeBook}>
              <span>📖</span>
              Recipe Book
            </button>
            <button type="button" onClick={pourTea}>
              <span>🍵</span>
              Tea Cup
            </button>
            <button type="button" onClick={openDrawer}>
              <span>🗄</span>
              Mom's Drawer
            </button>
          </section>

          <section className="kitchen-table" aria-label="Kitchen table artifacts">
            <header>
              <strong>Kitchen Table</strong>
              <span>{tableArtifacts.length ? "Artifacts from your day" : "Waiting for its first memory"}</span>
            </header>
            <div className="artifact-spread">
              {tableArtifacts.length ? tableArtifacts.map((artifact, index) => (
                <button
                  className={`table-card table-card-${index + 1}`}
                  key={artifact.id}
                  type="button"
                  onClick={() => {
                    setSelectedMemory(artifact);
                    travel("memory", "The hallway opens to the artifact you made.");
                  }}
                >
                  <span>{artifact.tableIcon}</span>
                  <strong>{artifact.tableTitle}</strong>
                </button>
              )) : <p className="empty-table">Say “I failed my exam”, “I got an internship”, or “Explain recursion.”</p>}
            </div>
          </section>

          {drawerItem && (
            <aside className="drawer-find paper-modal" key={drawerItem.title}>
              <button className="paper-close" type="button" onClick={() => setDrawerItem(null)}>Close</button>
              <span>{drawerItem.icon}</span>
              <strong>{drawerItem.title}</strong>
              <p>{drawerItem.body}</p>
            </aside>
          )}

          <section className="radio-console">
            <header>
              <span>📻</span>
              <div>
                <strong>{station.name}</strong>
                <small>{station.caption}</small>
              </div>
            </header>
            <div className={`radio-wave ${radioPlaying ? "playing" : ""}`}><i /><i /><i /><i /><i /></div>
            <div className="radio-controls">
              <button type="button" onClick={() => (radioPlaying ? stopRadio() : startRadio())}>{radioPlaying ? "Pause" : "Play"}</button>
              <input aria-label="Radio volume" type="range" min="0" max="1" step="0.01" value={radioVolume} onChange={(event) => setRadioVolume(Number(event.target.value))} />
            </div>
            <div className="station-list">
              {radioStations.map((item, index) => (
                <button className={index === radioStation ? "active" : ""} key={item.id} type="button" onClick={() => changeStation(index)}>
                  {item.icon} {item.name}
                </button>
              ))}
            </div>
          </section>

          <button className={`cat-presence cat-${catState}`} type="button" onClick={clickCat}>
            <span className="cat-emoji">🐈</span>
            {collectibles.length > 0 && <small className="cat-count">{collectibles.length}</small>}
          </button>
          {catBubble && <p className="cat-bubble">{catBubble}</p>}

          <aside className="inventory-shelf">
            <strong>Cat's Finds</strong>
            <div>
              {collectibles.slice(-6).map((item) => (
                <button title={`${item.title}: use discovery`} key={item.id} type="button" onClick={() => inspectCollectible(item)}>
                  <span>{item.icon}</span>
                  <small>{item.title}</small>
                </button>
              ))}
              {!collectibles.length && <small>Click the cat. It brings discoveries.</small>}
            </div>
          </aside>

          <button className="hallway-door" type="button" onClick={() => travel("memory", "The hallway opens to the house's memory.")}>
            House Memory Hallway →
          </button>
        </section>
      )}

      {scene === "memory" && (
        <section className="memory-scene scene-layer">
          <header className="hallway-progress">
            <p>House Memory Hallway</p>
            <div aria-label="House memory stats">
              <span><strong>{artifacts.length}</strong> Memories</span>
              <span><strong>{collectibles.length}</strong> Discoveries</span>
              <span><strong>{plants.length}</strong> Garden Progress</span>
              <span><strong>{visits}</strong> Visits</span>
            </div>
            <small className="visit-line">Visit #{visits} · The house remembers.</small>
          </header>

          <section className="memory-wall journey-wall" aria-label="Pinned memory museum wall">
            {(artifacts.length ? artifacts.slice(-12).reverse() : [{
              id: "empty",
              icon: "📌",
              title: "The first memory is waiting",
              date: "Today",
              description: "The wall is waiting for your first memory.",
            }]).map((artifact, index) => (
              <button
                key={artifact.id}
                className={`memory-artifact memory-artifact-${(index % 8) + 1}`}
                type="button"
                onClick={() => inspectMemory(artifact)}
              >
                <span>{artifact.icon || "📌"}</span>
                <strong>{artifact.title || "House Memory"}</strong>
                <small>{artifact.date || "Today"}</small>
                <em>{artifact.description || "The house kept this memory."}</em>
              </button>
            ))}
          </section>

          <aside className="hallway-side-rail" aria-label="Hallway shelves">
            <details open>
              <summary>Recent Visitor Notes</summary>
              <div className="note-strip">
                {notes.slice(-5).map((note, index) => (
                  <button key={`${note.title}-${index}`} type="button" onClick={() => setSelectedNote(note)}>
                    {note.title}
                  </button>
                ))}
              </div>
              <p>{selectedNote.body}</p>
              <form onSubmit={saveHallwayNote}>
                <textarea value={noteDraft} onChange={(event) => setNoteDraft(event.target.value)} placeholder="Leave a note for the hallway..." />
                <button type="submit">Pin note</button>
              </form>
            </details>

            <details open>
              <summary>Cat Discoveries</summary>
              {collectibles.length ? (
                <div className="museum-pill-row">
                  {collectibles.slice(-8).map((item) => (
                    <button className="museum-pill cat-pill" key={item.id} type="button" onClick={() => inspectCollectible(item, "hallway")}>
                      {item.icon} {item.title}
                    </button>
                  ))}
                </div>
              ) : <p>Click the cat in any room to start the shelf.</p>}
            </details>

            <details open>
              <summary>Garden Progress</summary>
              {plants.length ? (
                <div className="museum-pill-row">
                  {plants.slice(-6).map((plant) => (
                    <button className="museum-pill garden-pill" key={plant.id} type="button" onClick={() => tendPlant(plant.id)}>
                      {plant.icon} {plant.stageName || "Seed"} · {plant.title}
                    </button>
                  ))}
                </div>
              ) : <p>Plant a reflection in the backyard.</p>}
            </details>

            <button className="tidy-hallway" type="button" onClick={tidyHallway}>Tidy duplicate memories</button>
          </aside>

          {selectedMemory && selectedMemory.id !== "empty" && (
            <section className="memory-modal" role="dialog" aria-modal="true" aria-label="Memory detail">
              <article className="opened-letter memory-detail" key={selectedMemory.id}>
                <button className="paper-close" type="button" onClick={() => setSelectedMemory(null)}>Close</button>
                <span className="memory-modal-icon">{selectedMemory.icon || "📌"}</span>
                <p className="memory-modal-kicker">{selectedMemory.relatedRoom || selectedMemory.kind || "House Memory"}</p>
                <h2>{selectedMemory.title || "This house remembers you."}</h2>
                <small>{selectedMemory.date || "Today"}</small>
                <p>{selectedMemory.description || "Artifacts from your milestones, hard days, lessons, and discoveries will pin themselves here."}</p>
                {selectedMemory.source && <em>"{selectedMemory.source}"</em>}
                <div className="memory-actions">
                  {selectedMemory?.kind !== "collectible" && <button type="button" onClick={() => restoreArtifactToTable()}>Return to table</button>}
                  {selectedMemory?.kind !== "collectible" && <button type="button" onClick={askMomAboutMemory}>Ask Mom</button>}
                  {selectedMemory?.kind === "meal" && <button type="button" onClick={() => unlockKitchenRecipe(selectedMemory.source || selectedMemory.description)}>Unlock recipe</button>}
                  {selectedMemory?.kind !== "collectible" && <button type="button" onClick={removeSelectedMemory}>Take down</button>}
                </div>
              </article>
            </section>
          )}

          <button className="backyard-door" type="button" onClick={() => travel("backyard", "The hallway door opens into the reflection garden.")}>
            To the reflection garden →
          </button>
        </section>
      )}

      {scene === "backyard" && (
        <section className="backyard-scene scene-layer">
          <button className={`garden-cat cat-${catState}`} type="button" onClick={clickCat}>
            <span className="cat-emoji">🐈</span>
            {collectibles.length > 0 && <small className="cat-count">{collectibles.length}</small>}
          </button>
          {benchOpen ? (
            <section className="reflection-bench" aria-label="Reflection Bench">
              <button className="bench-close" type="button" aria-label="Close Reflection Bench" onClick={() => setBenchOpen(false)}>
                Close
              </button>
              <h2>Reflection Bench</h2>
              <p className="reflection-question">{reflectionQuestions[questionIndex]}</p>
              <div className="bench-garden-row" aria-label="Saved reflection plants">
                {plants.length ? plants.slice(-6).map((plant) => (
                  <button key={plant.id} type="button" onClick={() => tendPlant(plant.id)} title={plant.text || plant.body}>
                    <span>{plant.icon}</span>
                    <small>{plant.stageName}</small>
                  </button>
                )) : <small>No plants yet.</small>}
              </div>
              <form onSubmit={saveBackyardNote}>
                <textarea value={reflection} onChange={(event) => setReflection(event.target.value)} placeholder="Write a reflection. The garden will grow something from it." />
                <button type="submit">Plant this 🌱</button>
              </form>
            </section>
          ) : (
            <button className="reflection-bench-tab" type="button" onClick={() => setBenchOpen(true)}>
              Open Reflection Bench 🌱
            </button>
          )}

          <div className="garden-growth" aria-label="Your reflection garden">
            {plants.slice(-6).map((plant, index) => (
              <button
                className={`plant plant-${(index % 10) + 1}`}
                key={plant.id}
                type="button"
                onClick={() => tendPlant(plant.id)}
                title={`${plant.stageName}: ${plant.body}`}
              >
                <span>{plant.icon}</span>
                <small>{plant.stageName}</small>
              </button>
            ))}
          </div>

          <aside className="garden-progress">
            <strong>Garden Progress</strong>
            <span>{plants.length} plants grown</span>
            <span>{collectibles.length} cat finds</span>
            <span>{artifacts.length} hallway memories</span>
          </aside>

          <button className="kitchen-return" type="button" onClick={() => travel("kitchen", "You return to the warm kitchen.")}>
            Back to Mom
          </button>
        </section>
      )}

      {scene === "attic" && (
        <section className="attic-scene scene-layer">
          <article className="locked-room-panel">
            <p>Attic</p>
            <h2>{unlocks.attic ? "The old trunk is open." : "The attic is still locked."}</h2>
            <span>
              {unlocks.attic
                ? "Your key, old letters, and repeated visits have made this room part of Home."
                : "Find the Small Key from the cat or visit Home three times to open this room."}
            </span>
            <div className={`attic-trunk ${progression.trunkOpen ? "open" : ""}`}>
              <strong>{progression.trunkOpen ? "Unlocked Trunk" : "Locked Trunk"}</strong>
              <p>
                {progression.trunkOpen
                  ? "The trunk has revealed the old letter. Inspect the letter to pin its memory downstairs."
                  : "Needs the Small Key. When it opens, it should change the hallway."}
              </p>
              <button type="button" onClick={() => {
                const key = collectibles.find((item) => item.title === "Small Key");
                if (key) inspectCollectible(key, "attic");
                else setSceneNote("The trunk is locked. Ask the cat to look around for a Small Key.");
              }}>
                {progression.trunkOpen ? "Inspect trunk" : "Try the lock"}
              </button>
            </div>
            <div className="room-evidence">
              <strong>Collectibles with consequences</strong>
              {collectibles.map((item) => (
                <button key={item.id} type="button" onClick={() => inspectCollectible(item, "attic")}>
                  <span>{item.icon}</span>
                  {item.title}
                </button>
              ))}
              {!collectibles.length && <em>No discoveries yet. Click the cat when you see it.</em>}
            </div>
            <button type="button" onClick={() => travel("kitchen", "You come down from the attic stairs.")}>Back to Mom</button>
          </article>
        </section>
      )}

      {scene === "study" && (
        <section className="study-scene scene-layer">
          <section className="study-library" aria-label="Digital mini library">
            <div className="library-lamp" />
            <header>
              <p>Study Room</p>
              <h2>Mini Library</h2>
              <span>Lessons, papers, and quiet courage live here.</span>
            </header>
            <div className="bookcase" aria-label="Study books">
              {["Algorithms", "Python", "Math", "Design", "Dreams", "Letters", "Notes", "Maps", "Stories", "Rest"].map((book, index) => (
                <button
                  className={`book-spine book-${(index % 6) + 1}`}
                  key={book}
                  type="button"
                  onClick={() => setSceneNote(`${book}: Mom marked one page for later.`)}
                >
                  {book}
                </button>
              ))}
            </div>
            <div className="study-desk">
              <div className="laptop">
                <div className="laptop-screen">
                  <span>Home Notes</span>
                  <p>{selectedMemory?.title || "Choose a memory from the hallway."}</p>
                </div>
                <div className="laptop-base" />
              </div>
              <button className="paper-page page-one" type="button" onClick={() => openRecipeBook()}>
                <strong>Lesson Page</strong>
                <span>Open recipe book</span>
              </button>
              <button className="paper-page page-two" type="button" onClick={() => askMomAboutMemory()}>
                <strong>Ask Mom</strong>
                <span>Turn memory into a plan</span>
              </button>
              <button className="paper-page page-three" type="button" onClick={() => setSceneNote("The laptop saved a small study plan: 25 minutes, tea, then one example.")}>
                <strong>Study Plan</strong>
                <span>25 min focus</span>
              </button>
            </div>
          </section>

          <aside className="study-side">
            <strong>On the desk</strong>
            <p>{artifacts.some((item) => item.kind === "lesson") ? `${artifacts.filter((item) => item.kind === "lesson").length} study notes saved from Mom's teaching.` : "Ask Mom to explain recursion, operating systems, scheduling, or data structures."}</p>
            <div className="study-artifacts">
              {artifacts.filter((item) => item.kind === "lesson").slice(-6).map((artifact) => (
                <button key={artifact.id} type="button" onClick={() => {
                  setSelectedMemory(artifact);
                  setSceneNote(`${artifact.title}: ${artifact.source || artifact.description}`);
                }}>
                  <span>{artifact.icon}</span>
                  {artifact.title}
                </button>
              ))}
              {!artifacts.some((item) => item.kind === "lesson") && <em>No study notes yet.</em>}
            </div>
          </aside>

          <button className="kitchen-return study-return" type="button" onClick={() => travel("kitchen", "You carry a page back to the kitchen.")}>
            Back to Mom
          </button>
        </section>
      )}

      {scene === "greenhouse" && (
        <section className="greenhouse-scene scene-layer">
          <article className="locked-room-panel greenhouse-panel">
            <p>Greenhouse</p>
            <h2>{unlocks.greenhouse ? "The garden has moved indoors." : "The greenhouse is waiting."}</h2>
            <span>
              {unlocks.greenhouse
                ? "Your reflections have grown enough roots to live here."
                : "Grow three backyard plants or let the cat bring a greenhouse discovery."}
            </span>
            <div className="room-evidence">
              <strong>Plant evolution timeline</strong>
              {plants.slice(-8).map((plant) => (
                <button key={plant.id} type="button" onClick={() => setSceneNote(`${plant.title}: ${plant.history?.join(" → ") || plant.body}`)}>
                  <span>{plant.icon}</span>
                  {plant.stageName} · {plant.title}
                </button>
              ))}
              {!plants.length && <em>No plants yet. Answer the backyard bench question to grow one.</em>}
            </div>
            <div className="greenhouse-stats">
              <span>Oldest: {plants[0]?.title || "none yet"}</span>
              <span>Newest: {plants.at(-1)?.title || "none yet"}</span>
              <span>Completed trees: {plants.filter((plant) => plant.growth >= 3).length}</span>
            </div>
            <button type="button" onClick={() => travel("backyard", "You step back into the reflection garden.")}>Grow something</button>
          </article>
        </section>
      )}

      {sceneNote && <p className="scene-note">{sceneNote}</p>}
    </main>
  );
}

function SceneVideo({ src, active, label }) {
  return (
    <video
      className={`scene-video ${active ? "is-active" : ""}`}
      src={src}
      aria-label={label}
      autoPlay
      muted
      loop
      playsInline
      preload="auto"
    />
  );
}

function MemoryBackdrop() {
  return (
    <div className="memory-backdrop" aria-hidden="true">
      <div className="hall-ceiling" />
      <div className="hall-lamp" />
      <div className="hall-window" />
      <div className="hall-board" />
      <div className="floorboards" />
    </div>
  );
}

function StudyBackdrop() {
  return (
    <div className="study-backdrop" aria-hidden="true">
      <div className="study-wall" />
      <div className="study-window" />
      <div className="study-shelf shelf-one" />
      <div className="study-shelf shelf-two" />
      <div className="study-rug" />
      <div className="study-floor" />
    </div>
  );
}

const rootElement = document.getElementById("root");
globalThis.__homeRoot = globalThis.__homeRoot || createRoot(rootElement);
globalThis.__homeRoot.render(<App />);
