/* =========================================================
  app.js — 前端交互逻辑 v7.0 · 动态知识卡片 + 收藏历史 + 个人中心
  ========================================================= */

// ---- 全局状态 ----
let currentModule = "发现";
let currentVideoUrl = "";
let currentVideoTitle = "";
let currentDiscipline = "all";
let currentSubTab = "recommend";
let currentLifeTab = "story";
let allVideos = [];
let allKnowledgeCards = [];
let storyData = null;
let simulatorData = null;
let currentContext = { type: "", title: "" };
let userId = localStorage.getItem("mathAppUserId") || generateUserId();
let userFavorites = { video: {}, knowledge: {} };
let userAvatar = localStorage.getItem("mathAppAvatar") || "葫";
let userAvatarColor = localStorage.getItem("mathAppAvatarColor") || "#A0B9D8";

let vnState = {
  currentNode: null,
  stats: { shuli: 10, xiayi: 10, xinzhi: 10 },
  favorability: { "欧阳侠": 0, "Eddie侠": 0, "厚朴侠": 0, "三力思侠": 0, "溜溜侠": 0, "孔锝侠": 0, "星侠": 0, "甲亢侠": 0 }
};

let simState = {
  currentNode: null,
  stats: { gpa: 10, interest: 10, skill: 10 },
  history: []
};

const SIM_STAT_LABELS = { gpa: "绩点", interest: "兴趣", skill: "技能" };
const SIM_STAT_COLORS = { gpa: "#A0B9D8", interest: "#DAB8B0", skill: "#B8D0C9" };
const SIM_STAT_EMOJIS = { gpa: "📊", interest: "❤️", skill: "🔧" };

function generateUserId() {
  var id = "user_" + Date.now().toString(36) + "_" + Math.random().toString(36).substr(2, 6);
  localStorage.setItem("mathAppUserId", id);
  return id;
}

const STAT_LABELS = { shuli: "数理根基", xiayi: "侠义声望", xinzhi: "心志韧性" };
const STAT_COLORS = { shuli: "#A0B9D8", xiayi: "#DAB8B0", xinzhi: "#B8D0C9" };
const STAT_EMOJIS = { shuli: "📐", xiayi: "⚔️", xinzhi: "🧘" };

// 章节检测：根据节点ID前缀判断当前章节
function detectChapter(nodeId) {
  if (!nodeId) return 1;
  var m = nodeId.match(/^ch(\d+)/);
  return m ? parseInt(m[1]) : 1;
}

// ---- 模块配色 ----
const MODULE_COLORS = {
  "发现": { color: "#A0B9D8", gradient: "linear-gradient(135deg, #A0B9D8, #8AA3C2)", lightBg: "#DCE6F0", name: "发现 · 值得一看的数学内容" },
  "知识": { color: "#B8D0C9", gradient: "linear-gradient(135deg, #B8D0C9, #A0BBAF)", lightBg: "#E4EFEB", name: "知识 · 数学发现" },
  "数学人生": { color: "#DAB8B0", gradient: "linear-gradient(135deg, #DAB8B0, #C9A39A)", lightBg: "#F2E4DF", name: "数学人生 · 大学选择模拟" },
  "我的": { color: "#C5CAD0", gradient: "linear-gradient(135deg, #C5CAD0, #AEB3B9)", lightBg: "#EDEEF0", name: "我的 · 个人中心" }
};

const MODULE_BANNERS = {
  "发现": { icon: "◎", title: "发现值得一看的数学内容", sub: "从 12 个专业分区进入，直接查看该方向的视频内容" },
  "知识": { icon: "≡", title: "用数学视角理解世界", sub: "把复杂概念变成轻量、好懂、适合反复刷的知识卡片" },
  "数学人生": { icon: "◇", title: "葫芦侠行 · 互动剧情", sub: "八章完整剧情 | 四属性系统 | 多结局 | 北宋数学武侠世界" },
  "我的": { icon: "✦", title: "你的学习空间", sub: "查看资料、收藏、记录和剧情存档" }
};

const MODULE_CSS_CLASS = {
  "发现": "shuren", "知识": "shushuo", "数学人生": "shusi", "我的": "shuwu"
};

const DISCIPLINE_ICONS = {
  "计算机": { icon: "💻", symbol: "⌨", color: "#fff", bg: "#8BA4C0" },
  "机械工程": { icon: "⚙️", symbol: "⚙", color: "#fff", bg: "#B8A99A" },
  "电子信息": { icon: "📡", symbol: "⚡", color: "#fff", bg: "#9BB8AE" },
  "经济管理": { icon: "📊", symbol: "📈", color: "#fff", bg: "#C5A8A0" },
  "生物医学": { icon: "🧬", symbol: "❤", color: "#fff", bg: "#C4B0B8" },
  "化工材料": { icon: "🧪", symbol: "⚗", color: "#fff", bg: "#ACAEC5" },
  "人工智能": { icon: "🤖", symbol: "AI", color: "#fff", bg: "#95ABB8" },
  "微电子": { icon: "🔌", symbol: "μ", color: "#fff", bg: "#8BA4C0" },
  "自动化": { icon: "🦾", symbol: "⚙", color: "#fff", bg: "#BCA898" },
  "能源动力": { icon: "🔥", symbol: "⚡", color: "#fff", bg: "#A3B4A5" },
  "环境工程": { icon: "🌱", symbol: "✿", color: "#fff", bg: "#A8B8A8" },
  "数学建模": { icon: "📐", symbol: "Σ", color: "#fff", bg: "#9CAEC4" },
  "通用": { icon: "📚", symbol: "◎", color: "#fff", bg: "#ADB5BD" }
};

const AVATAR_COLORS = ["#A0B9D8","#B8D0C9","#DAB8B0","#C5CAD0","#B8A99A","#9BB8AE","#C4B0B8","#ACAEC5"];

// ---- 剧本广场定义 ----
const SCRIPTS = [
  {
    id: "huluxiaxing",
    name: "葫芦侠行",
    icon: "📖",
    desc: "北宋年间，紫金山下秉公习武堂。你扮演葫芦侠，用数学武学闯荡江湖。",
    meta: "93节点 · 8章 · 8+结局",
    status: "ready",
    cssClass: "ss-card-wuxia",
    engine: "story"
  },
  {
    id: "simulator",
    name: "数学专业模拟器",
    icon: "🎓",
    desc: "跑完四年大学数学专业的全流程。选课、考试、分流、毕业——你的每个选择决定结局。",
    meta: "8学期 · 大三专业分流 · 多结局",
    status: "beta",
    cssClass: "ss-card-simulator",
    engine: "simulator"
  },
  {
    id: "mathbti",
    name: "数学人格测试",
    icon: "🧮",
    desc: "12道场景题，发现你的数学人格类型——你是「梦境探索者」还是「博弈游侠」？",
    meta: "12题 · 4轴测评 · 12人格",
    status: "ready",
    cssClass: "ss-card-mathbti",
    engine: "mathbti"
  }
];

const COMING_SOON = [
  { name: "数学侦探录", icon: "🔍", desc: "民国上海，用数学破案", color: "#B8A99A" },
  { name: "星际数探", icon: "🔭", desc: "22世纪太空舰队首席数理官", color: "#9BB8AE" },
  { name: "算学千年", icon: "📜", desc: "穿越古代，亲历数学史", color: "#ACAEC5" },
  { name: "数学创业记", icon: "🚀", desc: "硅谷数学PhD辍学创业", color: "#C4B0B8" }
];

