/* 数据层：种子数据 + reducer + localStorage 持久化 + 派生选择器 */

const SUBJECT_COLOR = { '语文':'#e0697f','数学':'#4ea3dd','英语':'#56c79f','历史':'#c28a4a','地理':'#4fb48f','政治':'#d46b8c','物理':'#9b8cf0','化学':'#f5a35e','生物':'#5cc7d6','体育':'#6abf69','科学':'#5cc7d6' };

const MONTHS = ['2025-12','2026-01','2026-02','2026-03','2026-04','2026-05'];
const MONTH_LABEL = ['12月','1月','2月','3月','4月','5月'];
const TYPE_BY_MONTH = ['月考','周测','月考','期中','周测','期中'];

// 根据每月得分率构造全科考试
function buildExams(childKey, spec, totals, extra) {
  const exams = [];
  MONTHS.forEach((ym, i) => {
    const [y, m] = ym.split('-');
    const subjects = Object.keys(spec).map(sub => {
      const rate = spec[sub][i];
      const total = totals[sub] || 100;
      const score = Math.round(rate / 100 * total);
      const classAvg = Math.max(40, Math.round(score - 10 - Math.random()*6));
      const classMax = Math.min(total, Math.round(score + 4 + Math.random()*4));
      return { name: sub, score, total, classAvg, classMax,
        gradeAvg: Math.max(40, classAvg - 3), gradeMax: classMax,
        note: (sub==='物理' && i>=3) ? '力学综合题、受力分析失分较多，需专项复盘。' : '' };
    });
    exams.push({
      id: childKey + '-' + ym, name: `${parseInt(m)}月${TYPE_BY_MONTH[i]}`,
      date: `${y}-${m}-${String(12+i).padStart(2,'0')}`, type: TYPE_BY_MONTH[i],
      subjects, awarded: 0, seed: true
    });
  });
  return exams.concat(extra || []).sort((a,b)=> a.date < b.date ? 1 : -1);
}

const SEED = {
  children: [
    {
      id: 'xiao', name: '林晓', grade: '初三(3)班', stage: '中学', avatar: 'av-blue', initial: '林',
      points: 380, streak: 7,
      subjectList: ['语文','数学','英语','物理','化学'],
      exams: buildExams('xiao',
        { '语文':[78,80,79,82,81,83], '数学':[82,80,85,88,90,94], '英语':[85,86,88,87,89,91], '物理':[78,74,76,71,73,72], '化学':[80,78,81,83,77,79] },
        { '语文':120,'数学':100,'英语':100,'物理':100,'化学':100 },
        [
          { id:'xiao-w1', name:'数学第三次周测', date:'2026-05-22', type:'周测', awarded:0,
            subjects:[{name:'数学',score:94,total:100,classAvg:78,classMax:97,gradeAvg:75,gradeMax:99,note:'第18题二次函数最值思路偏长。'}] },
          { id:'xiao-w2', name:'英语单元测验', date:'2026-05-06', type:'周测', awarded:0,
            subjects:[{name:'英语',score:91,total:100,classAvg:84,classMax:96,gradeAvg:82,gradeMax:98,note:''}] },
        ]),
      pointsLog: [
        { id:'l1', date:'2026-05-22', text:'单次录入完整成绩 · 数学周测', delta:5 },
        { id:'l2', date:'2026-05-22', text:'数学得分率 94%（≥90%）', delta:15 },
        { id:'l3', date:'2026-05-18', text:'批量录入期中成绩', delta:10 },
        { id:'l4', date:'2026-05-15', text:'连续 7 天打卡奖励', delta:20 },
        { id:'l5', date:'2026-05-10', text:'兑换 学习主题壁纸', delta:-80 },
      ],
      favorites: ['s1','s5'],
    },
    {
      id: 'mo', name: '林末', grade: '小学五年级', stage: '小学', avatar: 'av-green', initial: '林',
      points: 210, streak: 3,
      subjectList: ['语文','数学','英语','科学'],
      exams: buildExams('mo',
        { '语文':[92,93,94,95,94,95], '数学':[85,86,84,88,87,88], '英语':[88,89,90,89,91,90], '科学':[86,88,87,90,89,89] },
        { '语文':100,'数学':100,'英语':100,'科学':100 }, []),
      pointsLog: [
        { id:'m1', date:'2026-05-20', text:'单次录入完整成绩 · 语文', delta:5 },
        { id:'m2', date:'2026-05-20', text:'语文得分率 95%（≥90%）', delta:15 },
        { id:'m3', date:'2026-05-14', text:'AI 拍照录入', delta:8 },
      ],
      favorites: ['p2'],
    },
  ],
};

