import { describe, expect, it } from "vitest";

import { ChatSendResponse } from "@/data/dto/chat/chat_send_response";
import type { ChatState } from "@/stores/chat/chat-state";
import {
  applyHttpSendOutput,
  countLockedHistoryMessages,
  localMessagesToUi,
  sendResponseToUiMessage,
} from "@/stores/chat/chat-machine.helpers";

function makeResponse(
  overrides: Partial<Parameters<typeof ChatSendResponse.from>[0]> = {},
): ChatSendResponse {
  return ChatSendResponse.from({
    reply: "AI reply",
    audioUrl: "",
    messageId: "msg-1",
    isGuest: false,
    timestamp: 1782356425363,
    image: { type: null, url: null },
    lockDetail: {
      locked: false,
      showContent: true,
      showUpgrade: false,
      reason: null,
      hint: null,
      detail: null,
    },
    ...overrides,
  });
}

function makeChatState(overrides: Partial<ChatState> = {}): ChatState {
  return {
    messages: [],
    isReplyingAI: true,
    pendingReplyCount: 1,
    upgradePromptVisible: false,
    upgradeReason: null,
    canSendMessage: true,
    cannotSendReason: null,
    creditBalance: 0,
    creditsCharged: 0,
    requiredCredits: 0,
    shortfallCredits: 0,
    isLoadingMore: false,
    hasMore: true,
    historyOffset: 0,
    historyLoaded: true,
    paymentUnlockPending: false,
    unlockHistoryPromptVisible: false,
    lockedHistoryCount: 0,
    isUnlockingHistory: false,
    unlockHistoryError: null,
    ...overrides,
  };
}

describe("sendResponseToUiMessage", () => {
  it("maps locked voice messages without treating them as private text", () => {
    const message = sendResponseToUiMessage(
      makeResponse({
        audioUrl:
          "https://proapi.banlv-ai.com/audio/5198b93c-2915-406a.mp3",
        lockDetail: {
          locked: true,
          showContent: false,
          showUpgrade: true,
          reason: "voice_message",
          hint: "这语气好撩呀，让人有点心跳加速呢…",
          detail: { messageId: "msg-1" },
        },
      }),
    );

    expect(message.content).toBe("");
    expect(message.audioUrl).toBe(
      "https://proapi.banlv-ai.com/audio/5198b93c-2915-406a.mp3",
    );
    expect(message.locked).toBe(true);
    expect(message.lockReason).toBe("voice_message");
    expect(message.lockedPrivate).toBeUndefined();
    expect(message.privateMessageHint).toBe(
      "这语气好撩呀，让人有点心跳加速呢…",
    );
  });

  it("maps locked private text messages as private messages", () => {
    const message = sendResponseToUiMessage(
      makeResponse({
        lockDetail: {
          locked: true,
          showContent: false,
          showUpgrade: true,
          reason: "private_message",
          hint: "Elio has a private message for you.",
          detail: { messageId: "msg-1" },
        },
      }),
    );

    expect(message.content).toBe("");
    expect(message.locked).toBe(true);
    expect(message.lockReason).toBe("private_message");
    expect(message.isPrivate).toBe(true);
    expect(message.lockedPrivate).toBe(true);
    expect(message.privateMessageHint).toBe(
      "Elio has a private message for you.",
    );
  });

  it("keeps paywalled image messages visible but locked", () => {
    const message = sendResponseToUiMessage(
      makeResponse({
        image: {
          type: "jpg",
          url: "https://example.com/private-photo.jpg",
        },
        lockDetail: {
          locked: true,
          showContent: false,
          showUpgrade: true,
          reason: "image",
          hint: "Activate VIP to view the full photo.",
          detail: { imageId: "image-1" },
        },
      }),
    );

    expect(message.content).toBe("");
    expect(message.imageUrl).toBe("https://example.com/private-photo.jpg");
    expect(message.imagePaywalled).toBe(true);
    expect(message.locked).toBe(true);
    expect(message.lockReason).toBe("image");
    expect(message.lockedPrivate).toBeUndefined();
  });
});