// ---- 角色立绘映射 (P0-2) ----
const SPRITE_MAP = {
  "葫芦侠":  "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=young%20Chinese%20Song%20dynasty%20scholar%20with%20a%20gourd%20hanging%20at%20his%20waist%2C%20simple%20blue-gray%20robes%2C%20gentle%20expression%2C%20clean%20ink%20wash%20painting%20style%20on%20soft%20background%2C%20morandi%20muted%20colors&image_size=portrait_4_3",
  "欧阳侠":  "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=diligent%20young%20Chinese%20scholar%20reading%20a%20book%20at%20dawn%2C%20warm%20candle%20light%2C%20ink%20wash%20style%2C%20wearing%20simple%20robes%2C%20soft%20morandi%20beige%20and%20blue%20tones&image_size=portrait_4_3",
  "溜溜侠":  "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=mysterious%20young%20Chinese%20female%20warrior%20with%20a%20large%20black%20dog%2C%20veiled%20face%2C%20dark%20grey%20robes%2C%20ink%20wash%20painting%20style%2C%20morandi%20muted%20dark%20tones%2C%20cool%20atmosphere&image_size=portrait_4_3",
  "孔锝侠":  "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=strict%20Chinese%20martial%20arts%20instructor%2C%20tall%20and%20upright%2C%20stern%20but%20kind%20eyes%2C%20ink%20wash%20style%2C%20wearing%20neat%20grey-blue%20robes%2C%20morandi%20muted%20tones&image_size=portrait_4_3",
  "Eddie侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=young%20Chinese%20scholar%20with%20copper%20coin%20spinning%20on%20finger%2C%20playful%20expression%2C%20ink%20wash%20style%2C%20soft%20morandi%20tones&image_size=portrait_4_3",
  "厚朴侠":  "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=elegant%20Chinese%20theater%20actor%2C%20dramatic%20pose%2C%20traditional%20costume%2C%20ink%20wash%20style%2C%20morandi%20muted%20warm%20tones&image_size=portrait_4_3",
  "星侠":    "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=gentle%20Chinese%20female%20scholar%20with%20warm%20smile%2C%20ink%20wash%20style%2C%20flowing%20robes%2C%20morandi%20soft%20pink%20and%20grey%20tones&image_size=portrait_4_3",
  "甲亢侠":  "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=passionate%20young%20Chinese%20debater%2C%20animated%20gestures%2C%20ink%20wash%20style%2C%20morandi%20muted%20tones%2C%20intense%20eyes&image_size=portrait_4_3"
};

// ---- 场景背景映射 (P0-2) ----
const BG_MAP = {
  "紫金山": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=ancient%20Chinese%20academy%20on%20misty%20Purple%20Mountain%2C%20traditional%20architecture%20with%20courtyards%2C%20morning%20fog%2C%20ink%20wash%20painting%20style%2C%20morandi%20muted%20greens%20and%20greys&image_size=landscape_4_3",
  "石室":   "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=underground%20stone%20chamber%20turned%20study%20room%2C%20candle%20lit%2C%20wooden%20shelves%20with%20scrolls%2C%20murals%20on%20rock%20walls%2C%20warm%20atmosphere%2C%20ink%20wash%20style%2C%20morandi%20warm%20greys&image_size=landscape_4_3",
  "姑苏":   "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=Suzhou%20ancient%20canal%20town%20in%20spring%2C%20stone%20bridges%2C%20willow%20trees%2C%20traditional%20white%20wall%20buildings%2C%20gentle%20rain%2C%20ink%20wash%20painting%20style%2C%20morandi%20muted%20blue-grey%20tones&image_size=landscape_4_3",
  "京城":   "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=ancient%20Chinese%20capital%20city%2C%20grand%20government%20hall%2C%20solemn%20atmosphere%2C%20ink%20wash%20style%2C%20morandi%20muted%20grey%20tones&image_size=landscape_4_3"
};

// 章节 → 默认背景映射
const CHAPTER_BG = {
  1: "紫金山",
  2: "紫金山",
  3: "紫金山",
  4: "石室",
  5: "紫金山",
  6: "石室",
  7: "石室",
  8: "京城"
};

// ---- 页面初始化 ----
document.addEventListener("DOMContentLoaded", async function () {
  await loadStoryData();
  await loadUserFavorites();
  loadVNSave();
  switchModule("发现");
});

// ============================================================
// 模块切换
// ============================================================
function switchModule(module) {
  currentModule = module;
  currentDiscipline = "all";
  currentSubTab = "recommend";

  var colors = MODULE_COLORS[module] || MODULE_COLORS["发现"];
  document.documentElement.style.setProperty("--accent", colors.color);
  document.documentElement.style.setProperty("--accent-light", colors.lightBg);
  document.documentElement.style.setProperty("--accent-gradient", colors.gradient);

  // 顶部品牌栏头像同步
  var brandIcon = document.querySelector(".brand-icon-sm");
  if (brandIcon) { brandIcon.textContent = "葫"; brandIcon.style.background = userAvatarColor; }

  // TabBar 高亮
  document.querySelectorAll(".tab-btn").forEach(function (btn) {
    btn.classList.toggle("active", btn.dataset.module === module);
  });

  // 先隐藏所有特殊面板
  hideAllPanels();

  if (module === "发现") {
    show("searchHeader");
    show("subNav");
    show("videoList");
    // 恢复子导航默认选中"推荐"
    document.querySelectorAll(".sub-nav-btn").forEach(function (btn) {
      btn.classList.toggle("active", btn.dataset.tab === "recommend");
    });
    currentSubTab = "recommend";
    loadVideos("数人");
    return;
  } else if (module === "知识") {
    show("sectionTitleBar");
    show("shushuoHero");
    show("shushuoTopics");
    show("shushuoFeed");
    document.getElementById("contentTitle").textContent = colors.name;
    loadKnowledgeCards();
    return;
  } else if (module === "数学人生") {
    show("scriptSquare");
    renderScriptSquare();
    return;
  } else if (module === "我的") {
    show("sectionTitleBar");
    show("profilePage");
    document.getElementById("contentTitle").textContent = colors.name;
    loadProfileData();
    return;
  }
}

function hideAllPanels() {
  hide("searchHeader"); hide("subNav"); hide("sectionTitleBar"); hide("lifeSubNav");
  hide("moduleBanner"); hide("shushuoHero"); hide("shushuoTopics");
  hide("shushuoFeed"); hide("storyContainer"); hide("simulatorContainer");
  hide("disciplineGridContainer"); hide("disciplineActiveHeader");
  hide("disciplineGrid"); hide("categoryFilter");
  hide("profilePage"); hide("scriptSquare");
  hide("loadingArea"); hide("videoList");
}

function show(id) { var el = document.getElementById(id); if (el) el.style.display = (id === "videoList" ? "grid" : id === "subNav" ? "flex" : "block"); }
function hide(id) { var el = document.getElementById(id); if (el) el.style.display = "none"; }

function updateBanner(module) {
  var info = MODULE_BANNERS[module] || MODULE_BANNERS["发现"];
  var el = document.getElementById("moduleBanner");
  if (!el) return;
  el.style.display = "flex";
  el.className = "module-banner " + (MODULE_CSS_CLASS[module] || "shuren");
  el.innerHTML = '<span class="banner-icon">' + info.icon + '</span><div class="banner-text"><div class="banner-title">' + info.title + '</div><div class="banner-sub">' + info.sub + '</div></div>';
}

// ============================================================
// 数人：搜索、子导航、学科网格
// ============================================================
function handleSearch() {
  var q = document.getElementById("videoSearch").value.toLowerCase().trim();
  if (!q) { renderVideoList(allVideos); return; }
  renderVideoList(allVideos.filter(function (v) {
    return (v.title && v.title.toLowerCase().indexOf(q) >= 0) ||
           (v.description && v.description.toLowerCase().indexOf(q) >= 0) ||
           (v.discipline && v.discipline.toLowerCase().indexOf(q) >= 0) ||
           (v.author && v.author.toLowerCase().indexOf(q) >= 0);
  }));
}

function switchSubTab(tab) {
  currentSubTab = tab;
  document.querySelectorAll(".sub-nav-btn").forEach(function (btn) { btn.classList.toggle("active", btn.dataset.tab === tab); });

  // 先把所有内容区重置
  var gridCtn = document.getElementById("disciplineGridContainer");
  var videoList = document.getElementById("videoList");
  var activeHdr = document.getElementById("disciplineActiveHeader");
  var catFilter = document.getElementById("categoryFilter");

  if (gridCtn) gridCtn.style.display = "none";
  if (activeHdr) activeHdr.style.display = "none";
  if (catFilter) catFilter.style.display = "none";

  if (tab === "discipline") {
    // 专业分区：显示图标网格
    if (videoList) videoList.style.display = "none";
    renderDisciplineGrid();
  } else if (tab === "hot") {
    if (videoList) videoList.style.display = "grid";
    var sorted = [].concat(allVideos).sort(function (a, b) { return (hashStr(b.title) % 1000) - (hashStr(a.title) % 1000); });
    renderVideoList(sorted);
  } else {
    // 推荐
    if (videoList) videoList.style.display = "grid";
    renderVideoList(allVideos);
  }
}

function renderDisciplineGrid() {
  var gridCtn = document.getElementById("disciplineGridContainer");
  var grid = document.getElementById("disciplineGrid");
  var videoList = document.getElementById("videoList");

  if (videoList) videoList.style.display = "none";

  var keys = Object.keys(DISCIPLINE_ICONS).filter(function (d) { return d !== "通用"; });

  grid.innerHTML = keys.map(function (d) {
    var info = DISCIPLINE_ICONS[d];
    return '<div class="discipline-item" onclick="selectDiscipline(\'' + esc(d) + '\')">'
      + '<div class="discipline-icon-box" style="background:' + info.bg + ';color:' + info.color + '">' + (info.symbol || info.icon) + '</div>'
      + '<div class="discipline-label">' + esc(d) + '</div></div>';
  }).join("");

  // 直接设置 display，绕过 show/hide
  gridCtn.style.display = "block";
  grid.style.display = "grid";
}