const QUOTES = {
  student: [
    { id:'s0', t:'你不必每次都考第一，但每一次比上一次的自己更好一点，就是了不起的进步。', theme:'成长', cls:'bg-green-soft' },
    { id:'s1', t:'你今天多走的一步，明天会变成别人追不上的距离。', theme:'坚持', cls:'bg-blue-soft' },
    { id:'s2', t:'错题不是失败的记录，而是你下次不会再错的地图。', theme:'错题复盘', cls:'bg-green-soft' },
    { id:'s3', t:'进步是安静发生的。等你回头看，才发现自己已经走了很远。', theme:'成长', cls:'bg-orange-soft' },
    { id:'s4', t:'状态有起伏很正常，重要的是你一直没有停下来。', theme:'稳步进步', cls:'bg-purple-soft' },
    { id:'s5', t:'今天的努力，是在给未来的自己存底气。', theme:'努力', cls:'bg-blue-soft' },
    { id:'s6', t:'别和别人比起点，和昨天的自己比进步就好。', theme:'成长', cls:'bg-green-soft' },
  ],
  parent: [
    { id:'p0', t:'成长不是一条直线。分数会起伏，但孩子学会面对起伏的能力，比任何一次满分都珍贵。', theme:'理性看待', cls:'bg-orange-soft' },
    { id:'p1', t:'每个孩子都有自己的花期，有的春天开，有的要等到夏天。你的耐心，就是他们最好的阳光。', theme:'循序渐进', cls:'bg-orange-soft' },
    { id:'p2', t:'比起"考了多少分"，孩子更需要听到"我看到你努力了"。', theme:'正向引导', cls:'bg-green-soft' },
    { id:'p3', t:'教育不是一场和别人家孩子的竞赛，而是陪自己的孩子，长成他自己。', theme:'理性看待', cls:'bg-blue-soft' },
    { id:'p4', t:'关注趋势，而不是单次分数。把目光放长，你会更平静，也更能看见真实的成长。', theme:'长期视角', cls:'bg-purple-soft' },
  ],
};

const DEFAULT_REWARDS = [
  { id:'r1', icon:'🏅', name:'专属虚拟勋章', cost:100, custom:false },
  { id:'r2', icon:'🖼️', name:'学习主题壁纸', cost:80, custom:false },
  { id:'r3', icon:'📊', name:'精细化学情报告', cost:150, custom:false },
  { id:'r7', icon:'📁', name:'错题整理模板', cost:60, custom:false },
  { id:'r8', icon:'🎯', name:'专属学习计划', cost:200, custom:false },
  { id:'r4', icon:'🎮', name:'周末游乐场出游', cost:500, custom:true },
  { id:'r5', icon:'📝', name:'免作业券 ×1', cost:300, custom:true },
  { id:'r6', icon:'🍦', name:'心愿小零食', cost:120, custom:true },
];

const DEMO_USERNAMES = ['student', 'student2', 'parent'];
const DEFAULT_SUBJECTS = ['语文','数学','英语'];

function isDemoUser(user) {
  return user && DEMO_USERNAMES.includes(user.username);
}

function buildEmptyStudent(user) {
  const profile = user && user.profile ? user.profile : {};
  const name = profile.nickname || user.username || '学生';
  return {
    id: 'me',
    name,
    grade: profile.grade || '未设置年级',
    stage: '中学',
    avatar: 'av-blue',
    initial: name.trim().charAt(0) || '学',
    points: 0,
    streak: 0,
    subjectList: DEFAULT_SUBJECTS,
    exams: [],
    pointsLog: [],
    favorites: [],
  };
}

