// Tiny store. No deps — useSyncExternalStore + a plain object.
// All UI reads through selectors in selectors.js, never the raw shape.
//
// Shape:
//   {
//     sessions: Session[],
//     messages: Record<sessionId, Message[]>,
//     activeId: string | null,
//     tab: 'yanli' | 'terminal' | 'memory' | 'settings',
//     loading: { sessions: boolean, messages: boolean },
//     streamingId: string | null,   // assistantMessageId currently streaming
//     ui: { newSessionOpen: boolean, msgActionFor: string | null }
//   }

(() => {
const listeners = new Set();
let state = {
  sessions: [],
  messages: {},
  activeId: null,
  tab: 'yanli',
  loading: { sessions: false, messages: false },
  streamingId: null,
  ui: { newSessionOpen: false, msgActionFor: null, editingId: null },
};

function setState(patch) {
  if (typeof patch === 'function') patch = patch(state);
  state = { ...state, ...patch };
  listeners.forEach(l => l());
}

function subscribe(l) { listeners.add(l); return () => listeners.delete(l); }
function getState() { return state; }
function isYanliChannel(id) { return id === window.YANLI_CHANNEL_ID; }
function getYanliSessionIds() {
  return state.sessions.filter(s => s.mode === 'yanli').map(s => s.id);
}
function getLatestYanliSessionId() {
  const latest = [...state.sessions]
    .filter(s => s.mode === 'yanli')
    .sort((a, b) => Date.parse(b.updatedAt) - Date.parse(a.updatedAt))[0];
  return latest?.id || null;
}
function findMessageOwner(messageId) {
  for (const sid of getYanliSessionIds()) {
    const arr = state.messages[sid] || [];
    if (arr.some(m => m.id === messageId)) return sid;
  }
  return null;
}

// Hook
function useStore(selector = (s) => s, equality = Object.is) {
  const [, force] = React.useReducer(x => x + 1, 0);
  const ref = React.useRef(selector(state));
  React.useEffect(() => subscribe(() => {
    const next = selector(state);
    if (!equality(ref.current, next)) {
      ref.current = next;
      force();
    }
  }), []);
  return selector(state);
}

// ── Actions (thin wrappers around chatApi) ───────────────────
const actions = {
  async loadSessions() {
    setState(s => ({ loading: { ...s.loading, sessions: true } }));
    const sessions = await window.chatApi.listSessions();
    setState(s => ({ sessions, loading: { ...s.loading, sessions: false } }));
  },

  async loadMessages(sessionId) {
    setState(s => ({ loading: { ...s.loading, messages: true } }));
    const msgs = await window.chatApi.getMessages(sessionId);
    setState(s => ({
      messages: { ...s.messages, [sessionId]: msgs },
      loading: { ...s.loading, messages: false },
    }));
  },

  async openSession(id) {
    setState({ activeId: id, tab: isYanliChannel(id) ? 'yanli' : 'terminal' });
    if (!isYanliChannel(id)) {
      await actions.loadMessages(id);
    }
  },

  setTab(tab) { setState({ tab }); },
  backToTerminalList() { setState({ tab: 'terminal', activeId: null }); },

  async newSession({ mode, title, provider }) {
    const s = await window.chatApi.createSession({ mode, title, provider });
    setState(prev => ({
      sessions: [s, ...prev.sessions],
      messages: { ...prev.messages, [s.id]: [] },
      activeId: mode === 'yanli' ? window.YANLI_CHANNEL_ID : s.id,
      tab: mode === 'yanli' ? 'yanli' : 'terminal',
      ui: { ...prev.ui, newSessionOpen: false },
    }));
    return s;
  },

  async deleteSession(id) {
    await window.chatApi.deleteSession(id);
    setState(prev => {
      const sessions = prev.sessions.filter(s => s.id !== id);
      const messages = { ...prev.messages }; delete messages[id];
      return {
        sessions, messages,
        activeId: prev.activeId === id ? null : prev.activeId,
        tab: prev.activeId === id ? 'terminal' : prev.tab,
      };
    });
  },

  async renameSession(id, title) {
    if (isYanliChannel(id)) return window.getYanliChannelSession(state);
    await window.chatApi.renameSession(id, title);
    setState(prev => ({
      sessions: prev.sessions.map(s => s.id === id ? { ...s, title } : s),
    }));
  },

  async setSessionMode(id, mode) {
    if (isYanliChannel(id)) return;
    setState(prev => ({
      sessions: prev.sessions.map(s => s.id === id ? { ...s, mode } : s),
    }));
  },

  async send(content) {
    const sid = isYanliChannel(state.activeId) ? getLatestYanliSessionId() : state.activeId;
    if (!sid || !content.trim()) return;
    const { userMessage, assistantMessageId, stream } =
      await window.chatApi.sendMessage(sid, content);

    setState(prev => ({
      messages: {
        ...prev.messages,
        [sid]: [...(prev.messages[sid] || []),
          userMessage,
          { id: assistantMessageId, role: 'assistant', content: '', status: 'streaming', createdAt: new Date().toISOString() }],
      },
      streamingId: assistantMessageId,
    }));

    let buf = '';
    try {
      for await (const chunk of stream) {
        if (chunk.type === 'delta') {
          buf = chunk.buffered ?? (buf + (chunk.delta || ''));
          setState(prev => ({
            messages: {
              ...prev.messages,
              [sid]: prev.messages[sid].map(m =>
                m.id === assistantMessageId ? { ...m, content: buf } : m),
            },
          }));
        } else if (chunk.type === 'done') {
          setState(prev => ({
            messages: {
              ...prev.messages,
              [sid]: prev.messages[sid].map(m =>
                m.id === assistantMessageId ? { ...m, status: 'done' } : m),
            },
            streamingId: null,
            sessions: prev.sessions.map(s => s.id === sid ? {
              ...s, updatedAt: new Date().toISOString(),
              preview: buf.slice(0, 80),
            } : s),
          }));
        } else if (chunk.type === 'error') {
          setState(prev => ({
            messages: {
              ...prev.messages,
              [sid]: prev.messages[sid].map(m =>
                m.id === assistantMessageId ? { ...m, status: 'error', error: chunk.error } : m),
            },
            streamingId: null,
          }));
        }
      }
    } catch (e) {
      setState(prev => ({
        messages: {
          ...prev.messages,
          [sid]: prev.messages[sid].map(m =>
            m.id === assistantMessageId ? { ...m, status: 'error', error: String(e) } : m),
        },
        streamingId: null,
      }));
    }
  },

  stopStream() {
    if (state.streamingId) {
      window.chatApi.stopStream(state.streamingId);
      const sid = isYanliChannel(state.activeId) ? findMessageOwner(state.streamingId) : state.activeId;
      if (!sid) return;
      setState(prev => ({
        messages: {
          ...prev.messages,
          [sid]: prev.messages[sid].map(m =>
            m.id === prev.streamingId ? { ...m, status: 'done' } : m),
        },
        streamingId: null,
      }));
    }
  },

  async regenerate(messageId) {
    const sid = isYanliChannel(state.activeId) ? findMessageOwner(messageId) : state.activeId;
    if (!sid) return;
    const msgs = state.messages[sid] || [];
    const idx = msgs.findIndex(m => m.id === messageId);
    if (idx < 1) return;
    // find prior user message
    let userIdx = idx - 1;
    while (userIdx >= 0 && msgs[userIdx].role !== 'user') userIdx--;
    if (userIdx < 0) return;
    const userText = msgs[userIdx].content;
    // drop everything from the user msg onwards then resend
    setState(prev => ({
      messages: {
        ...prev.messages,
        [sid]: prev.messages[sid].slice(0, userIdx),
      },
    }));
    await actions.send(userText);
  },

  async editAndResend(messageId, newContent) {
    const sid = isYanliChannel(state.activeId) ? findMessageOwner(messageId) : state.activeId;
    if (!sid) return;
    const msgs = state.messages[sid] || [];
    const idx = msgs.findIndex(m => m.id === messageId);
    if (idx < 0) return;
    setState(prev => ({
      messages: {
        ...prev.messages,
        [sid]: prev.messages[sid].slice(0, idx),
      },
      ui: { ...prev.ui, editingId: null },
    }));
    await actions.send(newContent);
  },

  toggleLike(messageId) {
    const sid = isYanliChannel(state.activeId) ? findMessageOwner(messageId) : state.activeId;
    if (!sid) return;
    setState(prev => ({
      messages: {
        ...prev.messages,
        [sid]: prev.messages[sid].map(m =>
          m.id === messageId ? { ...m, liked: !m.liked } : m),
      },
    }));
    // fire-and-forget persist
    const m = state.messages[sid].find(x => x.id === messageId);
    window.chatApi.updateMessage(sid, messageId, { liked: m?.liked });
  },

  toggleBookmark(messageId) {
    const sid = isYanliChannel(state.activeId) ? findMessageOwner(messageId) : state.activeId;
    if (!sid) return;
    setState(prev => ({
      messages: {
        ...prev.messages,
        [sid]: prev.messages[sid].map(m =>
          m.id === messageId ? { ...m, bookmarked: !m.bookmarked } : m),
      },
    }));
    const m = state.messages[sid].find(x => x.id === messageId);
    window.chatApi.updateMessage(sid, messageId, { bookmarked: m?.bookmarked });
  },

  async resetSession() {
    try { await window.chatApi.resetSession(); } catch (e) { console.warn('[yanli] reset failed:', e); }
    const yanliIds = getYanliSessionIds();
    const divider = {
      id: `sys_${Date.now().toString(36)}`,
      role: 'system',
      content: '会话已重置',
      status: 'done',
      createdAt: new Date().toISOString(),
    };
    setState(prev => ({
      messages: {
        ...prev.messages,
        ...Object.fromEntries(yanliIds.map(id => [id, [divider]])),
      },
    }));
  },

  setUI(patch) { setState(prev => ({ ui: { ...prev.ui, ...patch } })); },
};

Object.assign(window, { useStore, getState, setState, actions });
})();