function selectDiscipline(d) {
  document.getElementById("disciplineGridContainer").style.display = "none";
  document.getElementById("categoryFilter").style.display = "none";
  document.getElementById("videoList").style.display = "grid";
  document.getElementById("disciplineActiveHeader").style.display = "flex";
  document.getElementById("activeDisciplineName").textContent = d;
  renderVideoList(allVideos.filter(function (v) { return v.discipline === d; }));
}

function backToDisciplineGrid() {
  document.getElementById("disciplineActiveHeader").style.display = "none";
  document.getElementById("categoryFilter").style.display = "none";
  renderDisciplineGrid();
}

// ============================================================
// 数说：发现Feed（动态版）
// ============================================================
async function loadKnowledgeCards(topic) {
  show("loadingArea");
  hide("shushuoFeed");
  var url = "/api/knowledge";
  if (topic) url += "?topic=" + encodeURIComponent(topic);
  try {
    var resp = await fetch(url);
    var result = await resp.json();
    hide("loadingArea");
    if (result.code === 0) {
      allKnowledgeCards = result.data || [];
      var topics = result.topics || [];
      renderShushuoTopics(topics);
      renderShushuoFeed(allKnowledgeCards);
    }
  } catch (e) {
    hide("loadingArea");
  }
}

function renderShushuoTopics(topics) {
  var scrollEl = document.getElementById("topicsScroll");
  scrollEl.innerHTML = ('<span class="topic-pill active" onclick="filterShushuoByTopic(\'\')">全部</span>') +
    topics.map(function (t) {
      return '<span class="topic-pill" onclick="filterShushuoByTopic(\'' + esc(t) + '\')">' + esc(t) + '</span>';
    }).join("");
}

function renderShushuoFeed(cards) {
  show("shushuoFeed");
  updateShushuoHero();
  var feed = document.getElementById("shushuoFeed");
  feed.innerHTML = cards.map(function (c) {
    var isFav = userFavorites.knowledge[c.id] || false;
    return '<div class="feed-card" onclick="openKnowledgeDetail(' + c.id + ')">'
      + '<div class="feed-card-visual" style="background:linear-gradient(135deg, ' + (c.color || "#3B82F6") + ', ' + adjustColor((c.color || "#3B82F6"), 20) + ')">'
      + '<div class="feed-card-icon">' + (c.icon || "📖") + '</div>'
      + '<div class="feed-card-question">' + esc(c.question || "") + '</div>'
      + '<button class="feed-card-fav' + (isFav ? ' active' : '') + '" onclick="event.stopPropagation();toggleFavorite(\'knowledge\',' + c.id + ',\'' + esc(c.title) + '\',\'' + (c.icon || "📖") + '\')"><span>' + (isFav ? '★' : '☆') + '</span></button>'
      + '</div>'
      + '<div class="feed-card-body"><div class="feed-card-title">' + esc(c.title) + '</div>'
      + '<div class="feed-card-desc">' + esc(c.description) + '</div>'
      + '<div class="feed-card-footer"><span class="feed-card-topic">' + (c.topic ? '🏷 ' + esc(c.topic) : '') + '</span><span class="feed-card-time">' + esc(c.read_time) + '</span></div></div></div>';
  }).join("");
}

function filterShushuoByTopic(topic) {
  document.querySelectorAll(".topic-pill").forEach(function (p) {
    var is = p.textContent.trim() === (topic || "全部");
    p.classList.toggle("active", is);
  });
  if (topic) {
    loadKnowledgeCards(topic);
  } else {
    loadKnowledgeCards();
  }
  document.querySelector(".content-area").scrollTo({ top: 0, behavior: "smooth" });
}

function openKnowledgeDetail(cardId) {
  var card = allKnowledgeCards.find(function (c) { return c.id === cardId; });
  if (!card) return;
  setAIContext("knowledge", card.title);
  recordHistory("knowledge", card.id, card.title, card.icon || "📖");
  // 在聊天中询问
  openChat();
  quickAsk(card.title + "是什么？能给我详细讲讲吗？");
}

async function toggleFavorite(type, targetId, title, cover) {
  var isFav = (userFavorites[type] || {})[targetId];
  if (isFav) {
    // 取消收藏
    try {
      await fetch("/api/favorites", {
        method: "DELETE",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ user_id: userId, target_type: type, target_id: targetId })
      });
      if (userFavorites[type]) delete userFavorites[type][targetId];
      showToast("已取消收藏");
    } catch (e) { /* ignore */ }
  } else {
    // 添加收藏
    try {
      await fetch("/api/favorites", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ user_id: userId, target_type: type, target_id: targetId, title: title, cover: cover })
      });
      if (!userFavorites[type]) userFavorites[type] = {};
      userFavorites[type][targetId] = true;
      showToast("已加入收藏");
    } catch (e) { /* ignore */ }
  }
  // 刷新当前视图
  if (currentModule === "知识" && type === "knowledge") {
    renderShushuoFeed(allKnowledgeCards);
  } else if (currentModule === "发现" && type === "video") {
    renderVideoList(allVideos);
  }
}

async function loadUserFavorites() {
  try {
    var resp = await fetch("/api/favorites?user_id=" + userId);
    var result = await resp.json();
    if (result.code === 0) {
      userFavorites = { video: {}, knowledge: {} };
      (result.data || []).forEach(function (f) {
        if (!userFavorites[f.target_type]) userFavorites[f.target_type] = {};
        userFavorites[f.target_type][f.target_id] = true;
      });
    }
  } catch (e) { userFavorites = { video: {}, knowledge: {} }; }
}

async function recordHistory(type, targetId, title, cover) {
  try {
    await fetch("/api/history", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ user_id: userId, target_type: type, target_id: targetId, title: title, cover: cover })
    });
  } catch (e) { /* ignore */ }
}

function showToast(message) {
  var toast = document.getElementById("globalToast");
  if (!toast) {
    toast = document.createElement("div");
    toast.id = "globalToast";
    toast.style.cssText = "position:fixed;bottom:100px;left:50%;transform:translateX(-50%);background:rgba(0,0,0,0.78);color:#fff;padding:8px 20px;border-radius:20px;font-size:13px;z-index:9999;pointer-events:none;transition:opacity 0.3s;opacity:0;";
    document.body.appendChild(toast);
  }
  toast.textContent = message;
  toast.style.opacity = "1";
  clearTimeout(toast._timer);
  toast._timer = setTimeout(function () { toast.style.opacity = "0"; }, 1500);
}

// ============================================================
// 个人中心
// ============================================================
async function loadProfileData() {
  try {
    var [statsResp, profileResp] = await Promise.all([
      fetch("/api/user/stats?user_id=" + userId),
      fetch("/api/user/profile?user_id=" + userId)
    ]);
    var stats = (await statsResp.json()).data || { watch_count: 0, favorite_count: 0 };
    var profile = (await profileResp.json()).data || {};

    // 更新统计数据
    var statNums = document.querySelectorAll(".profile-stat-num");
    if (statNums.length >= 3) {
      statNums[0].textContent = stats.watch_count || 0;
      statNums[1].textContent = stats.favorite_count || 0;
      statNums[2].textContent = "2"; // 剧情存档数保持固定
    }
    // 更新头像和昵称
    var avatar = document.getElementById("profileAvatar");
    if (avatar) {
      avatar.textContent = (profile.avatar_emoji || userAvatar);
      avatar.style.background = (profile.avatar_color || userAvatarColor);
      userAvatar = profile.avatar_emoji || userAvatar;
      userAvatarColor = profile.avatar_color || userAvatarColor;
    }

    var nameEl = document.getElementById("profileName");
    if (nameEl) nameEl.textContent = (profile.nickname || "游客用户");

    var idEl = document.getElementById("profileId");
    if (idEl) idEl.textContent = "ID: " + userId.substring(0, 14);
  } catch (e) { /* ignore */ }
}

function showFavorites() {
  loadFavoritesList();
}

