import type { UiMessage } from "@/data/dto/chat";
import { getChatRepository } from "@/data/repositories/chat_repository";
import { ChatSendResponse } from "@/data/dto/chat/chat_send_response";
import { todayString, Result, Logger } from "@/utils";

import type { ChatState, ChatUpgradeReason } from "./chat-state";

const log = new Logger("StoresChatChatMachineHelpers");

// ============================================================
// Constants
// ============================================================
/** 翻历史每页大小（仅 `loadMoreHistoryActor` 用） */
export const PAGE_SIZE = 50;

// ============================================================
// DTO ↔ UiMessage 映射
// ============================================================
/**
 * ChatMessage[] → UiMessage[]（纯函数）
 * - 与 Dart `LocalMessage → ChatMessage` 等价
 * - 业务事实：`role === "assistant"` → `isFromAI: true`
 */
export function localMessagesToUi(
  records: ReadonlyArray<{
    id?: string;
    type?: string;
    content: string;
    role: string;
    createdAt: string;
    audioUrl?: string | null;
    image?: { type: string | null; url: string | null };
    lockDetail?: {
      locked: boolean;
      showContent: boolean;
      showUpgrade: boolean;
      reason: string | null;
      hint: string | null;
      detail: Record<string, unknown> | null;
    };
  }>,
): UiMessage[] {
  return records.map((m) => ({
    ...(m.id ? { id: m.id } : {}),
    content: getAiMessageDisplayContent({
      content: m.content,
      isFromAI: m.role === "assistant",
      hasImage: Boolean(m.image?.url),
      lockDetail: m.lockDetail,
    }),
    isFromAI: m.role === "assistant",
    date: messageDateFromCreatedAt(m.createdAt),
    ...(m.audioUrl ? { audioUrl: m.audioUrl } : {}),
    ...deriveUiLockFields(m.lockDetail, m.image?.url),
  }));
}

export function countLockedHistoryMessages(messages: readonly UiMessage[]): number {
  return messages.filter(isUnlockableLockedMessage).length;
}

function isUnlockableLockedMessage(message: UiMessage): boolean {
  if (!message.isFromAI || message.locked !== true) return false;
  if (message.imagePaywalled === true) return true;
  return (
    message.lockReason === "private_message" ||
    message.lockReason === "voice_message"
  );
}

function messageDateFromCreatedAt(createdAt: string): string {
  const parsed = new Date(createdAt);
  if (Number.isNaN(parsed.getTime())) return todayString();
  return todayString(parsed);
}

function getAiMessageDisplayContent(input: {
  content: string;
  isFromAI: boolean;
  hasImage?: boolean;
  lockDetail?: {
    showContent: boolean;
    reason: string | null;
  };
}): string {
  if (input.lockDetail?.showContent === false) return "";
  return input.isFromAI && input.hasImage ? "" : input.content;
}

/**
 * ChatSendResponse → UiMessage（纯函数）
 * - 业务事实：后端响应就是 AI 的回复
 * - 用后端 `timestamp`（不用本地 time）—— 多设备/时区一致
 */
export function sendResponseToUiMessage(response: ChatSendResponse): UiMessage {
  return {
    ...(response.messageId ? { id: response.messageId } : {}),
    content: getAiMessageDisplayContent({
      content: response.reply,
      isFromAI: true,
      hasImage: Boolean(response.image.url),
      lockDetail: response.lockDetail,
    }),
    isFromAI: true,
    date: todayString(new Date(response.timestamp)),
    ...(response.audioUrl ? { audioUrl: response.audioUrl } : {}),
    ...deriveUiLockFields(response.lockDetail, response.image.url),
  };
}

function deriveUiLockFields(
  lockDetail:
    | {
        locked: boolean;
        showContent: boolean;
        showUpgrade: boolean;
        reason: string | null;
        hint: string | null;
      }
    | undefined,
  imageUrl?: string | null,
): Partial<UiMessage> {
  const lockReason = lockDetail?.reason ?? null;
  const isPrivateMessage = lockReason === "private_message";
  const isVoiceMessage = lockReason === "voice_message";
  return {
    ...(imageUrl ? { imageUrl } : {}),
    ...(imageUrl && lockDetail?.showUpgrade ? { imagePaywalled: true } : {}),
    ...(lockDetail ? { locked: lockDetail.locked, lockReason } : {}),
    ...(isPrivateMessage ? { isPrivate: true } : {}),
    ...(isPrivateMessage && lockDetail?.locked
      ? { lockedPrivate: true }
      : {}),
    ...((isPrivateMessage || isVoiceMessage) && lockDetail?.hint
      ? { privateMessageHint: lockDetail.hint }
      : {}),
  };
}

