/**
 * Zustand Store — Agent 状态管理
 *
 * 管理 Agent 的实时状态：当前会话、消息流、流式输出、工具调用状态、Token 统计。
 */

import { create } from 'zustand';
import { useSessionStore } from './session-store';

// L-1: 消息 ID 防碰撞计数器
let _msgIdCounter = 0;
export const genMsgId = (role: string) => `msg_${Date.now()}_${role}_${_msgIdCounter++}`;

/**
 * L-16 修复: 提取上下文窗口默认值为命名常量
 * - Ollama 默认 4096（与 OllamaAdapter.DEFAULT_CONTEXT_WINDOW 保持一致）
 * - 云端 Provider（DeepSeek/Agnes）默认 1M（与各 adapter MODEL_INFO 保持一致）
 * 实际值会通过 getContextWindow() 或 listModels() 获取，此处仅为 UI 占位默认值
 */
const DEFAULT_OLLAMA_CONTEXT_WINDOW = 4096;
const DEFAULT_CLOUD_CONTEXT_WINDOW = 1_000_000;

// ===== 消息类型 =====

export type MessageRole = 'user' | 'assistant' | 'system' | 'tool';

export interface ToolCallInfo {
  id: string;
  name: string;
  args: Record<string, unknown>;
  status: 'pending' | 'executing' | 'success' | 'error' | 'blocked';
  result?: unknown;
  durationMs?: number;
  error?: string;
}

export interface AttachmentInfo {
  id: string;
  name: string;
  type: 'image' | 'text' | 'other';
  size: number;
  preview?: string;      // 图片 base64 data URL
  textContent?: string;  // 文本文件内容
}

export interface ChatMessage {
  id: string;
  role: MessageRole;
  content: string;
  timestamp: number;
  iteration?: number;
  toolCalls?: ToolCallInfo[];
  reasoningContent?: string;
  attachments?: AttachmentInfo[];
}

// ===== Agent 状态 =====

export type AgentStatus = 'idle' | 'thinking' | 'executing' | 'error';

// ===== Token 统计 =====

export interface TokenUsage {
  /** 累计输入 token（所有轮次累加） */
  inputTokens: number;
  /** 累计输出 token（所有轮次累加） */
  outputTokens: number;
  /** 累计总 token（所有轮次累加） */
  totalTokens: number;
  /**
   * v0.3.18 修复: 最近一次 LLM 调用的输入 token（单次上下文占用）
   * 用于计算"上下文占用百分比"——与累计值区分。
   * 压缩触发后，下一次 LLM 调用返回的 inputTokens 会大幅下降，
   * 反映压缩效果。累计值只增不减，无法体现压缩。
   */
  lastInputTokens: number;
  /**
   * v0.3.18 修复: 最近一次上下文压缩节省的 token 数
   * 用于在 UI 显示"压缩节省了 X tokens"
   */
  lastCompressedSaved: number;
}

// ===== Trace 步骤 =====

export interface TraceStep {
  id: string;
  iteration: number;
  state: string;
  states: string[];
  startedAt: number;
  completedAt?: number;
  thought?: string;
  toolCalls?: ToolCallInfo[];
  tokenUsage?: { promptTokens: number; completionTokens: number; totalTokens: number };
  /**
   * 创建该 step 的 runId，用于跨 run 隔离
   * 防御场景：abort 重发、并发 run、traceSteps 未清空时仍能正确识别新旧 step 边界
   */
  runId?: string;
}

// ===== Store 状态 =====

interface AgentState {
  // 会话
  currentSessionId: string | null;
  messages: ChatMessage[];
  agentStatus: AgentStatus;
  currentIteration: number;
  maxIterations: number;

  // Token 统计
  tokenUsage: TokenUsage;

  // Trace
  traceSteps: TraceStep[];

  // 流式
  isStreaming: boolean;

  // P1-6 修复: 配置加载完成标志 — 加载完成前禁用发送按钮
  configLoaded: boolean;

  // v0.3.18 修复: 工具就绪标志 — MCP 初始化完成前禁用发送按钮
  // 避免用户在工具不全时发送消息，导致子任务委派等 MCP 工具不可用
  toolsReady: boolean;