async function loadFavoritesList() {
  showProfileListPanel("我的收藏");
  var panel = document.getElementById("profileListContent");
  panel.innerHTML = '<div class="loading" style="padding:40px"><div class="spinner"></div></div>';
  try {
    var resp = await fetch("/api/favorites?user_id=" + userId);
    var result = await resp.json();
    var items = result.data || [];
    if (items.length === 0) {
      panel.innerHTML = '<div class="empty-state"><div class="icon">☆</div><h3>还没有收藏</h3><p>去发现有趣的内容吧</p></div>';
    } else {
      panel.innerHTML = items.map(function (f) {
        return '<div class="history-item">'
          + '<div class="hi-icon" style="background:var(--accent-light)">' + (f.cover || "◎") + '</div>'
          + '<div class="hi-info"><div class="hi-title">' + esc(f.title) + '</div><div class="hi-meta">' + f.target_type + ' · ' + (f.created_at || "").substring(0, 10) + '</div></div>'
          + '<button class="hi-del" onclick="event.stopPropagation();removeFavorite(\'' + f.target_type + '\',' + f.target_id + ')">✕</button>'
          + '</div>';
      }).join("");
    }
  } catch (e) {
    panel.innerHTML = '<div class="empty-state"><div class="icon">⚠️</div><h3>加载失败</h3></div>';
  }
}

function showHistory() {
  loadHistoryList();
}

async function loadHistoryList() {
  showProfileListPanel("观看记录");
  var panel = document.getElementById("profileListContent");
  panel.innerHTML = '<div class="loading" style="padding:40px"><div class="spinner"></div></div>';
  try {
    var resp = await fetch("/api/history?user_id=" + userId + "&limit=30");
    var result = await resp.json();
    var items = result.data || [];
    if (items.length === 0) {
      panel.innerHTML = '<div class="empty-state"><div class="icon">👀</div><h3>还没有观看记录</h3><p>去看看视频和知识卡片吧</p></div>';
    } else {
      panel.innerHTML = items.map(function (h) {
        return '<div class="history-item">'
          + '<div class="hi-icon" style="background:var(--accent-light)">' + (h.cover || "◎") + '</div>'
          + '<div class="hi-info"><div class="hi-title">' + esc(h.title) + '</div><div class="hi-meta">' + h.target_type + ' · ' + (h.watched_at || "").substring(0, 16) + '</div></div>'
          + '</div>';
      }).join("");
    }
  } catch (e) {
    panel.innerHTML = '<div class="empty-state"><div class="icon">⚠️</div><h3>加载失败</h3></div>';
  }
}

function showProfileListPanel(title) {
  var panel = document.getElementById("profileListPanel");
  var titleEl = document.getElementById("profileListTitle");
  if (titleEl) titleEl.textContent = title;
  if (panel) panel.style.display = "block";
  // 隐藏个人中心其他内容
  var profilePage = document.getElementById("profilePage");
  if (profilePage) profilePage.style.display = "none";
}

function hideProfileListPanel() {
  var panel = document.getElementById("profileListPanel");
  if (panel) panel.innerHTML = "";
  if (panel) panel.style.display = "none";
  var profilePage = document.getElementById("profilePage");
  if (profilePage) profilePage.style.display = "block";
  loadProfileData();
}

async function removeFavorite(type, targetId) {
  await toggleFavorite(type, targetId, "", "");
  loadFavoritesList();
  loadProfileData();
}

function editNickname() {
  var name = prompt("输入新昵称：", document.querySelector(".profile-name").textContent);
  if (name && name.trim()) {
    var trimmed = name.trim().substring(0, 12);
    fetch("/api/user/profile", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ user_id: userId, nickname: trimmed })
    }).then(function () {
      var el = document.querySelector(".profile-name");
      if (el) el.textContent = trimmed;
      showToast("昵称已更新");
    });
  }
}
function updateShushuoHero() {
  document.getElementById("heroTitle").textContent = "数学不是公式，是你理解世界的方式";
  document.getElementById("heroDesc").textContent = "每天一个数学概念，让思维变得更清晰";
  var el = document.querySelector(".hero-stats");
  if (el) el.innerHTML = "<span>📖 " + allKnowledgeCards.length + "个概念</span><span>⏱ 阅读约" + (allKnowledgeCards.length * 5) + "分钟</span>";
}
function adjustColor(hex, amount) {
  var num = parseInt(hex.replace("#",""), 16);
  var r = Math.min(255, (num >> 16) + amount);
  var g = Math.min(255, ((num >> 8) & 0x00FF) + amount);
  var b = Math.min(255, (num & 0x0000FF) + amount);
  return "#" + ((r << 16) | (g << 8) | b).toString(16).padStart(6, "0");
}

// ============================================================
// 视频加载与渲染
// ============================================================
async function loadVideos(module, discipline) {
  show("loadingArea");
  document.getElementById("videoList").innerHTML = "";
  var url = "/api/videos?module=" + encodeURIComponent(module);
  if (discipline && discipline !== "all") url += "&discipline=" + encodeURIComponent(discipline);
  try {
    var resp = await fetch(url);
    var result = await resp.json();
    hide("loadingArea");
    allVideos = result.data || [];
    if (allVideos.length > 0) {
      renderVideoList(allVideos);
    } else {
      document.getElementById("videoList").innerHTML = '<div class="empty-state"><div class="icon">📭</div><h3>暂无相关视频</h3><p>后续将由各学院志愿者上传</p></div>';
    }
  } catch (e) {
    hide("loadingArea");
    document.getElementById("videoList").innerHTML = '<div class="empty-state"><div class="icon">⚠️</div><h3>加载失败</h3><p>请稍后重试</p></div>';
  }
}

function renderVideoList(videos) {
  var list = document.getElementById("videoList");
  if (!videos || videos.length === 0) { list.innerHTML = '<div class="empty-state"><div class="icon">📭</div><h3>暂无相关视频</h3></div>'; return; }
  list.style.display = "grid";
  list.innerHTML = videos.map(function (v) {
    var avatarColor = AVATAR_COLORS[hashStr(v.author || "") % AVATAR_COLORS.length];
    var initial = (v.author || "?").replace(/学院|·|同学|系/g, "").slice(0, 1);
    var dur = v.duration_min || 5;
    var icon = v.discipline_icon && v.discipline_icon !== "·" ? v.discipline_icon : "◎";
    var coverBg = "hsl(" + (hashStr(v.discipline || "") % 360) + ", 40%, 90%)";
    var isFav = userFavorites.video[v.id] || false;
    return '<div class="video-card" onclick="openVideo(\'' + v.id + '\',\'' + esc(v.title) + '\',\'' + esc(v.description) + '\',\'' + esc(v.video_url) + '\',\'' + esc(v.author) + '\',\'' + esc(v.sub_category || v.discipline) + '\')">'
      + '<div class="vc-cover" style="background:' + coverBg + '"><div class="vc-cover-placeholder">' + icon + '</div><div class="vc-duration">' + dur + ':00</div>'
      + '<button class="vc-fav' + (isFav ? ' active' : '') + '" onclick="event.stopPropagation();toggleFavorite(\'video\',' + v.id + ',\'' + esc(v.title) + '\',\'' + icon + '\')"><span>' + (isFav ? '★' : '☆') + '</span></button>'
      + '</div>'
      + '<div class="vc-content"><div class="vc-title">' + esc(v.title) + '</div>'
      + '<div class="vc-info-row"><span class="vc-author-avatar" style="background:' + avatarColor + '">' + initial + '</span>'
      + '<span class="vc-author-name">' + esc(v.author) + '</span>'
      + '<div class="vc-stat"><span>👁️</span><span>' + ((hashStr(v.title) % 1000) + 100) + '</span></div></div></div></div>';
  }).join("");
}

function openVideo(id, title, desc, url, author, cat) {
  currentVideoUrl = url;
  currentVideoTitle = title;
  document.getElementById("videoModalTitle").textContent = title;
  document.getElementById("videoModalDesc").textContent = desc;
  document.getElementById("videoModalAuthor").innerHTML = author;
  document.getElementById("videoModalCat").innerHTML = cat || "通用";
  document.getElementById("videoLinkBtn").style.display = url ? "block" : "none";
  document.getElementById("videoPlaceholderText").textContent = url ? "点击下方按钮查看完整课程视频" : "视频链接待补充";
  // 收藏状态
  var favBtn = document.getElementById("videoModalFav");
  var vid = parseInt(id);
  favBtn.textContent = (userFavorites.video[vid]) ? "★ 已收藏" : "☆ 收藏";
  favBtn.dataset.vid = vid;
  document.getElementById("videoModal").classList.add("show");
  // AI上下文
  setAIContext("video", title);
  // 记录观看历史
  recordHistory("video", vid, title, cat || "◎");
}
function closeVideo() { document.getElementById("videoModal").classList.remove("show"); }
function openVideoLink() { if (currentVideoUrl) window.open(currentVideoUrl, "_blank"); }
function toggleVideoFav() {
  var btn = document.getElementById("videoModalFav");
  var vid = parseInt(btn.dataset.vid);
  var title = currentVideoTitle;
  toggleFavorite("video", vid, title, "◎");
  btn.textContent = (userFavorites.video[vid]) ? "★ 已收藏" : "☆ 收藏";
}

