/* store-cloudflare.jsx — 云端数据层 for 咔嗒Lab（Cloudflare 版）
   ----------------------------------------------------------------
   当 cloudflare-config.js 里 ENABLED=true 且能连上后端时，本文件接管
   window.KadaStore，把所有读写指向 Cloudflare 后端(D1 数据库)。

   关键设计：
   · 对外暴露的 KadaStore API 与本地版「完全一致」，上层 20+ 页面不用改。
   · 维护一份内存缓存 cache（镜像数据库），页面同步读取缓存。
   · 写操作先乐观更新缓存→立即刷新 UI→异步写库→回拉权威数据。
   · 【自动回退】如果后端连不上（比如还没部署、或本地预览），自动委托给
     store.jsx 的本地演示数据，页面照常可用、不会白屏、不报错。
   ---------------------------------------------------------------- */
(function () {
  const CFG = window.KADA_CLOUDFLARE || {};
  const API = (CFG.API || '/api').replace(/\/$/, '');
  if (CFG.ENABLED === false) {
    console.info('[咔嗒Lab] Cloudflare 后端已在配置里关闭，使用本地演示模式。');
    return;
  }

  const LOCAL = window.KadaStore;          // store.jsx 的本地版（种子数据 + 回退用）
  const TOKEN_KEY = 'kada.cf.token';
  let token = null;
  try { token = localStorage.getItem(TOKEN_KEY) || null; } catch (e) {}
  let connected = false;                    // 后端是否连通

  const subs = new Set();
  const HUES = [222, 256, 200, 268, 188, 244, 210, 176];
  const hueFor = (s) => HUES[[...(s || '?')].reduce((a, c) => a + c.charCodeAt(0), 0) % HUES.length];
  const notify = () => subs.forEach((fn) => { try { fn(); } catch (e) {} });

  function timeAgo(iso) {
    const d = (Date.now() - new Date(iso).getTime()) / 1000;
    if (d < 60) return '刚刚';
    if (d < 3600) return Math.floor(d / 60) + ' 分钟前';
    if (d < 86400) return Math.floor(d / 3600) + ' 小时前';
    if (d < 604800) return Math.floor(d / 86400) + ' 天前';
    return new Date(iso).toLocaleDateString('zh-CN');
  }

  const cache = {
    session: null,
    posts: [], eventsC: [], eventsE: [], works: [],
    likedPosts: new Set(), likedWorks: new Set(),
    myRegs: new Set(), follows: new Set(),
    ready: false,
  };

  /* ---------- fetch 封装 ---------- */
  async function api(path, { method = 'GET', body } = {}) {
    const headers = {};
    if (body !== undefined) headers['content-type'] = 'application/json';
    if (token) headers['authorization'] = 'Bearer ' + token;
    const res = await fetch(API + path, {
      method, headers, body: body !== undefined ? JSON.stringify(body) : undefined,
    });
    const ct = res.headers.get('content-type') || '';
    if (!ct.includes('json')) throw new Error('后端未返回 JSON（可能未部署）');
    const data = await res.json();
    if (!res.ok || data.ok === false) throw new Error(data.error || ('HTTP ' + res.status));
    return data;
  }
  function setToken(t) { token = t || null; try { t ? localStorage.setItem(TOKEN_KEY, t) : localStorage.removeItem(TOKEN_KEY); } catch (e) {} }

  /* ---------- 启动：拉取全部数据 ---------- */
  async function boot() {
    try {
      const d = await api('/bootstrap');
      applyBootstrap(d);
      connected = true;
      cache.ready = true;
      console.info('[咔嗒Lab] 已连接 Cloudflare 云端模式 ☁️');
      notify();
    } catch (e) {
      connected = false;
      console.info('[咔嗒Lab] 未连接后端，暂用本地演示数据（部署到 Cloudflare 后自动生效）。原因：' + e.message);
      notify();
    }
  }

  function applyBootstrap(d) {
    cache.session = d.user || null;
    // 帖子 + 评论
    const byPost = {};
    (d.postComments || []).forEach((c) => {
      (byPost[c.post_id] = byPost[c.post_id] || []).unshift({
        id: c.id, name: c.aname || '匿名', hue: c.ahue || 210, t: timeAgo(c.created_at), text: c.text,
      });
    });
    cache.posts = (d.posts || []).map((p) => ({
      id: p.id, author: p.author?.name || '匿名', handle: p.author?.handle || '@user', hue: p.author?.hue || 210,
      kind: p.kind, title: p.title, body: p.body, tags: p.tags || [],
      time: timeAgo(p.created_at), likes: p.like_count, comments: byPost[p.id] || [],
    }));
    // 作品 + 评论
    const byWork = {};
    (d.workComments || []).forEach((c) => {
      (byWork[c.work_id] = byWork[c.work_id] || []).unshift({
        id: c.id, name: c.aname || '匿名', hue: c.ahue || 210, t: timeAgo(c.created_at), text: c.text,
      });
    });
    cache.works = (d.works || []).map((w) => ({
      id: w.id, name: w.name, type: w.type, task: w.task, blurb: w.blurb,
      author: (w.author?.handle || '@me').replace(/^@/, ''), org: w.author?.name || '',
      links: w.links || {}, cover: w.cover_url, hue: w.hue, likes: w.like_count, runs: w.runs,
      updated: timeAgo(w.created_at), comments: byWork[w.id] || [], trend: w.like_count >= 10,
    }));
    // 活动
    const mapE = (e) => ({ id: e.id, scope: e.scope, regCount: e.reg_count, ...(e.data || {}) });
    cache.eventsC = (d.events || []).filter((e) => e.scope === 'c').map(mapE);
    cache.eventsE = (d.events || []).filter((e) => e.scope === 'e').map(mapE);
    // 我的点赞/报名/关注
    cache.likedPosts = new Set((d.mine && d.mine.likedPosts) || []);
    cache.likedWorks = new Set((d.mine && d.mine.likedWorks) || []);
    cache.myRegs = new Set((d.mine && d.mine.myRegs) || []);
    cache.follows = new Set((d.mine && d.mine.follows) || []);
  }

  async function rehydrate() { try { applyBootstrap(await api('/bootstrap')); } catch (e) {} notify(); }

  /* ---------- 会话 ---------- */
  function currentUser() { return connected ? cache.session : LOCAL.currentUser(); }
  function actor() {
    if (!connected) return LOCAL.actor();
    const u = cache.session;
    return u ? { name: u.name, handle: u.handle, hue: u.hue } : { name: '访客', handle: '@guest', hue: 205 };
  }
  function needLogin() {
    if (!cache.session) { location.hash = '#auth'; return true; }
    return false;
  }

  async function register({ name, email, password }) {
    if (!connected) return LOCAL.register({ name, email, password });
    try {
      const d = await api('/register', { method: 'POST', body: { name, email, password } });
      setToken(d.token); await rehydrate();
      return { ok: true, user: cache.session };
    } catch (e) { return { ok: false, error: e.message }; }
  }
  async function login({ email, password }) {
    if (!connected) return LOCAL.login({ email, password });
    try {
      const d = await api('/login', { method: 'POST', body: { email, password } });
      setToken(d.token); await rehydrate();
      return { ok: true, user: cache.session };
    } catch (e) { return { ok: false, error: e.message }; }
  }
  async function logout() {
    if (!connected) return LOCAL.logout();
    setToken(null); cache.session = null;
    cache.likedPosts = new Set(); cache.likedWorks = new Set(); cache.myRegs = new Set(); cache.follows = new Set();
    notify();
  }

  /* ---------- 帖子 ---------- */
  const posts = () => (connected ? cache.posts : LOCAL.posts());
  const getPost = (id) => (connected ? cache.posts.find((p) => p.id === id) || null : LOCAL.getPost(id));
  const postIndex = (id) => (connected ? cache.posts.findIndex((p) => p.id === id) : LOCAL.postIndex(id));
  async function addPost(payload) {
    if (!connected) return LOCAL.addPost(payload);
    if (needLogin()) return null;
    const d = await api('/posts', { method: 'POST', body: payload });
    await rehydrate();
    return getPost(d.id) || { id: d.id };
  }
  const isPostLiked = (id) => (connected ? cache.likedPosts.has(id) : LOCAL.isPostLiked(id));
  async function toggleLikePost(id) {
    if (!connected) return LOCAL.toggleLikePost(id);
    if (needLogin()) return;
    const p = getPost(id); const liked = cache.likedPosts.has(id);
    if (liked) { cache.likedPosts.delete(id); if (p) p.likes = Math.max(0, p.likes - 1); }
    else { cache.likedPosts.add(id); if (p) p.likes += 1; }
    notify();
    try { await api('/posts/like', { method: 'POST', body: { id } }); } catch (e) { await rehydrate(); }
  }
  async function addComment(postId, text) {
    if (!connected) return LOCAL.addComment(postId, text);
    if (needLogin()) return;
    const a = actor(); const p = getPost(postId);
    if (p) p.comments.unshift({ id: 'tmp', name: a.name, hue: a.hue, t: '刚刚', text: text.trim() });
    notify();
    await api('/comments', { method: 'POST', body: { postId, text: text.trim() } });
    await rehydrate();
  }

  /* ---------- 活动 ---------- */
  const events = (scope) => (connected ? (scope === 'e' ? cache.eventsE : cache.eventsC) : LOCAL.events(scope));
  const getEvent = (scope, idx) => (connected ? (events(scope)[idx] || null) : LOCAL.getEvent(scope, idx));
  async function addEvent(scope, data) {
    if (!connected) return LOCAL.addEvent(scope, data);
    if (needLogin()) return { event: null, idx: 0 };
    const { id: _o, scope: _s, regCount: _rc, ...payload } = data;
    const d = await api('/events', { method: 'POST', body: { scope, data: payload } });
    await rehydrate();
    const idx = events(scope).findIndex((e) => e.id === d.id);
    return { event: getEvent(scope, idx), idx: idx < 0 ? events(scope).length - 1 : idx };
  }
  const isRegistered = (scope, idx) => {
    if (!connected) return LOCAL.isRegistered(scope, idx);
    const e = getEvent(scope, idx); return !!e && cache.myRegs.has(e.id);
  };
  async function registerEvent(scope, idx, info) {
    if (!connected) return LOCAL.registerEvent(scope, idx, info);
    if (needLogin()) return { ok: false };
    const e = getEvent(scope, idx); if (!e) return { ok: false };
    cache.myRegs.add(e.id); e.regCount = (e.regCount || 0) + 1; notify();
    await api('/events/register', { method: 'POST', body: { eventId: e.id, name: info.name, contact: info.contact, role: info.role } });
    return { ok: true };
  }

  /* ---------- 作品 ---------- */
  const works = () => (connected ? cache.works : LOCAL.works());
  const getWork = (idx) => (connected ? cache.works[idx] || null : LOCAL.getWork(idx));
  async function addWork(data) {
    if (!connected) return LOCAL.addWork(data);
    if (needLogin()) return null;
    const d = await api('/works', { method: 'POST', body: data });
    await rehydrate();
    return cache.works.find((w) => w.id === d.id) || cache.works[0];
  }
  const isWorkLiked = (idx) => {
    if (!connected) return LOCAL.isWorkLiked(idx);
    const w = getWork(idx); return !!w && cache.likedWorks.has(w.id);
  };
  async function toggleLikeWork(idx) {
    if (!connected) return LOCAL.toggleLikeWork(idx);
    if (needLogin()) return;
    const w = getWork(idx); if (!w) return;
    const liked = cache.likedWorks.has(w.id);
    if (liked) { cache.likedWorks.delete(w.id); w.likes = Math.max(0, w.likes - 1); }
    else { cache.likedWorks.add(w.id); w.likes += 1; }
    notify();
    try { await api('/works/like', { method: 'POST', body: { id: w.id } }); } catch (e) { await rehydrate(); }
  }
  async function addWorkComment(idx, text) {
    if (!connected) return LOCAL.addWorkComment(idx, text);
    if (needLogin()) return;
    const w = getWork(idx); if (!w) return;
    const a = actor();
    w.comments.unshift({ id: 'tmp', name: a.name, hue: a.hue, t: '刚刚', text: text.trim() });
    notify();
    await api('/comments', { method: 'POST', body: { workId: w.id, text: text.trim() } });
    await rehydrate();
  }

  /* ---------- 关注 ---------- */
  const isFollowing = (handle) => (connected ? cache.follows.has(handle) : LOCAL.isFollowing(handle));
  async function toggleFollow(handle) {
    if (!connected) return LOCAL.toggleFollow(handle);
    if (needLogin()) return;
    const on = cache.follows.has(handle);
    if (on) cache.follows.delete(handle); else cache.follows.add(handle);
    notify();
    try { await api('/follows', { method: 'POST', body: { handle } }); } catch (e) { await rehydrate(); }
  }

  /* ---------- 文件上传 ---------- */
  async function uploadMedia(file) {
    if (!connected) return LOCAL.uploadMedia ? LOCAL.uploadMedia(file) : { ok: false, error: '本地演示模式不支持上传' };
    if (!file) return { ok: false, error: '没有选择文件' };
    if (needLogin()) return { ok: false, error: '请先登录再上传' };
    try {
      const fd = new FormData(); fd.append('file', file);
      const headers = {}; if (token) headers['authorization'] = 'Bearer ' + token;
      const res = await fetch(API + '/upload', { method: 'POST', headers, body: fd });
      const d = await res.json();
      if (!res.ok || d.ok === false) return { ok: false, error: d.error || '上传失败' };
      return { ok: true, url: d.url };
    } catch (e) { return { ok: false, error: '上传失败：' + e.message }; }
  }

  /* ---------- 导出（覆盖本地版 KadaStore） ---------- */
  window.KadaStore = {
    cloud: true,
    subscribe(fn) {
      subs.add(fn);
      const unLocal = LOCAL.subscribe(fn);   // 本地回退时的写入也能通知到组件
      return () => { subs.delete(fn); unLocal(); };
    },
    getDB: () => (connected ? cache : LOCAL.getDB()),
    currentUser, actor, register, login, logout,
    posts, getPost, postIndex, addPost, isPostLiked, toggleLikePost, addComment,
    events, getEvent, addEvent, isRegistered, registerEvent,
    works, getWork, addWork, isWorkLiked, toggleLikeWork, addWorkComment,
    isFollowing, toggleFollow, uploadMedia,
    reset() { if (!connected && LOCAL.reset) LOCAL.reset(); else console.warn('云端模式不支持 reset；请在 Cloudflare 后台管理数据。'); },
  };

  boot();
})();