function buildEmptyAuthChild(authChild, index) {
  const name = authChild.name || authChild.nickname || `孩子${index + 1}`;
  return {
    id: String(authChild.id || `child-${index + 1}`),
    name,
    grade: authChild.grade || '未设置年级',
    stage: '中学',
    avatar: index % 2 ? 'av-green' : 'av-blue',
    initial: name.trim().charAt(0) || '学',
    points: 0,
    streak: 0,
    subjectList: DEFAULT_SUBJECTS,
    exams: [],
    pointsLog: [],
    favorites: [],
  };
}

/* ---------- 派生选择器 ---------- */
const Selectors = {
  examRate(exam) {
    const s = exam.subjects.reduce((a,b)=>a+b.score,0);
    const t = exam.subjects.reduce((a,b)=>a+b.total,0);
    return t ? (s/t*100) : 0;
  },
  avgRate(child) {
    const rates = child.exams.map(e=>Selectors.examRate(e));
    return rates.length ? rates.reduce((a,b)=>a+b,0)/rates.length : 0;
  },
  // 每学科：平均得分率
  subjectAvg(child) {
    const map = {};
    child.exams.forEach(e=> e.subjects.forEach(s=>{
      (map[s.name] = map[s.name] || []).push(s.score/s.total*100);
    }));
    const out = {};
    Object.keys(map).forEach(k=> out[k] = map[k].reduce((a,b)=>a+b,0)/map[k].length);
    return out;
  },
  // 按月趋势（仅全科 seed 考试 + 含该科的考试）
  monthlyTrend(child, subject) {
    return MONTHS.map(ym => {
      const exam = child.exams.find(e=> e.date.slice(0,7)===ym && e.subjects.some(s=>s.name===subject) && e.seed);
      if (!exam) return null;
      const s = exam.subjects.find(x=>x.name===subject);
      return s ? Math.round(s.score/s.total*100) : null;
    });
  },
  stability(child) {
    const map = {};
    child.exams.forEach(e=> e.subjects.forEach(s=>{
      (map[s.name] = map[s.name] || []).push(s.score/s.total*100);
    }));
    const out = {};
    Object.keys(map).forEach(k=>{
      const a = map[k]; const m = a.reduce((x,y)=>x+y,0)/a.length;
      const sd = Math.sqrt(a.reduce((x,y)=>x+(y-m)**2,0)/a.length);
      out[k] = Math.max(0, Math.min(100, Math.round(100 - sd*5)));
    });
    return out;
  },
  monthProgress(child) {
    // 最近全科 vs 上一次全科 综合分变化（示意）
    const full = child.exams.filter(e=>e.seed);
    if (full.length < 2) return 0;
    const last = Selectors.examRate(full[full.length-1]);
    const prev = Selectors.examRate(full[full.length-2]);
    return Math.round((last - prev) * (full[0].subjects.reduce((a,b)=>a+b.total,0)/100) / 5);
  },
  classAvgBySubject(child) {
    const map = {};
    child.exams.forEach(e=> e.subjects.forEach(s=>{
      if (s.classAvg) (map[s.name]=map[s.name]||[]).push(s.classAvg/s.total*100);
    }));
    const out={}; Object.keys(map).forEach(k=> out[k]=Math.round(map[k].reduce((a,b)=>a+b,0)/map[k].length));
    return out;
  },
  gradeAvgBySubject(child) {
    const map = {};
    child.exams.forEach(e=> e.subjects.forEach(s=>{
      if (s.gradeAvg) (map[s.name]=map[s.name]||[]).push(s.gradeAvg/s.total*100);
    }));
    const out={}; Object.keys(map).forEach(k=> out[k]=Math.round(map[k].reduce((a,b)=>a+b,0)/map[k].length));
    return out;
  },
};

function computePoints(exam, mode) {
  const items = [];
  const base = mode==='batch' ? 10 : mode==='ai' ? 8 : 5;
  items.push({ text: mode==='batch'?'批量录入完整成绩':mode==='ai'?'AI 拍照录入':'单次录入完整成绩', delta: base });
  exam.subjects.forEach(s=>{
    const rate = s.score/s.total*100;
    if (s.score === s.total) items.push({ text:`${s.name} 单科满分`, delta:20 });
    else if (rate >= 90) items.push({ text:`${s.name} 得分率 ${rate.toFixed(0)}%（≥90%）`, delta:15 });
    if (s.note && s.note.trim()) items.push({ text:`完善 ${s.name} 错题备注`, delta:3 });
  });
  return items;
}