// ============================================================
// 视觉小说引擎 (数思) — 唯一引擎
// ============================================================
async function loadStoryData() {
  try {
    var resp = await fetch("/api/story");
    var result = await resp.json();
    if (result.code === 0) storyData = result.data;
  } catch (e) { console.error("故事数据加载失败:", e); }
}

function loadVNSave() {
  try { var s = localStorage.getItem("mathAppVNSave"); if (s) vnState = JSON.parse(s); } catch (e) {}
}

function saveVNState() {
  localStorage.setItem("mathAppVNSave", JSON.stringify(vnState));
  alert("进度已存档！");
}

function resetVNSave() {
  var startNode = (storyData && storyData.start_node) || "ch1_start";
  vnState = { currentNode: startNode, stats: { shuli: 10, xiayi: 10, xinzhi: 10 }, favorability: { "欧阳侠": 0, "Eddie侠": 0, "厚朴侠": 0, "三力思侠": 0, "溜溜侠": 0, "孔锝侠": 0, "星侠": 0, "甲亢侠": 0 } };
  localStorage.removeItem("mathAppVNSave");
  navigateVN(startNode);
}

function applyVNStats(effects) {
  if (!effects) return;
  if (effects.shuli) vnState.stats.shuli = (vnState.stats.shuli || 10) + effects.shuli;
  if (effects.xiayi) vnState.stats.xiayi = (vnState.stats.xiayi || 10) + effects.xiayi;
  if (effects.xinzhi) vnState.stats.xinzhi = (vnState.stats.xinzhi || 10) + effects.xinzhi;
  // 兼容旧属性名
  if (effects.intelligence !== undefined) vnState.stats.shuli = (vnState.stats.shuli || 10) + effects.intelligence;
  if (effects.charm !== undefined) vnState.stats.xiayi = (vnState.stats.xiayi || 10) + effects.charm;
  if (effects.stress !== undefined) vnState.stats.xinzhi = (vnState.stats.xinzhi || 10) - effects.stress;
  // 好感度
  if (effects.favorability) {
    if (!vnState.favorability) vnState.favorability = {};
    for (var char in effects.favorability) {
      vnState.favorability[char] = (vnState.favorability[char] || 0) + effects.favorability[char];
    }
  }
}

function startStoryEngine() {
  setAIContext("story", "葫芦侠行");
  var startNode = (storyData && storyData.start_node) || "ch1_start";
  // 如果存档中有旧节点但新JSON里不存在，回退到起始节点
  var savedNode = vnState.currentNode;
  if (savedNode && storyData && storyData.nodes && storyData.nodes[savedNode]) {
    navigateVN(savedNode);
  } else {
    vnState.currentNode = startNode;
    navigateVN(startNode);
  }
}

function navigateVN(nodeId) {
  if (!storyData || !storyData.nodes || !storyData.nodes[nodeId]) {
    document.getElementById("storyContainer").innerHTML = '<div class="empty-state"><h3>剧情加载失败</h3><p>节点未找到</p></div>';
    return;
  }
  var node = storyData.nodes[nodeId];
  vnState.currentNode = nodeId;

  var container = document.getElementById("storyContainer");
  if (!container) return;
  container.style.display = "block";
  container.scrollIntoView({ behavior: "smooth" });

  // 构建完整 HTML
  var ch = detectChapter(nodeId);
  var chNames = ["","初入江湖","西部教习","数理论道","脑学初创","报国之路","暗流涌动","陋室修炼","真相之门"];
  var chName = chNames[ch] || "";

  var html = '';
  html += '<div class="vn-header">';
  html += '<div class="vn-chapter-badge">第' + ch + '章 · ' + chName + '</div>';
  html += '<div class="vn-stats">';
  html += '<span class="stat-badge" style="background:#A0B9D8">📐 数理根基: ' + (vnState.stats.shuli || 10) + '</span>';
  html += '<span class="stat-badge" style="background:#DAB8B0">⚔️ 侠义声望: ' + (vnState.stats.xiayi || 10) + '</span>';
  html += '<span class="stat-badge" style="background:#B8D0C9">🧘 心志韧性: ' + (vnState.stats.xinzhi || 10) + '</span>';
  html += '</div>';
  html += '<div class="vn-actions">';
  html += '<button onclick="openSavePanel()" class="vn-btn">💾 存档</button>';
  html += '<button onclick="openGallery()" class="vn-btn">🏆 图鉴</button>';
  html += '<button onclick="resetVNSave()" class="vn-btn outline">🔄 重置</button>';
  html += '</div></div>';

  // 场景背景 + 立绘
  var bgKey = CHAPTER_BG[ch] || "紫金山";
  var bgUrl = BG_MAP[bgKey] || "";
  if (node.background) bgUrl = BG_MAP[node.background] || node.background || bgUrl;

  // 立绘：优先 node.sprite，其次从 SPRITE_MAP 查找角色名
  var spriteUrl = "";
  var characterName = node.character || "";
  if (node.sprite) {
    spriteUrl = node.sprite;
  } else if (characterName) {
    // 匹配 SPRITE_MAP 中的角色（支持多角色用 & 分隔）
    var names = characterName.split("&");
    for (var ni = 0; ni < names.length; ni++) {
      var n = names[ni].trim();
      if (SPRITE_MAP[n]) { spriteUrl = SPRITE_MAP[n]; break; }
    }
  }

  html += '<div class="story-scene" id="sceneBox">';
  if (bgUrl) html += '<img src="' + bgUrl + '" class="vn-background" alt="" onerror="this.style.display=\'none\'" />';
  if (spriteUrl) html += '<img src="' + spriteUrl + '" class="vn-sprite" alt="' + esc(characterName) + '" onerror="this.style.display=\'none\'" />';
  html += '<div class="story-text-box">';
  if (characterName) html += '<div class="story-character">' + esc(characterName) + '</div>';
  html += '<div class="story-text" id="sceneText"></div>';
  html += '</div></div>';

  html += '<div class="story-choices" id="choicesArea"></div>';

  container.innerHTML = html;

  // 打字机效果
  var sceneText = document.getElementById("sceneText");
  var text = node.scene || "";
  var i = 0;
  function typeNext() {
    if (i < text.length) {
      sceneText.textContent += text.charAt(i);
      i++;
      setTimeout(typeNext, 25);
    } else {
      buildChoiceButtons(node.choices || []);
    }
  }
  sceneText.textContent = "";
  typeNext();
}

function buildChoiceButtons(choices) {
  var area = document.getElementById("choicesArea");
  if (!area) return;
  area.innerHTML = "";

  choices.forEach(function (c, idx) {
    var btn = document.createElement("button");
    btn.className = "choice-btn";
    btn.style.animationDelay = (idx * 0.1) + "s";

    // 效果文本
    var effectsHTML = "";
    if (c.effects) {
      var parts = [];
      if (c.effects.shuli) parts.push("数理根基" + (c.effects.shuli > 0 ? "+" : "") + c.effects.shuli);
      if (c.effects.xiayi) parts.push("侠义声望" + (c.effects.xiayi > 0 ? "+" : "") + c.effects.xiayi);
      if (c.effects.xinzhi) parts.push("心志韧性" + (c.effects.xinzhi > 0 ? "+" : "") + c.effects.xinzhi);
      // 兼容旧属性
      if (c.effects.intelligence) parts.push("数理根基" + (c.effects.intelligence > 0 ? "+" : "") + c.effects.intelligence);
      if (c.effects.charm) parts.push("侠义声望" + (c.effects.charm > 0 ? "+" : "") + c.effects.charm);
      if (c.effects.stress) parts.push("心志韧性" + (c.effects.stress > 0 ? "+" : "") + c.effects.stress);
      if (c.effects.favorability) {
        for (var char in c.effects.favorability) {
          var val = c.effects.favorability[char];
          parts.push(char + "好感" + (val > 0 ? "+" : "") + val);
        }
      }
      if (parts.length > 0) effectsHTML = '<span class="choice-hint">[' + parts.join(" | ") + ']</span>';
    }

    btn.innerHTML = '<span class="choice-text">' + esc(c.text) + '</span>' + effectsHTML;

    btn.onclick = (function (nextNode, effects) {
      return function () {
        applyVNStats(effects);
        navigateVN(nextNode);
      };
    })(c.next, c.effects);

    area.appendChild(btn);
  });
}

// ============================================================
// AI 聊天
// ============================================================
function openChat() { document.getElementById("chatOverlay").classList.add("show"); document.getElementById("chatMessages").scrollTop = document.getElementById("chatMessages").scrollHeight; }
function closeChat() { document.getElementById("chatOverlay").classList.remove("show"); }
function quickAsk(q) { document.getElementById("chatInput").value = q; sendMessage(); }