describe("applyHttpSendOutput", () => {
  it("removes the optimistic user message when the weekly limit is reached", () => {
    const context = makeChatState({
      messages: [
        {
          content: "hello",
          isFromAI: false,
          date: "2026-06-25",
        },
      ],
    });

    const nextState = applyHttpSendOutput(context, {
      response: makeResponse({
        reply: "",
        messageId: "",
        lockDetail: {
          locked: true,
          showContent: false,
          showUpgrade: true,
          reason: "weekly_limit",
          hint: "Free message limit reached.",
          detail: {
            type: "weekly_msg_limit",
            usedThisWeek: 3,
            limit: 3,
          },
        },
      }),
      reply: null,
    });

    expect(nextState.messages).toEqual([]);
    expect(nextState.isReplyingAI).toBe(false);
    expect(nextState.upgradePromptVisible).toBe(true);
    expect(nextState.upgradeReason).toBe("weekly_limit");
  });

  it("stores insufficient credit state and removes failed optimistic messages", () => {
    const context = makeChatState({
      messages: [
        {
          content: "hello",
          isFromAI: false,
          date: "2026-06-25",
        },
      ],
    });

    const nextState = applyHttpSendOutput(context, {
      response: makeResponse({
        reply: "",
        messageId: "",
        canSendMessage: false,
        cannotSendReason: "insufficient_credits",
        creditBalance: 0,
        creditsCharged: 0,
        requiredCredits: 2,
        shortfallCredits: 2,
        lockDetail: {
          locked: true,
          showContent: false,
          showUpgrade: true,
          reason: "insufficient_credits",
          hint: "Insufficient credits.",
          detail: {
            requiredCredits: 2,
            currentCredits: 0,
            shortfallCredits: 2,
          },
        },
      }),
      reply: null,
    });

    expect(nextState.messages).toEqual([]);
    expect(nextState.isReplyingAI).toBe(false);
    expect(nextState.upgradePromptVisible).toBe(true);
    expect(nextState.upgradeReason).toBe("insufficient_credits");
    expect(nextState.canSendMessage).toBe(false);
    expect(nextState.cannotSendReason).toBe("insufficient_credits");
    expect(nextState.requiredCredits).toBe(2);
    expect(nextState.shortfallCredits).toBe(2);
  });

  it("keeps a valid reply while marking future sends as unavailable", () => {
    const context = makeChatState({
      messages: [
        {
          content: "hello",
          isFromAI: false,
          date: "2026-06-25",
        },
      ],
    });
    const reply = sendResponseToUiMessage(
      makeResponse({
        reply: "This one went through, but you need credits next.",
        canSendMessage: false,
        cannotSendReason: "insufficient_credits",
        creditBalance: 0,
        requiredCredits: 2,
        shortfallCredits: 2,
      }),
    );

    const nextState = applyHttpSendOutput(context, {
      response: makeResponse({
        reply: "This one went through, but you need credits next.",
        canSendMessage: false,
        cannotSendReason: "insufficient_credits",
        creditBalance: 0,
        requiredCredits: 2,
        shortfallCredits: 2,
      }),
      reply,
    });

    expect(nextState.messages).toHaveLength(2);
    expect(nextState.messages?.[1]).toMatchObject({
      content: "This one went through, but you need credits next.",
      isFromAI: true,
    });
    expect(nextState.upgradePromptVisible).toBe(true);
    expect(nextState.upgradeReason).toBe("insufficient_credits");
    expect(nextState.canSendMessage).toBe(false);
  });
});

describe("localMessagesToUi", () => {
  it("maps locked voice messages from history", () => {
    const [message] = localMessagesToUi([
      {
        id: "voice-1",
        role: "assistant",
        type: "voice",
        content: "hidden voice transcript",
        createdAt: "2026-06-25T12:00:00.000Z",
        audioUrl: "https://example.com/voice.mp3",
        image: { type: null, url: null },
        lockDetail: {
          locked: true,
          showContent: false,
          showUpgrade: true,
          reason: "voice_message",
          hint: "A locked voice message is waiting.",
          detail: { messageId: "voice-1" },
        },
      },
    ]);

    expect(message.content).toBe("");
    expect(message.audioUrl).toBe("https://example.com/voice.mp3");
    expect(message.locked).toBe(true);
    expect(message.lockReason).toBe("voice_message");
    expect(message.lockedPrivate).toBeUndefined();
    expect(message.privateMessageHint).toBe("A locked voice message is waiting.");
  });

  it("hides text content for image history messages", () => {
    const [message] = localMessagesToUi([
      {
        id: "image-1",
        role: "assistant",
        type: "image",
        content: "This text should not render with an image.",
        createdAt: "2026-06-25T12:00:00.000Z",
        audioUrl: null,
        image: {
          type: "jpg",
          url: "https://example.com/image.jpg",
        },
        lockDetail: {
          locked: false,
          showContent: true,
          showUpgrade: false,
          reason: null,
          hint: null,
          detail: null,
        },
      },
    ]);

    expect(message.content).toBe("");
    expect(message.imageUrl).toBe("https://example.com/image.jpg");
    expect(message.imagePaywalled).toBeUndefined();
  });
});

describe("countLockedHistoryMessages", () => {
  it("counts only unlockable locked AI messages", () => {
    expect(
      countLockedHistoryMessages([
        {
          content: "",
          isFromAI: true,
          date: "2026-06-29",
          locked: true,
          lockReason: "private_message",
        },
        {
          content: "",
          isFromAI: true,
          date: "2026-06-29",
          locked: true,
          lockReason: "voice_message",
        },
        {
          content: "",
          isFromAI: true,
          date: "2026-06-29",
          locked: true,
          imageUrl: "https://example.com/locked.jpg",
          imagePaywalled: true,
          lockReason: "image",
        },
        {
          content: "",
          isFromAI: true,
          date: "2026-06-29",
          locked: true,
          lockReason: "weekly_limit",
        },
        {
          content: "user text",
          isFromAI: false,
          date: "2026-06-29",
          locked: true,
          lockReason: "private_message",
        },
      ]),
    ).toBe(3);
  });
});