  // Run 标识（用于过滤旧流事件）
  currentRunId: string | null;

  // Provider
  provider: string;
  model: string;
  contextWindow: number;

  // Actions
  setCurrentSession: (id: string | null) => void;
  setMessages: (messages: ChatMessage[]) => void;
  addMessage: (message: ChatMessage) => void;
  updateMessage: (id: string, updates: Partial<ChatMessage>) => void;
  sendMessage: (content: string, images?: Array<{ url: string; detail?: 'low' | 'high' | 'auto' }>, attachments?: AttachmentInfo[]) => void;
  updateLastAssistantMessage: (delta: string) => void;
  setAgentStatus: (status: AgentStatus) => void;
  setStreaming: (streaming: boolean) => void;
  // P1-6 修复: 配置加载完成标志的 setter
  setConfigLoaded: (loaded: boolean) => void;
  // v0.3.18 修复: 工具就绪标志的 setter
  setToolsReady: (ready: boolean) => void;
  setCurrentRunId: (runId: string | null) => void;
  addTraceStep: (step: TraceStep) => void;
  updateTraceStep: (iteration: number, updates: Partial<TraceStep>) => void;
  updateTraceStepById: (id: string, updates: Partial<TraceStep>) => void;
  updateLastTraceStep: (updates: Partial<TraceStep>) => void;
  updateTokenUsage: (usage: Partial<TokenUsage>) => void;
  /**
   * v0.3.18 修复: 应用上下文压缩事件
   * 记录压缩节省的 token 数，用于 UI 展示压缩效果
   */
  applyCompression: (savedTokens: number) => void;
  setProvider: (provider: string, model: string) => void;
  setMaxIterations: (max: number) => void;
  setCurrentIteration: (n: number) => void;
  saveTraceData: () => void;
  clearMessages: () => void;
  abort: () => void;
}