async function sendMessage() {
  var input = document.getElementById("chatInput");
  var message = input.value.trim();
  if (!message) return;
  appendChat("user", message);
  input.value = "";
  var typingId = appendTyping();
  try {
    var resp = await fetch("/api/chat", {
      method: "POST", headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ message: message, context: currentContext })
    });
    var result = await resp.json();
    removeTyping(typingId);
    appendChat("assistant", result.code === 0 ? result.reply : "抱歉，AI 助教暂时无法回复。");
  } catch (e) {
    removeTyping(typingId);
    appendChat("assistant", "网络异常，请稍后重试。");
  }
}

function setAIContext(type, title) {
  currentContext = { type: type, title: title || "" };
  var hint = document.getElementById("chatContextHint");
  if (!hint) {
    hint = document.createElement("div");
    hint.id = "chatContextHint";
    hint.style.cssText = "padding:6px 12px;font-size:11px;color:var(--accent);background:var(--accent-light);text-align:center;";
    var chatPanel = document.querySelector(".chat-panel");
    var header = document.querySelector(".chat-header");
    if (header && chatPanel) chatPanel.insertBefore(hint, header.nextSibling);
  }
  hint.textContent = currentContext.title ? "📍 当前：" + currentContext.title : "";
  hint.style.display = currentContext.title ? "block" : "none";
}
function appendChat(role, text) {
  var container = document.getElementById("chatMessages");
  var div = document.createElement("div");
  div.className = "chat-msg " + role;
  div.innerHTML = '<div class="msg-bubble">' + esc(text) + "</div>";
  container.appendChild(div);
  container.scrollTop = container.scrollHeight;
}
function appendTyping() {
  var container = document.getElementById("chatMessages");
  var div = document.createElement("div");
  div.className = "chat-msg assistant typing-msg";
  div.innerHTML = '<div class="msg-bubble"><div class="typing-indicator"><span></span><span></span><span></span></div></div>';
  container.appendChild(div);
  container.scrollTop = container.scrollHeight;
  return div;
}
function removeTyping(el) { if (el && el.parentNode) el.remove(); }

// ============================================================
// 数学人生子标签切换
// ============================================================
function switchLifeTab(tab) {
  currentLifeTab = tab;
  document.querySelectorAll(".life-sub-btn").forEach(function (b) { b.classList.toggle("active", b.dataset.life === tab); });
  if (tab === "simulator") {
    hide("storyContainer");
    show("simulatorContainer");
    setAIContext("simulator", "数学专业模拟器");
    startSimulator();
  } else {
    hide("simulatorContainer");
    show("storyContainer");
    setAIContext("story", "葫芦侠行");
    startStoryEngine();
  }
}

// ============================================================
// 数学专业模拟器引擎
// ============================================================
async function loadSimulatorData() {
  try {
    var resp = await fetch("/api/simulator");
    var result = await resp.json();
    if (result.code === 0) simulatorData = result.data;
  } catch (e) { console.error("模拟器数据加载失败:", e); }
}

async function loadSimSave() {
  try {
    var resp = await fetch("/api/simulator/save?user_id=" + userId);
    var result = await resp.json();
    if (result.code === 0 && result.data) {
      simState = result.data;
    }
  } catch (e) {}
}

function saveSimState() {
  simState.history.push(simState.currentNode);
  fetch("/api/simulator/save", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ user_id: userId, state: simState })
  }).catch(function () {});
}

function resetSimulator() {
  simState = { currentNode: (simulatorData && simulatorData.start_node) || "y1_welcome", stats: { gpa: 10, interest: 10, skill: 10 }, history: [] };
  saveSimState();
  navigateSimulator(simState.currentNode);
}

async function startSimulator() {
  if (!simulatorData) await loadSimulatorData();
  await loadSimSave();
  if (!simState.currentNode || !simulatorData || !simulatorData.nodes || !simulatorData.nodes[simState.currentNode]) {
    resetSimulator();
  } else {
    navigateSimulator(simState.currentNode);
  }
}

function navigateSimulator(nodeId) {
  if (!simulatorData || !simulatorData.nodes || !simulatorData.nodes[nodeId]) {
    document.getElementById("simulatorContainer").innerHTML = '<div class="empty-state"><h3>剧情加载失败</h3></div>';
    return;
  }
  var node = simulatorData.nodes[nodeId];
  simState.currentNode = nodeId;
  saveSimState();

  var container = document.getElementById("simulatorContainer");
  if (!container) return;
  container.style.display = "block";

  // 如果遇到结局节点
  if (node.end) {
    showEnding(node);
    return;
  }

  container.scrollIntoView({ behavior: "smooth" });
  var ch = (node.year || 1);
  var stage = node.stage || "";

  var html = '';
  html += '<div class="sim-header">';
  html += '<div class="sim-year-badge" style="background:' + (["", "#3B82F6", "#8B5CF6", "#F97316", "#10B981"][ch]) + '">🎓 大' + ["", "一", "二", "三", "四"][ch] + ' · ' + stage + '</div>';
  html += '<div class="sim-stats">';
  Object.keys(simState.stats).forEach(function (k) {
    html += '<span class="sim-stat-bar" style="background:' + SIM_STAT_COLORS[k] + '">' + SIM_STAT_EMOJIS[k] + ' ' + SIM_STAT_LABELS[k] + ': ' + simState.stats[k] + '</span>';
  });
  html += '</div>';
  html += '<div class="sim-actions-top">';
  html += '<button onclick="openSavePanel()" class="vn-btn">💾 存档</button>';
  html += '<button onclick="openGallery()" class="vn-btn">🏆 图鉴</button>';
  html += '<button onclick="resetSimulator()" class="vn-btn outline">🔄 重新开始</button>';
  html += '<button onclick="setAIContext(\'simulator\',\'数学专业模拟器\');openChat()" class="vn-btn">? 问学长</button>';
  html += '</div></div>';

  html += '<div class="sim-scene">';
  if (node.character) html += '<div class="sim-character">' + esc(node.character) + '</div>';
  html += '<div class="sim-text" id="simText"></div>';
  html += '</div>';

  html += '<div class="sim-choices" id="simChoicesArea"></div>';

  container.innerHTML = html;

  // 打字机效果
  var textEl = document.getElementById("simText");
  var text = node.scene || "";
  var i = 0;
  function typeNext() {
    if (i < text.length) {
      textEl.textContent += text.charAt(i);
      i++;
      setTimeout(typeNext, 20);
    } else {
      buildSimChoices(node.choices || []);
    }
  }
  textEl.textContent = "";
  typeNext();
}

function buildSimChoices(choices) {
  var area = document.getElementById("simChoicesArea");
  if (!area) return;
  area.innerHTML = "";

  choices.forEach(function (c, idx) {
    var btn = document.createElement("button");
    btn.className = "sim-choice-btn";
    btn.style.animationDelay = (idx * 0.1) + "s";

    var effectsHTML = "";
    if (c.effects) {
      var parts = [];
      if (c.effects.gpa) parts.push((c.effects.gpa > 0 ? "+" : "") + c.effects.gpa + " 绩点");
      if (c.effects.interest) parts.push((c.effects.interest > 0 ? "+" : "") + c.effects.interest + " 兴趣");
      if (c.effects.skill) parts.push((c.effects.skill > 0 ? "+" : "") + c.effects.skill + " 技能");
      if (parts.length > 0) effectsHTML = '<span class="sim-hint">[' + parts.join(" | ") + ']</span>';
    }
    if (c.hint) effectsHTML += '<span class="sim-hint-tip">💡 ' + esc(c.hint) + '</span>';

    btn.innerHTML = '<span class="sim-choice-text">' + esc(c.text) + '</span>' + effectsHTML;

    btn.onclick = (function (nextNode, effects) {
      return function () {
        if (effects) applySimStats(effects);
        navigateSimulator(nextNode);
      };
    })(c.next, c.effects);

    area.appendChild(btn);
  });
}

function applySimStats(effects) {
  if (!effects) return;
  if (effects.gpa) simState.stats.gpa = Math.max(0, Math.min(30, (simState.stats.gpa || 10) + effects.gpa));
  if (effects.interest) simState.stats.interest = Math.max(0, Math.min(30, (simState.stats.interest || 10) + effects.interest));
  if (effects.skill) simState.stats.skill = Math.max(0, Math.min(30, (simState.stats.skill || 10) + effects.skill));
}