/* ---------- reducer ---------- */
const STORAGE_KEY = 'xqx_app_v2';
const initialState = {
  token: null,
  user: null,
  role: null,
  authChildren: [],
  requiresFamilySetup: false,
  nextPage: null,
  activeChildId: 'xiao',
  children: SEED.children,
  rewards: DEFAULT_REWARDS,
  quoteIdx: { student: 0, parent: 0 },
  toast: null,
};

function deduplicateChildren(children) {
  if (!Array.isArray(children)) return [];
  const seenIds = new Set();
  return children.filter(c => {
    if (!c || !c.id) return false;
    const idKey = String(c.id);
    if (seenIds.has(idKey)) return false;
    seenIds.add(idKey);
    return true;
  });
}

function getChildrenForAuth(user, authChildren) {
  const role = user && user.role;
  if (isDemoUser(user)) {
    return role === 'parent' && authChildren.length
      ? mergeAuthChildrenForDisplay(initialState.children, authChildren)
      : initialState.children;
  }
  if (role === 'parent') return deduplicateChildren(authChildren.map(buildEmptyAuthChild));
  if (role === 'student') return [buildEmptyStudent(user)];
  return initialState.children;
}

function hasDemoExamData(child) {
  return child && Array.isArray(child.exams) && child.exams.some(exam => exam.seed || String(exam.id || '').startsWith('xiao-') || String(exam.id || '').startsWith('mo-'));
}

function stripDemoDataFromChild(user, child) {
  const exams = (child.exams || []).filter(exam => !exam.seed && !String(exam.id || '').startsWith('xiao-') && !String(exam.id || '').startsWith('mo-'));
  const demoChild = hasDemoExamData(child) || ['xiao','mo'].includes(child.id);
  if (demoChild) {
    return { ...buildEmptyStudent(user), exams };
  }
  return {
    ...child,
    exams,
    pointsLog: child.pointsLog || [],
    favorites: child.favorites || [],
  };
}

function getPersistedRealChildren(user, persistedChildren) {
  if (!user || !Array.isArray(persistedChildren) || !persistedChildren.length) return null;
  const cleaned = persistedChildren.map(child => stripDemoDataFromChild(user, child));
  return cleaned.length ? cleaned : null;
}

function load() {
  try {
    const raw = localStorage.getItem(STORAGE_KEY);
    if (raw) {
      const parsed = JSON.parse(raw) || {};
      const authChildren = Array.isArray(parsed.authChildren) ? parsed.authChildren : [];
      const persistedChildren = Array.isArray(parsed.children) ? parsed.children : [];

      const user = parsed.user;
      const demoUser = user && DEMO_USERNAMES.includes(user.username);

      let children;
      if (demoUser) {
        const realChildren = getPersistedRealChildren(user, persistedChildren);
        children = deduplicateChildren(realChildren || getChildrenForAuth(user, authChildren));
      } else {
        children = deduplicateChildren(getChildrenForAuth(user, authChildren));
      }

      return {
        ...initialState,
        ...parsed,
        authChildren,
        children,
        activeChildId: children[0] ? children[0].id : null,
        quoteIdx: { ...initialState.quoteIdx, ...(parsed.quoteIdx || {}) },
        toast: null,
      };
    }
  } catch(e) {}
  return initialState;
}
function persist(state) {
  try { const { toast, ...rest } = state; localStorage.setItem(STORAGE_KEY, JSON.stringify(rest)); } catch(e){}
}

function mergeAuthChildrenForDisplay(baseChildren, authChildren) {
  if (!Array.isArray(authChildren) || !authChildren.length) return baseChildren;
  if (!Array.isArray(baseChildren) || !baseChildren.length) return baseChildren;

  const limit = Math.min(authChildren.length, baseChildren.length);
  return baseChildren.slice(0, limit).map((child, index) => {
    const authChild = authChildren[index] || {};
    const displayName = authChild.name || authChild.nickname || child.name;
    return {
      ...child,
      name: displayName,
      grade: authChild.grade || child.grade,
      initial: (displayName || '学').trim().charAt(0) || '学',
    };
  });
}