export type HttpSendOutput = {
  response: ChatSendResponse;
  reply: UiMessage | null;
};

export function beginPendingReply(context: ChatState): Pick<
  ChatState,
  "isReplyingAI" | "pendingReplyCount"
> {
  return {
    pendingReplyCount: context.pendingReplyCount + 1,
    isReplyingAI: true,
  };
}

export function finishPendingReply(context: ChatState): Pick<
  ChatState,
  "isReplyingAI" | "pendingReplyCount"
> {
  const pendingReplyCount = Math.max(0, context.pendingReplyCount - 1);
  return {
    pendingReplyCount,
    isReplyingAI: pendingReplyCount > 0,
  };
}

export function applyHttpSendOutput(
  context: ChatState,
  output: HttpSendOutput,
): Partial<ChatState> {
  const { response, reply } = output;

  const lockDetail = response.lockDetail;
  const isWeeklyLimitBlocked =
    lockDetail.locked &&
    lockDetail.showUpgrade &&
    lockDetail.reason === "weekly_limit";
  const isInsufficientCredits =
    response.canSendMessage === false &&
    response.cannotSendReason === "insufficient_credits";
  const upgradeReason: ChatUpgradeReason | null = isWeeklyLimitBlocked
    ? "weekly_limit"
    : isInsufficientCredits
      ? "insufficient_credits"
      : null;
  const sendCapabilityState = getSendCapabilityState(response);

  if (upgradeReason) {
    const lastMessage = context.messages[context.messages.length - 1];
    const messages =
      !reply && lastMessage && !lastMessage.isFromAI
        ? context.messages.slice(0, -1)
        : context.messages;

    return {
      messages: reply ? [...messages, reply] : messages,
      ...finishPendingReply(context),
      upgradePromptVisible: true,
      upgradeReason,
      ...sendCapabilityState,
    };
  }

  if (!reply) {
    return {
      ...finishPendingReply(context),
      ...sendCapabilityState,
    };
  }

  if (lockDetail.showUpgrade && lockDetail.reason === "private_message") {
    return {
      messages: [...context.messages, reply],
      ...finishPendingReply(context),
      upgradePromptVisible: false,
      upgradeReason: null,
      ...sendCapabilityState,
    };
  }

  return {
    messages: [...context.messages, reply],
    ...finishPendingReply(context),
    upgradePromptVisible: false,
    upgradeReason: null,
    ...sendCapabilityState,
  };
}

function getSendCapabilityState(
  response: ChatSendResponse,
): Pick<
  ChatState,
  | "canSendMessage"
  | "cannotSendReason"
  | "creditBalance"
  | "creditsCharged"
  | "requiredCredits"
  | "shortfallCredits"
> {
  return {
    canSendMessage: response.canSendMessage,
    cannotSendReason: response.cannotSendReason,
    creditBalance: response.creditBalance,
    creditsCharged: response.creditsCharged,
    requiredCredits: response.requiredCredits,
    shortfallCredits: response.shortfallCredits,
  };
}

export function applyHistoryLoadedOutput(
  output: {
    messages: UiMessage[];
    hasMore: boolean;
    newOffset: number;
  },
): Pick<
  ChatState,
  "messages" | "hasMore" | "historyOffset" | "historyLoaded"
> {
  return {
    messages: output.messages,
    hasMore: output.hasMore,
    historyOffset: output.newOffset,
    historyLoaded: true,
  };
}

export function shouldAutoUnlockHistory(messages: readonly UiMessage[]): boolean {
  return countLockedHistoryMessages(messages) === 1;
}

export function shouldPromptUnlockHistory(messages: readonly UiMessage[]): boolean {
  return countLockedHistoryMessages(messages) > 1;
}