function showEnding(node) {
  document.getElementById("endingOverlay").classList.add("show");
  var badges = { pure_math: "🔮", ai: "🤖", finance: "💰", research: "🔬", startup: "🚀", teacher: "📚", engineer: "⚙️", security: "🔒" };
  document.getElementById("endingBadge").textContent = badges[node.ending_type] || "🎓";
  document.getElementById("endingType").textContent = node.character || "结局";
  document.getElementById("endingScene").textContent = node.scene || "";
  document.getElementById("endingTitle").textContent = node.stage || "";

  // 记录解锁结局
  if (node.ending_type) unlockEnding(node.ending_type);

  // 统计数据
  var statsHtml = '<div style="display:flex;gap:12px;justify-content:center;flex-wrap:wrap;">';
  Object.keys(simState.stats).forEach(function (k) {
    statsHtml += '<div style="text-align:center"><div style="font-size:22px;font-weight:800;color:' + SIM_STAT_COLORS[k] + '">' + simState.stats[k] + '</div><div style="font-size:11px;color:#999">' + SIM_STAT_LABELS[k] + '</div></div>';
  });
  statsHtml += '</div>';
  document.getElementById("endingStats").innerHTML = statsHtml;
}

function closeEnding() { document.getElementById("endingOverlay").classList.remove("show"); }

// ============================================================
// 头像选择器
// ============================================================
var PICKER_EMOJIS = ["π","∑","∫","∞","Δ","∂","α","β","γ","λ","μ","σ","Ω","∇","⊥","⊕","⊗","≈","±","÷","√","∏","∃","∀","∈","⊂","∪","∩","ℵ","∮"];

function openAvatarPicker() {
  document.getElementById("avatarPickerOverlay").classList.add("show");
  // 渲染emoji网格
  var grid = document.getElementById("pickerEmojiGrid");
  grid.innerHTML = PICKER_EMOJIS.map(function (e) {
    var selected = e === userAvatar ? ' style="border-color:var(--accent);background:var(--accent-light)"' : "";
    return '<button class="picker-emoji-btn"' + selected + ' onclick="selectAvatar(\'' + e + '\')">' + e + '</button>';
  }).join("");
  // 渲染颜色行
  var colors = ["#A0B9D8","#B8D0C9","#DAB8B0","#C5CAD0","#B8A99A","#9BB8AE","#ACAEC5","#C4B0B8"];
  var colorRow = document.getElementById("pickerColorRow");
  colorRow.innerHTML = colors.map(function (c) {
    var selected = c === userAvatarColor ? ' style="box-shadow:0 0 0 4px ' + c + '44"' : "";
    return '<button class="picker-color-btn" style="background:' + c + '"' + selected + ' onclick="selectAvatarColor(\'' + c + '\')"></button>';
  }).join("");
}

function selectAvatar(emoji) {
  userAvatar = emoji;
  localStorage.setItem("mathAppAvatar", emoji);
  openAvatarPicker(); // refresh grid
  updateAvatarUI();
  fetch("/api/user/profile", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ user_id: userId, avatar_emoji: emoji })
  });
}

function selectAvatarColor(color) {
  userAvatarColor = color;
  localStorage.setItem("mathAppAvatarColor", color);
  openAvatarPicker();
  updateAvatarUI();
  fetch("/api/user/profile", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ user_id: userId, avatar_color: color })
  });
}

function updateAvatarUI() {
  var av = document.getElementById("profileAvatar");
  if (av) { av.textContent = userAvatar; av.style.background = userAvatarColor; }
  var brandIcon = document.querySelector(".brand-icon-sm");
  if (brandIcon) { brandIcon.textContent = "葫"; brandIcon.style.background = userAvatarColor; }
}

function closeAvatarPicker(e) {
  if (e && e.target !== document.getElementById("avatarPickerOverlay")) return;
  document.getElementById("avatarPickerOverlay").classList.remove("show");
}

// ---- 工具函数 ----
function esc(text) {
  if (!text) return "";
  var div = document.createElement("div");
  div.textContent = text;
  return div.innerHTML;
}
function hashStr(str) {
  var hash = 0;
  for (var i = 0; i < str.length; i++) { hash = ((hash << 5) - hash) + str.charCodeAt(i); hash |= 0; }
  return Math.abs(hash);
}

// ============================================================
// 剧本广场 (Script Square)
// ============================================================
function renderScriptSquare() {
  try {
    // 渲染精选剧本大卡
    var heroCards = document.getElementById("ssHeroCards");
    if (!heroCards) return;
    heroCards.innerHTML = SCRIPTS.map(function (s) {
      var statusLabel = s.status === "beta" ? "开发中 · 抢先体验" : "已上线";
      return '<div class="ss-hero-card ' + s.cssClass + '" onclick="enterScript(\'' + s.id + '\')">'
        + '<div class="ss-hero-bg"></div>'
        + '<div class="ss-hero-content">'
        + '<div class="ss-hero-icon">' + s.icon + '</div>'
        + '<div class="ss-hero-name">' + esc(s.name) + '</div>'
        + '<div class="ss-hero-desc">' + esc(s.desc) + '</div>'
        + '<div class="ss-hero-meta"><span>' + s.meta + '</span><span class="ss-hero-status">' + statusLabel + '</span></div>'
        + '</div></div>';
    }).join("");

    // 渲染即将上线小卡
    var comingCards = document.getElementById("ssComingCards");
    if (comingCards) {
      comingCards.innerHTML = COMING_SOON.map(function (c) {
        return '<div class="ss-coming-card">'
          + '<div class="ss-coming-card-bg" style="background:' + c.color + '20">' + c.icon + '</div>'
          + '<div class="ss-coming-card-body">'
          + '<div class="ss-coming-card-name">' + esc(c.name) + '</div>'
          + '<div class="ss-coming-card-desc">' + esc(c.desc) + '</div>'
          + '<span class="ss-coming-badge">敬请期待</span>'
          + '</div></div>';
      }).join("");
    }

    // 检查是否有可继续的存档
    updateContinueCard();
  } catch (e) {
    console.error("剧本广场渲染失败:", e);
  }
}

function updateContinueCard() {
  try {
    var continueEl = document.getElementById("ssContinue");
    if (!continueEl) return;
    var vnSavedNode = vnState.currentNode;
    var simSavedNode = simState.currentNode;

    // 优先显示葫芦侠行存档，其次模拟器存档
    if (vnSavedNode && storyData && storyData.nodes && storyData.nodes[vnSavedNode]) {
      var ch = detectChapter(vnSavedNode);
      var chNames = ["","初入江湖","西部教习","数理论道","脑学初创","报国之路","暗流涌动","陋室修炼","真相之门"];
      document.getElementById("ssContinueTitle").textContent = "葫芦侠行 · 第" + ch + "章";
      document.getElementById("ssContinueSub").textContent = chNames[ch] || "";
      document.getElementById("ssContinueCard").onclick = function () { enterScript("huluxiaxing"); };
      continueEl.style.display = "block";
    } else if (simSavedNode && simulatorData && simulatorData.nodes && simulatorData.nodes[simSavedNode]) {
      var node = simulatorData.nodes[simSavedNode];
      document.getElementById("ssContinueTitle").textContent = "数学专业模拟器";
      document.getElementById("ssContinueSub").textContent = (node.stage || "") + (node.year ? " · 大" + ["","一","二","三","四"][node.year] : "");
      document.getElementById("ssContinueCard").onclick = function () { enterScript("simulator"); };
      continueEl.style.display = "block";
    } else {
      continueEl.style.display = "none";
    }
  } catch (e) {
    console.error("继续卡片更新失败:", e);
  }
}

function enterScript(scriptId) {
  hide("scriptSquare");

  if (scriptId === "huluxiaxing") {
    setAIContext("story", "葫芦侠行");
    hide("simulatorContainer");
    show("lifeSubNav");
    show("storyContainer");
    var btns = document.querySelectorAll(".life-sub-btn");
    btns.forEach(function (b) {
      b.classList.toggle("active", b.dataset.life === "story");
    });
    startStoryEngine();
  } else if (scriptId === "simulator") {
    setAIContext("simulator", "数学专业模拟器");
    hide("storyContainer");
    show("lifeSubNav");
    show("simulatorContainer");
    var btns = document.querySelectorAll(".life-sub-btn");
    btns.forEach(function (b) {
      b.classList.toggle("active", b.dataset.life === "simulator");
    });
    startSimulator();
  } else if (scriptId === "mathbti") {
    // 打开 MathBTI 独立页面
    window.location.href = "/mathbti";
  }
}

function backToSquare() {
  hide("lifeSubNav");
  hide("storyContainer");
  hide("simulatorContainer");
  show("scriptSquare");
  renderScriptSquare();
  document.querySelector(".content-area").scrollTo({ top: 0, behavior: "smooth" });
}

// ============================================================
// 存档系统 (Save System — v8.3)
// ============================================================
function getSaveKey(scriptId) { return "mathAppSave_" + scriptId; }