function reducer(state, action) {
  switch (action.type) {
    case 'AUTH_SUCCESS': {
      const user = action.user || {};
      const role = user.role || null;
      const authChildren = Array.isArray(action.children) ? action.children : [];
      const demoUser = isDemoUser(user);
      // 对于真实用户，直接使用从服务器获取的新数据，不要用本地持久化的旧数据
      const children = deduplicateChildren(getChildrenForAuth(user, authChildren));

      // 对于真实用户，直接选择第一个child；对于demo用户才尝试保留旧的id
      const activeChildId = demoUser
        ? (children.some(child => String(child.id) === String(state.activeChildId)) ? state.activeChildId : (children[0] ? children[0].id : null))
        : (children[0] ? children[0].id : null);

      const toast = action.message === null ? null : { kind:'success', msg: action.message || '登录成功' };
      return {
        ...initialState, // 对于真实用户，完全重置到初始状态再应用新数据
        token: action.token,
        user,
        role,
        authChildren,
        requiresFamilySetup: role === 'parent' && !authChildren.length,
        children,
        activeChildId,
        nextPage: action.nextPage,
        toast
      };
    }
    case 'AUTH_INVALID':
      return {
        ...state,
        token: null,
        user: null,
        role: null,
        authChildren: [],
        requiresFamilySetup: false,
        nextPage: null,
        children: initialState.children,
        activeChildId: initialState.activeChildId
      };
    case 'LOGOUT':
      return {
        ...state,
        token: null,
        user: null,
        role: null,
        authChildren: [],
        requiresFamilySetup: false,
        nextPage: null,
        children: initialState.children,
        activeChildId: initialState.activeChildId
      };
    case 'SET_CHILD':
      return { ...state, activeChildId: action.childId };
    case 'ADD_EXAM': {
      const children = state.children.map(c=>{
        if (c.id !== action.childId) return c;
        const items = computePoints(action.exam, action.mode);
        const total = items.reduce((a,b)=>a+b.delta,0);
        const exam = { ...action.exam, awarded: total };
        const log = items.map((it,i)=>({ id:'lg'+Date.now()+i, date: exam.date, text: it.text, delta: it.delta }));
        const subjectList = [...new Set([...(c.subjectList || []), ...exam.subjects.map(s=>s.name)])];
        return { ...c, subjectList, exams: [exam, ...c.exams], points: c.points + total, pointsLog: [...log, ...c.pointsLog] };
      });
      return { ...state, children, toast:{ kind:'success', msg:'成绩已入库，积分已到账' } };
    }
    case 'DELETE_EXAM': {
      const children = state.children.map(c=>{
        if (c.id !== action.childId) return c;
        const exam = c.exams.find(e=>e.id===action.examId);
        const back = exam ? (exam.awarded||0) : 0;
        return { ...c, exams: c.exams.filter(e=>e.id!==action.examId), points: Math.max(0, c.points - back) };
      });
      return { ...state, children, toast:{ kind:'info', msg:'记录已删除，关联积分与分析已同步更新' } };
    }
    case 'REDEEM': {
      const children = state.children.map(c=>{
        if (c.id !== action.childId) return c;
        if (c.points < action.reward.cost) return c;
        const log = { id:'rd'+Date.now(), date:'2026-06-08', text:'兑换 '+action.reward.name, delta:-action.reward.cost };
        return { ...c, points: c.points - action.reward.cost, pointsLog:[log, ...c.pointsLog] };
      });
      const child = state.children.find(c=>c.id===action.childId);
      const ok = child && child.points >= action.reward.cost;
      return { ...state, children, toast: ok ? { kind:'success', msg:`已兑换「${action.reward.name}」` } : { kind:'warn', msg:'积分不足，继续加油' } };
    }
    case 'TOGGLE_FAV': {
      const children = state.children.map(c=>{
        if (c.id !== action.childId) return c;
        const has = c.favorites.includes(action.quoteId);
        return { ...c, favorites: has ? c.favorites.filter(q=>q!==action.quoteId) : [...c.favorites, action.quoteId] };
      });
      return { ...state, children };
    }
    case 'NEXT_QUOTE': {
      if (!QUOTES[action.audience]) return state;
      const len = QUOTES[action.audience].length;
      return { ...state, quoteIdx: { ...state.quoteIdx, [action.audience]: (state.quoteIdx[action.audience]+1) % len } };
    }
    case 'ADD_REWARD':
      return { ...state, rewards: [action.reward, ...state.rewards], toast:{ kind:'success', msg:'自定义权益已添加，学生端已同步' } };
    case 'CLEAR_TOAST':
      return { ...state, toast: null };
    case 'LOAD_EXAMS': {
      const apiExams = (action.exams || []).map(exam => ({
        id: String(exam.id), user_id: exam.user_id,
        name: exam.exam_name, date: exam.exam_date,
        type: exam.exam_type, semester: exam.semester || '', grade: exam.grade || '',
        subjects: (exam.subjects || []).map(s => ({
          name: s.subject, score: s.score, total: s.max_score,
          classAvg: s.class_avg, classMax: s.class_max,
          gradeAvg: s.grade_avg, gradeMax: s.grade_max,
          classRank: s.class_rank, gradeRank: s.grade_rank,
          note: s.wrong_notes || ''
        })),
        seed: false, awarded: 0
      }));
      if (!apiExams.length) return state;
      const children = state.children.map(child => {
        let matches;
        if (action.childId && String(child.id) === String(action.childId)) {
          matches = apiExams;
        } else if (state.role === 'student') {
          matches = apiExams;
        } else if (!action.childId) {
          const childUserId = parseInt(child.id);
          matches = isNaN(childUserId)
            ? []
            : apiExams.filter(e => parseInt(e.user_id) === childUserId || e.user_id === childUserId);
        } else {
          return child;
        }
        if (!matches.length) return child;
        const existingIds = new Set(child.exams.map(e => String(e.id)));
        const newExams = matches.filter(e => !existingIds.has(String(e.id)));
        if (!newExams.length) return child;
        const subjectList = [...new Set([...child.subjectList, ...newExams.flatMap(e => e.subjects.map(s => s.name))])];
        return { ...child, subjectList, exams: [...newExams, ...child.exams] };
      });
      return { ...state, children };
    }
    case 'RESET':
      return initialState;
    default:
      return state;
  }
}