export const useAgentStore = create<AgentState>((set, get) => ({
  // ===== 初始状态 =====
  currentSessionId: null,
  messages: [],
  agentStatus: 'idle',
  currentIteration: 0,
  maxIterations: 20,
  tokenUsage: { inputTokens: 0, outputTokens: 0, totalTokens: 0, lastInputTokens: 0, lastCompressedSaved: 0 },
  traceSteps: [],
  isStreaming: false,
  configLoaded: false,
  toolsReady: false,
  currentRunId: null,
  provider: '',
  model: '',
  contextWindow: 0,

  // ===== Actions =====

  setCurrentSession: (id) => {
    set({ currentSessionId: id, messages: [], traceSteps: [], currentIteration: 0, tokenUsage: { inputTokens: 0, outputTokens: 0, totalTokens: 0, lastInputTokens: 0, lastCompressedSaved: 0 }, agentStatus: 'idle', isStreaming: false, currentRunId: null });

    // 从数据库加载该会话的消息
    if (id && window.metona?.sessions?.getMessages) {
      window.metona.sessions.getMessages(id).then((msgs) => {
        // v0.3.0 修复: 竞态保护 — 快速切换会话时，旧请求返回后不再覆盖当前会话消息
        if (get().currentSessionId !== id) return;
        const messages = (msgs as Array<{
          id: string; role: string; content: string;
          reasoningContent?: string; toolCalls?: unknown[];
          toolResult?: unknown;
          attachments?: Array<{ id: string; name: string; type: string; size: number; preview?: string; textContent?: string }>;
          iteration?: number;
          timestamp: number;
        }>)
          // v0.3.0 修复: 过滤掉 tool 消息 — tool 结果已包含在 assistant 消息的 toolCalls 中
          // 独立的 tool 消息只用于 LLM API 上下文，不需要在前端显示为独立卡片
          .filter((m) => m.role !== 'tool')
          .map((m) => ({
            id: m.id,
            role: m.role as ChatMessage['role'],
            content: m.content,
            reasoningContent: m.reasoningContent,
            toolCalls: m.toolCalls as ToolCallInfo[] | undefined,
            attachments: m.attachments as AttachmentInfo[] | undefined,
            iteration: m.iteration,
            timestamp: m.timestamp,
          }));
        set({ messages });
      }).catch((err) => { console.error('[AgentStore]', err); });
    }

    // 从数据库加载该会话的 trace 步骤和 token 用量
    if (id && window.metona?.sessions?.getTrace) {
      window.metona.sessions.getTrace(id).then((data) => {
        // v0.3.0 修复: 竞态保护 — 快速切换会话时，旧请求返回后不再覆盖当前会话 trace
        if (get().currentSessionId !== id) return;
        if (data) {
          if (data.traceSteps) {
            // 兼容旧数据：为缺少 id/states 的 trace 步骤补全
            const steps = (data.traceSteps as TraceStep[]).map((t, i) => ({
              ...t,
              id: t.id ?? `trace_legacy_${t.iteration}_${t.state}_${i}`,
              states: t.states ?? [t.state],
            }));
            set({ traceSteps: steps });
          }
          // v0.3.18 修复: 兼容旧数据 — 旧 tokenUsage 没有 lastInputTokens/lastCompressedSaved 字段
          if (data.tokenUsage) {
            const old = data.tokenUsage as Partial<TokenUsage>;
            set({
              tokenUsage: {
                inputTokens: old.inputTokens ?? 0,
                outputTokens: old.outputTokens ?? 0,
                totalTokens: old.totalTokens ?? 0,
                lastInputTokens: old.lastInputTokens ?? 0,
                lastCompressedSaved: old.lastCompressedSaved ?? 0,
              },
            });
          }
        }
      }).catch((err) => { console.error('[AgentStore]', err); });
    }
  },

  setMessages: (messages) => set({ messages }),

  addMessage: (message) =>
    set((s) => ({ messages: [...s.messages, message] })),

  updateMessage: (id, updates) =>
    set((s) => ({
      messages: s.messages.map((m) =>
        m.id === id ? { ...m, ...updates } : m,
      ),
    })),

  sendMessage: async (content: string, images?: Array<{ url: string; detail?: 'low' | 'high' | 'auto' }>, attachments?: AttachmentInfo[]) => {
    let sessionId = get().currentSessionId;

    // v0.3.18 修复: 工具未就绪时阻止发送，避免 MCP 工具不可用
    if (!get().toolsReady) {
      import('@metona-team/metona-toast').then((mod) => mod.default.warning('工具正在加载中，请稍候...')).catch(() => {});
      return;
    }

    // 没有当前会话时自动创建
    if (!sessionId && window.metona?.sessions?.create) {
      try {
        const session = await window.metona.sessions.create() as { id: string; title: string; createdAt: number; updatedAt: number; messageCount: number; pinned: boolean; archived: boolean };
        useSessionStore.getState().addSession({
          id: session.id, title: session.title, createdAt: session.createdAt,
          updatedAt: session.updatedAt, messageCount: session.messageCount,
          pinned: session.pinned, archived: session.archived,
        });
        sessionId = session.id;
        set({ currentSessionId: sessionId });
        // v0.3.0 修复: 同步 session-store，确保 Sidebar 高亮和 Ctrl+[/] 可用
        useSessionStore.getState().setCurrentSession(sessionId);
      } catch (err) {
        console.error('[AgentStore]', 'Failed to create session:', err);
        set({ agentStatus: 'error', isStreaming: false });
        get().addMessage({
          id: genMsgId('error'),
          role: 'system',
          content: `错误: 无法创建会话 — ${(err as Error).message}`,
          timestamp: Date.now(),
        });
        // 额外弹 toast 作为通知，确保用户感知（system message 仅在聊天流内可见）
        import('@metona-team/metona-toast').then((mod) => mod.default.error(`无法创建会话：${(err as Error).message}`)).catch(() => {});
        return;
      }
    }

    const userMessage: ChatMessage = {
      id: genMsgId('user'),
      role: 'user',
      content,  // 用户可见内容（纯文本）
      timestamp: Date.now(),
      attachments: attachments && attachments.length > 0 ? attachments : undefined,
    };

    set((s) => ({
      messages: [...s.messages, userMessage],
      agentStatus: 'thinking',
      isStreaming: true,
      currentRunId: null, // 将在首个 streamEvent 中由 runId 设置
      currentIteration: 0,
      // 方案 A: 不清空 traceSteps，避免前一条消息的 trace 被永久覆盖
      // TraceViewer 按 runId 过滤显示，只展示当前 run 的 steps
      // 历史 trace 仍在 DB 中，切换会话回来可恢复
      tokenUsage: { inputTokens: 0, outputTokens: 0, totalTokens: 0, lastInputTokens: 0, lastCompressedSaved: 0 },
    }));

    // 自动更新会话标题为用户第一条消息
    if (sessionId && window.metona?.sessions?.getMessages) {
      window.metona.sessions.getMessages(sessionId).then((msgs) => {
        if (msgs.length <= 1) {
          // 这是第一条消息，更新标题
          const title = content.length > 30 ? content.slice(0, 30) + '...' : content;
          window.metona?.sessions?.rename(sessionId, title);
          useSessionStore.getState().updateSession(sessionId, { title });
        }
      }).catch((err) => { console.error('[AgentStore]', err); });
    }

    if (sessionId && window.metona?.agent?.sendMessage) {
      // 构建发给 LLM 的消息
      // images 参数由 ChatInput 传入（已从附件中提取 base64 data URL）
      let llmContent = content;
      const llmImages: Array<{ url: string; detail?: 'low' | 'high' | 'auto' }> = [
        ...(images ?? []),
      ];

      if (attachments && attachments.length > 0) {
        const parts: string[] = [];
        for (const att of attachments) {
          if (att.type === 'image') {
            // 图片已在 images 参数中处理，跳过
            continue;
          }
          // 非图片文件：JSON 结构化，原始文本内容（不编码）
          const ext = att.name.split('.').pop() ?? 'unknown';
          const rawContent = att.textContent ?? '';
          parts.push(JSON.stringify({
            file_name: att.name,
            file_type: ext,
            content: rawContent,
          }));
        }
        llmContent = parts.length > 0
          ? (content ? content + '\n\n' : '') + parts.join('\n')
          : content;
      }

      const messageWithImages = {
        ...userMessage,
        content: llmContent,
        images: llmImages.length > 0 ? llmImages : undefined,
      };
      window.metona.agent.sendMessage(messageWithImages, sessionId).catch((err) => {
        console.error('[AgentStore]', 'IPC sendMessage failed:', err);
        set({ agentStatus: 'error', isStreaming: false });
        get().addMessage({
          id: genMsgId('error'),
          role: 'system',
          content: `错误: 消息发送失败 — ${(err as Error).message}`,
          timestamp: Date.now(),
        });
        // 额外弹 toast 作为通知，确保用户感知（system message 仅在聊天流内可见）
        import('@metona-team/metona-toast').then((mod) => mod.default.error(`消息发送失败：${(err as Error).message}`)).catch(() => {});
      });
    }
  },

  updateLastAssistantMessage: (delta: string) => {
    set((s) => {
      const messages = [...s.messages];
      const lastIdx = messages.length - 1;
      const lastMsg = messages[lastIdx];
      if (lastMsg?.role === 'assistant') {
        // 不可变更新：创建新对象而非直接突变
        messages[lastIdx] = { ...lastMsg, content: lastMsg.content + delta };
      } else {
        messages.push({
          id: genMsgId('assistant'),
          role: 'assistant',
          content: delta,
          timestamp: Date.now(),
          iteration: s.currentIteration || undefined,
        });
      }
      return { messages };
    });
  },

  setAgentStatus: (status) => set({ agentStatus: status }),

  setStreaming: (streaming) => set({ isStreaming: streaming }),

  setConfigLoaded: (loaded) => set({ configLoaded: loaded }),
  // v0.3.18 修复: 工具就绪标志 setter
  setToolsReady: (ready) => set({ toolsReady: ready }),

  setCurrentRunId: (runId) => set({ currentRunId: runId }),

  addTraceStep: (step) =>
    set((s) => ({ traceSteps: [...s.traceSteps, step] })),

  updateTraceStep: (iteration, updates) =>
    set((s) => ({
      traceSteps: s.traceSteps.map((t) =>
        t.iteration === iteration ? { ...t, ...updates } : t,
      ),
    })),

  // L-2: 按 ID 精确匹配 traceStep（避免 iteration 碰撞）
  updateTraceStepById: (id, updates) =>
    set((s) => ({
      traceSteps: s.traceSteps.map((t) =>
        t.id === id ? { ...t, ...updates } : t,
      ),
    })),

  updateLastTraceStep: (updates) =>
    set((s) => {
      if (s.traceSteps.length === 0) return s;
      const steps = [...s.traceSteps];
      steps[steps.length - 1] = { ...steps[steps.length - 1], ...updates };
      return { traceSteps: steps };
    }),

  updateTokenUsage: (usage) =>
    set((s) => ({ tokenUsage: { ...s.tokenUsage, ...usage } })),

  // v0.3.18 修复: 应用上下文压缩事件，记录节省的 token 数
  // 压缩后下一轮 LLM 调用的 inputTokens 会大幅下降，lastInputTokens 会自动反映
  applyCompression: (savedTokens) =>
    set((s) => ({ tokenUsage: { ...s.tokenUsage, lastCompressedSaved: savedTokens } })),

  setProvider: (provider, model) => {
    // L-16 修复: 使用命名常量替代魔法数字
    // v0.3.1: DeepSeek/Agnes 不再固定 1M，从配置读取
    const initialCtx = provider === 'ollama' ? DEFAULT_OLLAMA_CONTEXT_WINDOW : DEFAULT_CLOUD_CONTEXT_WINDOW;
    set({ provider, model, contextWindow: initialCtx });
    // v0.3.1: 异步读取实际配置（所有 Provider）
    if (window.metona?.config?.get) {
      // Ollama 用 ollama.numCtx，DeepSeek 用 deepseek.contextWindow，Agnes 用 agnes.contextWindow
      const configKey = provider === 'ollama' ? 'ollama.numCtx' : `${provider}.contextWindow`;
      // P2-12 修复: 竞态保护 — 快速切换 provider 时，旧 Promise resolve 不覆盖新值
      const expectedProvider = provider;
      window.metona.config.get(configKey).then((v) => {
        if (get().provider !== expectedProvider) return; // provider 已切换，丢弃旧结果
        if (v != null && typeof v === 'number' && v > 0) {
          set({ contextWindow: v });
        }
      }).catch((err) => { console.error('[AgentStore]', err); });
    }
  },

  setMaxIterations: (max) => set({ maxIterations: max }),

  setCurrentIteration: (currentIteration) => set({ currentIteration }),

  saveTraceData: () => {
    const { currentSessionId, traceSteps, tokenUsage } = get();
    if (currentSessionId && window.metona?.sessions?.saveTrace) {
      window.metona.sessions.saveTrace(currentSessionId, { traceSteps, tokenUsage }).catch((err) => { console.error('[AgentStore]', err); });
    }
  },

  clearMessages: () =>
    set({
      messages: [],
      agentStatus: 'idle',
      currentIteration: 0,
      tokenUsage: { inputTokens: 0, outputTokens: 0, totalTokens: 0, lastInputTokens: 0, lastCompressedSaved: 0 },
      traceSteps: [],
      isStreaming: false,
      currentRunId: null,
    }),

  abort: () => {
    const sessionId = get().currentSessionId;
    // 不清除 currentRunId — 保留用于过滤旧 run 的延迟终止事件（DONE/TERMINATED）
    // 旧 run 的 DONE 到达时会匹配 currentRunId，处理后清除；新 run 的 INIT 会覆盖
    set({ agentStatus: 'idle', isStreaming: false });
    if (sessionId && window.metona?.agent?.abortSession) {
      window.metona.agent.abortSession(sessionId).catch((err) => { console.error('[AgentStore]', err); });
    }
  },
}));
