/**
 * Chat 状态机：4 个 XState actor
 */

import { fromPromise, fromCallback } from "xstate";

import { createChatWebSocket, ChatWebSocket } from "@/core/net/chat-websocket";
import { Result, Logger } from "@/utils";

import {
  PAGE_SIZE,
  chatRepo,
  localMessagesToUi,
  readAndSyncHistory,
  readGuestQuota,
  sendResponseToUiMessage,
} from "./chat-machine.helpers";
import type { ChatEvent } from "./chat-events";

const log = new Logger("StoresChatChatMachineActors");

// ============================================================
// 共享 WS 引用（chatWebSocketActor ↔ sendMessageWsActor）
// ============================================================
/**
 * 活跃 ChatWebSocket 单例引用（module-level）。
 * - `chatWebSocketActor` 在 connect 后写入、cleanup 时清空
 * - `sendMessageWsActor` 读取并调 `ws.sendMessage(content)`
 *
 * 为什么用 module-level 而非 context.wsRef：
 *   1) `ChatWebSocket` 是带回调 + 重连 timer 的非可序列化对象，放进
 *      XState context 会污染快照、警告 non-serializable
 *   2) userSession 同一时间只可能有一个活跃 WS（parent-level invoke），
 *      module 单例的语义和"userSession 内一个活跃连接"一一对应
 *   3) 不需要重构 ChatWebSocket 类的对外 API
 */
let activeChatWebSocket: ChatWebSocket | null = null;

/** 暴露给 `sendMessageWsActor` 读取（测试也可 import） */
export function getActiveChatWebSocket(): ChatWebSocket | null {
  return activeChatWebSocket;
}

// ============================================================
// Init 任务 1：拉游客配额（fromPromise，纯 local）
// ============================================================
/**
 * 拉游客日配 + 总配（仅 guestSession 期间 invoke）
 * - 失败返 `{ remaining: 0, total: 0 }`（不抛，让 UI 还是能进 ready）
 * - onDone 在 state machine 里赋 guestRemainingQuota + guestTotalQuota + 设 quotaLoaded: true
 */
export const loadQuotaActor = fromPromise<{
  remaining: number;
  total: number;
}>(async () => {
  return readGuestQuota();
});

// ============================================================
// Init 任务 2：拉 history 3 步流（fromPromise，返回最终结果）
// ============================================================
/**
 * local → network → save network to local 3 步流（返回 network 最终 messages）
 * - 选方案 A（fromPromise）：UI 不会看到 local 中间态，直接显示 network 结果
 * - 内部 3 步走 helper `readAndSyncHistory`（日志全在 helper 里）
 * - 失败返 local（退而求其次）
 */
export const loadHistoryActor = fromPromise<{
  messages: UiMessage[];
  hasMore: boolean;
  newOffset: number;
  localOverwritten: boolean;
  localCount: number;
  networkCount: number;
}>(async () => {
  return readAndSyncHistory();
});

// ============================================================
// HTTP: one-shot Promise
// ============================================================
/**
 * HTTP 发送消息（一次发送 = 一次返回）
 * - 后端响应就是 AI 回复（`ChatSendResponse.reply: string`）
 * - 不再调 `getLocalMessages()`（多此一举）
 * - 返回 `{ reply: UiMessage }` —— `sending.onDone` 把它追加到 `context.messages`
 */
export const sendMessageHttpActor = fromPromise<
  { reply: UiMessage },
  { content: string }
>(async ({ input }) => {
  const result = await chatRepo.sendMessage(input.content);
  if (Result.isErr(result)) {
    log.error("[chat-machine] sendMessageHttpActor failed", { error: result.error });
    throw result.error;
  }
  return { reply: sendResponseToUiMessage(result.data) };
});

/** 翻历史（pagination）—— 不走 local-first，纯 server fetch */
export const loadMoreHistoryActor = fromPromise<
  { messages: UiMessage[]; hasMore: boolean; newOffset: number },
  { offset: number }
