/**
 * Chat 状态机：history / send / queue actors
 */

import { fromPromise, fromCallback } from "xstate";

import { MessageQueue } from "@/core/net/message-queue";
import type { ChatSendResponse } from "@/data/dto/chat";
import { getChatRepository } from "@/data/repositories/chat_repository";
import { Result, Logger } from "@/utils";

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

const log = new Logger("StoresChatChatMachineActors");

// ============================================================
// 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()`（多此一举）
 * - 返回完整 response + 可追加的 reply，方便处理后端 lockDetail 响应
 */
export const sendMessageHttpActor = fromPromise<
  { response: ChatSendResponse; reply: UiMessage | null },
  { content: string }
>(async ({ input }) => {
  return sendMessageViaHttp(input.content);
});

/** 翻历史（pagination）—— 不走 local-first，纯 server fetch */
export const loadMoreHistoryActor = fromPromise<
  { messages: UiMessage[]; hasMore: boolean; newOffset: number },
  { offset: number }
>(async ({ input }) => {
  const chatRepo = getChatRepository();
  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);
  void chatRepo.prefetchMediaForMessages(result.data.messages);
  return {
    messages: page,
    hasMore: page.length >= PAGE_SIZE,
    newOffset: input.offset + page.length,
  };
});

export const unlockHistoryActor = fromPromise<{
  unlocked: boolean;
  reason: string;
  shortfallCredits: number;
  messages: UiMessage[];
  hasMore: boolean;
  newOffset: number;
}>(async () => {
  const chatRepo = getChatRepository();
  const unlockResult = await chatRepo.unlockHistory();
  if (Result.isErr(unlockResult)) {
    log.error("[chat-machine] unlockHistoryActor failed", {
      error: unlockResult.error,
    });
    throw unlockResult.error;
  }

  const history = await readAndSyncHistory();
  return {
    unlocked: unlockResult.data.unlocked,
    reason: unlockResult.data.reason,
    shortfallCredits: unlockResult.data.shortfallCredits,
    messages: history.messages,
    hasMore: history.hasMore,
    newOffset: history.newOffset,
  };
});

export const httpMessageQueueActor = fromCallback<ChatEvent>(
  ({ sendBack, receive }) => {
    return createMessageQueueActor(sendBack, receive);
  },
);

function createMessageQueueActor(
  sendBack: (event: ChatEvent) => void,
  receive: (listener: (event: ChatEvent) => void) => void,
): () => void {
  const queue = new MessageQueue();

  queue.setConsumer(async (content) => {
    sendBack({ type: "ChatQueuedSendStarted" });
    try {
      const output = await sendMessageViaHttp(content);
      sendBack({ type: "ChatQueuedHttpDone", output });
    } catch (error) {
      const errorMessage =
        error instanceof Error ? error.message : "Message send failed";
      log.error("[chat-machine] message queue send failed", {
        error,
      });
      sendBack({
        type: "ChatQueuedSendError",
        content,
        errorMessage,
      });
    }
  });

  receive((event) => {
    if (event.type !== "ChatSendMessage") return;
    const content = event.content.trim();
    if (content.length === 0) return;
    queue.enqueue(content);
  });

  return () => queue.dispose();
}

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

async function sendMessageViaHttp(content: string): Promise<{
  response: ChatSendResponse;
  reply: UiMessage | null;
}> {
  const chatRepo = getChatRepository();
  const result = await chatRepo.sendMessage(content);
  if (Result.isErr(result)) {
    log.error("[chat-machine] sendMessageHttpActor failed", { error: result.error });
    throw result.error;
  }
  void chatRepo.prefetchMediaForSendResponse(result.data);
  const isMessageLimit =
    result.data.lockDetail.locked &&
    result.data.lockDetail.showUpgrade &&
    result.data.lockDetail.reason === "weekly_limit";
  const isInsufficientCredits =
    result.data.canSendMessage === false &&
    result.data.cannotSendReason === "insufficient_credits" &&
    !hasRenderableSendResponse(result.data);
  return {
    response: result.data,
    reply:
      isMessageLimit || isInsufficientCredits
        ? null
        : sendResponseToUiMessage(result.data),
  };
}

function hasRenderableSendResponse(response: ChatSendResponse): boolean {
  return (
    response.reply.trim().length > 0 ||
    response.audioUrl.trim().length > 0 ||
    Boolean(response.image.url) ||
    response.lockDetail.reason === "private_message" ||
    response.lockDetail.reason === "voice_message" ||
    response.lockDetail.reason === "image_paywall"
  );
}