// ============================================================
// Init 任务 2：history 3 步流（local → network → save）—— `loadHistoryActor` 调
// ============================================================
/**
 * 拉 history 流程（每步都记日志 —— 用户要求"不要删除"）
 *   1. 读 local（chatRepo.getLocalMessages）→ 返 localMessages
 *   2. 读 network（chatRepo.getHistory）→ 返 networkMessages
 *   3. 用 network 覆盖 local（chatRepo.saveMessagesToLocal）→ local 被同步
 *
 * 返回 network 最终 messages（authorititative），不返 local（UI 不需要过渡态）
 *
 * 保留日志（前缀 `[chat-machine]` 与 chat-machine.actors.ts 对齐）
 */
export async function readAndSyncHistory(): Promise<{
  messages: UiMessage[];        // network 最终 messages（authorititative）
  hasMore: boolean;
  newOffset: number;
  localOverwritten: boolean;     // true = local 已被 network 覆盖（写入 ChatStorage）
  /** 真实从 local 读到的消息数（调试用） */
  localCount: number;
  /** 真实从 network 读到的消息数（调试用） */
  networkCount: number;
}> {
  /**
   * 空历史首屏欢迎语（AI persona 首条消息）。
   * 触发条件：最终返回的 messages 为空（network 空 + local 空 / 或 network 失败且 local 空）。
   *
   * 不主动持久化到 local —— 这是纯 UI 层"首屏不空"的兜底：
   *   - 用户一旦发了消息，network 上有真实消息 → 后续 readAndSyncHistory
   *     返回的 messages 非空，不会再触发本条
   *   - 用户从未发过消息就关掉 app 再开，仍然没 network 消息 → 会再次显示本条
   *     （"每次冷启动给个温暖的起点" —— 若要"只显示一次"，需额外存 local）
   */
  const chatRepo = getChatRepository();
  const greetingMessage: UiMessage = {
    content:
      "You're here! Facebook is so restrictive, " +
      "there were things I couldn't say there. " +
      "Finally I can relax. How was your day out?",
    isFromAI: true,
    date: todayString(),
  };

  // 1. 读 local
  const localResult = await chatRepo.getLocalMessages();
  const localMessages =
    Result.isOk(localResult) && localResult.data
      ? localMessagesToUi(localResult.data)
      : [];
  log.debug("[chat-machine] loadHistory LOCAL DONE", {
    count: localMessages.length,
  });

  // 2. 读 network
  const networkResult = await chatRepo.getHistory(PAGE_SIZE, 0);
  if (!Result.isOk(networkResult)) {
    // network 失败 —— 返空 messages（不让 UI 卡住）
    log.error(
      "[chat-machine] loadHistory NETWORK FAILED",
      networkResult.success ? null : (networkResult as { error: unknown }).error,
    );

    // 空历史 → fallback 也走 welcome（不让屏幕空空如也）
    const fallbackMessages =
      localMessages.length === 0 ? [greetingMessage] : localMessages;
    if (fallbackMessages[0] === greetingMessage) {
      log.debug(
        "[chat-machine] loadHistory EMPTY → prepend greeting (network-failed path)",
      );
    }
    return {
      messages: fallbackMessages,
      hasMore: false,
      newOffset: fallbackMessages.length,
      localOverwritten: false,
      localCount: localMessages.length,
      networkCount: 0,
    };
  }
  const networkUi = localMessagesToUi(networkResult.data.messages);
  log.debug("[chat-machine] loadHistory NETWORK DONE", {
    count: networkUi.length,
  });
  void chatRepo.prefetchMediaForMessages(networkResult.data.messages);

  // 3. 用 network 覆盖 local
  const saveResult = await chatRepo.saveMessagesToLocal(networkResult.data.messages);
  const localOverwritten = Result.isOk(saveResult);
  log.debug("[chat-machine] loadHistory SAVE TO LOCAL DONE", {
    localOverwritten,
  });

  // 空历史 → prepend welcome（network 权威为空 = 用户从未发过消息）
  const finalMessages =
    networkUi.length === 0 ? [greetingMessage] : networkUi;
  if (finalMessages[0] === greetingMessage) {
    log.debug("[chat-machine] loadHistory EMPTY → prepend greeting");
  }

  return {
    messages: finalMessages, // authoritative（empty 时塞 greeting）
    hasMore: finalMessages.length >= PAGE_SIZE,
    newOffset: finalMessages.length,
    localOverwritten,
    localCount: localMessages.length,
    networkCount: networkUi.length,
  };
}