>(async ({ input }) => {
  const result = await chatRepo.getHistory(PAGE_SIZE, input.offset);
  if (Result.isErr(result)) {
    log.error("[chat-machine] loadMoreHistoryActor failed", { error: result.error });
    throw result.error;
  }
  const page = localMessagesToUi(result.data.messages);
  return {
    messages: page,
    hasMore: page.length >= PAGE_SIZE,
    newOffset: input.offset + page.length,
  };
});

// ============================================================
// WebSocket: long-lived callback
// ============================================================
/**
 * 长生命周期 WebSocket（仅 `userSession` 期间 invoke）
 * - 启动：连 WS，事件 → sendBack 派回机器
 * - 停止（cleanup）：disconnect
 * - 游客不 invoke 此 actor（业务事实"游客不连 WS"）
 *
 * 之所以用 fromCallback 而非 fromPromise：
 * - WS 是长连接，不是一次性 Promise
 * - fromCallback 提供 `sendBack` 回调，actor 内部事件 → 机器
 * - cleanup 函数 → XState 在 parent state exit 时自动调用
 */
export const chatWebSocketActor = fromCallback<ChatEvent, { token: string }>(
  ({ sendBack, input }) => {
    const ws = createChatWebSocket(input.token);
    activeChatWebSocket = ws;

    ws.onConnected = (userId) => {
      log.debug("[chat-machine] WS connected", { userId });
      sendBack({ type: "ChatWebSocketConnected", userId });
    };
    ws.onSentence = (p) => {
      sendBack({
        type: "ChatAISentenceReceived",
        index: p.index,
        text: p.text,
        total: p.total,
        done: p.done,
      });
    };
    ws.onError = (msg) => {
      log.error("[chat-machine] WS error", { errorMessage: msg });
      sendBack({ type: "ChatWebSocketError", errorMessage: msg });
    };
    ws.connect();
    return () => {
      log.debug("[chat-machine] chatWebSocketActor cleanup");
      if (activeChatWebSocket === ws) {
        activeChatWebSocket = null;
      }
      ws.disconnect();
    };
  },
);

// ============================================================
// WebSocket: per-send callback
// ============================================================
/**
 * WS 发送消息（userSession.sendingViaWs 期间 invoke）
 * - 走 `activeChatWebSocket.sendMessage(content)`
 * - 成功后 AI 回复通过 `chatWebSocketActor` 的 `onSentence` → `ChatAISentenceReceived` 流回
 * - 失败抛错 → state machine 的 `onError` 兜底（清 isReplyingAI、回 ready）
 *
 * 与 sendMessageHttpActor 的区别：
 *   - sendMessageHttpActor: 一次发送 = 一次返回（HTTP response = AI reply）
 *   - sendMessageWsActor: 一次发送 = 0 次直接返回（WS 异步流式回 reply，actor 本身不返 reply）
 *
 * 选 fromPromise 而非 fromCallback：
 *   - 本 actor 是 fire-and-forget —— sync 内同步执行 ws.sendMessage，立即 resolve
 *   - actor 本身没有"完成"语义（done 来自 WS 事件流，不来自这个 actor）
 *   - 抛错走 onError 通道
 *   - fromPromise 的 input type 推断比 fromCallback<void, ...> 稳定（XState v5 已知坑）
 */
export const sendMessageWsActor = fromPromise<void, { content: string }>(
  async ({ input }) => {
    const ws = getActiveChatWebSocket();
    if (!ws) {
      log.error("[chat-machine] sendMessageWsActor no active WebSocket");
      throw new Error("[chat-machine] sendMessageWs: no active WebSocket (actor not running)");
    }
    const ok = ws.sendMessage(input.content);
    if (!ok) {
      log.error("[chat-machine] sendMessageWsActor WebSocket not OPEN");
      throw new Error("[chat-machine] sendMessageWs: WebSocket not OPEN");
    }
    // resolve 不返值 —— AI reply 由 chatWebSocketActor 的 ChatAISentenceReceived 流回
  },
);

// Re-export `UiMessage` type for the chat-machine.ts public API
type UiMessage = import("@/data/dto/chat").UiMessage;