const StoreCtx = React.createContext(null);
function StoreProvider({ children }) {
  const [state, dispatch] = React.useReducer(reducer, undefined, load);
  React.useEffect(()=>{ persist(state); }, [state]);
  React.useEffect(()=>{
    if (state.toast) { const t = setTimeout(()=>dispatch({type:'CLEAR_TOAST'}), 2600); return ()=>clearTimeout(t); }
  }, [state.toast]);
  React.useEffect(()=>{
    if (!state.token) return;
    let cancelled = false;
    (async ()=>{
      try {
        if (state.role === 'parent' && state.children.length) {
          for (const child of state.children) {
            if (cancelled) break;
            const resp = await fetch('/api/exams?childId=' + child.id, { headers: { Authorization: `Bearer ${state.token}` } });
            const result = await resp.json();
            if (!cancelled && result.success && result.data) {
              dispatch({ type: 'LOAD_EXAMS', exams: result.data, userId: state.user && state.user.id, childId: child.id });
            }
          }
        } else {
          const resp = await fetch('/api/exams', { headers: { Authorization: `Bearer ${state.token}` } });
          const result = await resp.json();
          if (!cancelled && result.success && result.data) {
            dispatch({ type: 'LOAD_EXAMS', exams: result.data, userId: state.user && state.user.id });
          }
        }
      } catch(e) {}
    })();
    return ()=>{ cancelled = true; };
  }, [state.token, state.children.length]);
  const fallbackUser = state.user || { username:'学生', profile:{} };
  // 确保id比较时类型一致
  const activeChild = state.children.find(c => String(c.id) === String(state.activeChildId)) || state.children[0] || buildEmptyStudent(fallbackUser);
  return React.createElement(StoreCtx.Provider, { value: { state, dispatch, activeChild } }, children);
}
function useStore() { return React.useContext(StoreCtx); }

Object.assign(window, { SEED, QUOTES, DEFAULT_REWARDS, Selectors, computePoints, SUBJECT_COLOR, MONTH_LABEL, StoreProvider, useStore });
