import { fromCallback, fromPromise } from "xstate";

import {
  ChatSendResponse,
  UnlockPrivateResponse,
  type UiMessage,
} from "@/data/dto/chat";
import { chatMachine } from "@/stores/chat/chat-machine";
import type { ChatEvent } from "@/stores/chat/chat-events";
import type {
  UnlockMessageOutput as MachineUnlockMessageOutput,
  UnlockMessageRequest,
} from "@/stores/chat/helper/unlock";

export interface SendMessageHttpOutput {
  response: ChatSendResponse;
  reply: UiMessage | null;
}

export interface UnlockHistoryOutput {
  unlocked: boolean;
  reason: string;
  shortfallCredits: number;
  messages: UiMessage[];
}

export interface TestUnlockMessageOutput {
  messageId: string;
  response: UnlockPrivateResponse;
}

export function makeChatSendResponse(): ChatSendResponse {
  return ChatSendResponse.from({
    reply: "",
    audioUrl: "",
    messageId: "msg-1",
    isGuest: false,
    timestamp: Date.now(),
    image: { type: null, url: null },
    lockDetail: {
      locked: false,
      reason: null,
    },
  });
}

export function makeUnlockPrivateResponse(
  overrides: Partial<Parameters<typeof UnlockPrivateResponse.from>[0]> = {},
): UnlockPrivateResponse {
  return UnlockPrivateResponse.from({
    unlocked: true,
    content: "unlocked content",
    audioUrl: "",
    reason: "ok",
    creditBalance: 90,
    creditsCharged: 10,
    requiredCredits: 10,
    shortfallCredits: 0,
    ...overrides,
  });
}

export function createTestChatMachine(
  options: {
    historyMessages?: UiMessage[];
    sendMessageHttpError?: Error;
    unlockHistoryOutput?: UnlockHistoryOutput;
    unlockHistoryError?: Error;
    unlockMessageOutput?: TestUnlockMessageOutput;
  } = {},
) {
  return chatMachine.provide({
    actors: {
      loadHistory: createLoadHistoryCallback(options.historyMessages ?? []),
      sendMessageHttp: fromPromise<
        SendMessageHttpOutput,
        { content: string }
      >(async () => {
        if (options.sendMessageHttpError) {
          throw options.sendMessageHttpError;
        }
        return {
          response: makeChatSendResponse(),
          reply: null,
        };
      }),
      httpMessageQueue: fromCallback<ChatEvent>(({ receive, sendBack }) => {
        receive((event) => {
          if (event.type !== "ChatSendMessage") return;
          sendBack({ type: "ChatQueuedSendStarted" });
          sendBack({
            type: "ChatQueuedHttpDone",
            output: {
              response: makeChatSendResponse(),
              reply: null,
            },
          });
        });
        return () => undefined;
      }),
      unlockHistory: fromPromise<UnlockHistoryOutput>(async () => {
        if (options.unlockHistoryError) {
          throw options.unlockHistoryError;
        }
        return {
          unlocked: true,
          reason: "ok",
          shortfallCredits: 0,
          messages: [],
          ...options.unlockHistoryOutput,
        };
      }),
      unlockMessage: fromPromise<
        MachineUnlockMessageOutput,
        UnlockMessageRequest
      >(async ({ input }) => {
        const output = options.unlockMessageOutput ?? {
          messageId: input.messageId ?? input.displayMessageId,
          response: makeUnlockPrivateResponse(),
        };
        return {
          displayMessageId: input.displayMessageId,
          request: input,
          response: output.response,
        };
      }),
    },
  });
}

export function createLoadHistoryCallback(
  messages: UiMessage[],
  networkMessages: UiMessage[] = messages,
) {
  return fromCallback<ChatEvent>(({ sendBack }) => {
    sendBack({
      type: "ChatLocalHistoryLoaded",
      output: {
        messages,
        localCount: messages.length,
      },
    });
    sendBack({
      type: "ChatNetworkHistoryLoaded",
      output: {
        messages: networkMessages,
        localOverwritten: true,
        localCount: messages.length,
        networkCount: networkMessages.length,
      },
    });
    return () => undefined;
  });
}