function loadAllSaves(scriptId) {
  try {
    var raw = localStorage.getItem(getSaveKey(scriptId));
    return raw ? JSON.parse(raw) : [];
  } catch (e) { return []; }
}

function saveAllSaves(scriptId, saves) {
  localStorage.setItem(getSaveKey(scriptId), JSON.stringify(saves));
}

function openSavePanel() {
  var scriptId = (currentLifeTab === "simulator") ? "simulator" : "huluxiaxing";
  document.getElementById("savePanelOverlay").classList.add("show");
  renderSaveSlots(scriptId);
}

function closeSavePanel() {
  document.getElementById("savePanelOverlay").classList.remove("show");
}

function renderSaveSlots(scriptId) {
  var slots = document.getElementById("saveSlots");
  var saves = loadAllSaves(scriptId);
  var MAX_SLOTS = 5;

  var html = "";
  for (var i = 0; i < MAX_SLOTS; i++) {
    var s = saves[i];
    if (s) {
      // 已使用的槽位
      var icon = scriptId === "simulator" ? "🎓" : "📖";
      var name = (scriptId === "simulator") ? "数学专业模拟器" : "葫芦侠行";
      var meta = s.chapter || "";
      var date = s.savedAt ? s.savedAt.substring(0, 16).replace("T", " ") : "";
      html += '<div class="save-slot active" onclick="loadFromSlot(\'' + scriptId + '\',' + i + ')">';
      html += '<div class="save-slot-icon" style="background:' + (scriptId === "simulator" ? "var(--morandi-pink-light)" : "var(--morandi-green-light)") + '">' + icon + '</div>';
      html += '<div class="save-slot-info">';
      html += '<div class="save-slot-name">' + esc(name) + '</div>';
      html += '<div class="save-slot-meta"><span>' + esc(meta) + '</span><span>' + date + '</span></div>';
      html += '</div>';
      html += '<div class="save-slot-actions">';
      html += '<button class="save-slot-btn" onclick="event.stopPropagation();overwriteSlot(\'' + scriptId + '\',' + i + ')">💾 覆盖</button>';
      html += '<button class="save-slot-btn" onclick="event.stopPropagation();deleteSlot(\'' + scriptId + '\',' + i + ')">✕</button>';
      html += '</div></div>';
    } else {
      // 空槽位
      html += '<div class="save-slot save-slot-empty" onclick="saveToSlot(\'' + scriptId + '\',' + i + ')">';
      html += '<span>+ 空槽位 ' + (i + 1) + ' — 点击保存</span>';
      html += '</div>';
    }
  }
  slots.innerHTML = html;
}

function saveToSlot(scriptId, slotIndex) {
  var saveData = {
    savedAt: new Date().toISOString(),
    scriptId: scriptId
  };

  if (scriptId === "huluxiaxing") {
    saveData.chapter = detectChapter(vnState.currentNode) ? "第" + detectChapter(vnState.currentNode) + "章" : "";
    saveData.nodeId = vnState.currentNode;
    saveData.stats = JSON.parse(JSON.stringify(vnState.stats));
    saveData.favorability = JSON.parse(JSON.stringify(vnState.favorability || {}));
    saveData.vnState = JSON.parse(JSON.stringify(vnState));
  } else {
    var node = simulatorData && simulatorData.nodes && simulatorData.nodes[simState.currentNode];
    saveData.chapter = (node ? (node.stage || "大" + node.year) : "");
    saveData.nodeId = simState.currentNode;
    saveData.stats = JSON.parse(JSON.stringify(simState.stats));
    saveData.simState = JSON.parse(JSON.stringify(simState));
  }

  var saves = loadAllSaves(scriptId);
  saves[slotIndex] = saveData;
  saveAllSaves(scriptId, saves);
  renderSaveSlots(scriptId);
  showToast("存档成功 ✓");
}

function overwriteSlot(scriptId, slotIndex) {
  saveToSlot(scriptId, slotIndex);
}

function loadFromSlot(scriptId, slotIndex) {
  var saves = loadAllSaves(scriptId);
  var saveData = saves[slotIndex];
  if (!saveData) return;

  if (scriptId === "huluxiaxing") {
    if (saveData.vnState) {
      vnState = JSON.parse(JSON.stringify(saveData.vnState));
      localStorage.setItem("mathAppVNSave", JSON.stringify(vnState));
    }
    hide("scriptSquare"); hide("simulatorContainer");
    show("lifeSubNav"); show("storyContainer");
    document.querySelectorAll(".life-sub-btn").forEach(function (b) { b.classList.toggle("active", b.dataset.life === "story"); });
    currentLifeTab = "story";
    startStoryEngine();
  } else {
    if (saveData.simState) {
      simState = JSON.parse(JSON.stringify(saveData.simState));
      saveSimState();
    }
    hide("scriptSquare"); hide("storyContainer");
    show("lifeSubNav"); show("simulatorContainer");
    document.querySelectorAll(".life-sub-btn").forEach(function (b) { b.classList.toggle("active", b.dataset.life === "simulator"); });
    currentLifeTab = "simulator";
    startSimulator();
  }

  closeSavePanel();
  showToast("读档成功 ✓");
}

function deleteSlot(scriptId, slotIndex) {
  var saves = loadAllSaves(scriptId);
  saves[slotIndex] = null; // 保留空位
  saveAllSaves(scriptId, saves);
  renderSaveSlots(scriptId);
  showToast("已删除存档");
}

// 兼容旧版存档按钮
function saveVNState() {
  var scriptId = "huluxiaxing";
  // 自动保存到槽位0
  saveToSlot(scriptId, 0);
}

// ============================================================
// 结局画廊 (Ending Gallery — v8.3)
// ============================================================
const ALL_ENDINGS = [
  { id: "truth", name: "真相守望", icon: "🔮", desc: "成为葫芦数学世界首位居民与共创者" },
  { id: "master", name: "数理宗师", icon: "📚", desc: "葫芦侠成为一代宗师，数堂发扬光大" },
  { id: "wander", name: "逍遥江湖", icon: "🍃", desc: "远离纷争，在江湖中自由游历" },
  { id: "sail", name: "出海远航", icon: "⛵", desc: "随欧阳侠远赴新大陆，开创东西方交流" },
  { id: "hero", name: "悲情英雄", icon: "⚔️", desc: "坚守信念，孤身面对审查与命运" },
  { id: "fall", name: "堕落之路", icon: "🌑", desc: "在权力与诱惑中迷失自我" },
  { id: "jail", name: "冤狱", icon: "⛓", desc: "被冤入狱，含恨而终" },
  { id: "void", name: "虚无", icon: "🌫", desc: "发现真相后精神崩溃，消失于江湖" },
  { id: "math_phd", name: "数学博士", icon: "🔬", desc: "坚持学术之路，走向研究巅峰" },
  { id: "quant", name: "量化交易员", icon: "💰", desc: "用数学模型在金融市场叱咤风云" },
  { id: "teacher", name: "高中教师", icon: "📝", desc: "回到家乡，培养下一代数学人才" },
  { id: "data_sci", name: "数据科学家", icon: "📊", desc: "在互联网公司用数据改变世界" }
];

function getUnlockedEndings() {
  try {
    var raw = localStorage.getItem("mathAppUnlockedEndings");
    return raw ? JSON.parse(raw) : [];
  } catch (e) { return []; }
}

function unlockEnding(endingId) {
  var endings = getUnlockedEndings();
  if (endings.indexOf(endingId) < 0) {
    endings.push(endingId);
    localStorage.setItem("mathAppUnlockedEndings", JSON.stringify(endings));
  }
}

function openGallery() {
  document.getElementById("galleryOverlay").classList.add("show");
  renderGallery();
}

function closeGallery() {
  document.getElementById("galleryOverlay").classList.remove("show");
}

function renderGallery() {
  var unlocked = getUnlockedEndings();
  var progressEl = document.getElementById("galleryProgress");
  progressEl.textContent = unlocked.length + "/" + ALL_ENDINGS.length;

  var grid = document.getElementById("galleryGrid");
  grid.innerHTML = ALL_ENDINGS.map(function (e) {
    var isUnlocked = unlocked.indexOf(e.id) >= 0;
    if (isUnlocked) {
      return '<div class="gallery-card unlocked">'
        + '<div class="gallery-badge">' + e.icon + '</div>'
        + '<div class="gallery-card-name">' + esc(e.name) + '</div>'
        + '<div class="gallery-card-desc">' + esc(e.desc) + '</div>'
        + '</div>';
    } else {
      return '<div class="gallery-card locked">'
        + '<div class="gallery-card-lock">🔒</div>'
        + '<div class="gallery-card-name">???</div>'
        + '<div class="gallery-card-desc">未解锁</div>'
        + '</div>';
    }
  }).join("");
}
