import { fromPromise } from "xstate";

import { getChatRepository } from "@/data/repositories/chat_repository";
import { Logger, Result } from "@/utils";

import {
  chatAssign,
  type ChatActionArgs,
  type ChatContextArgs,
} from "./chat-flow-actions";
import { readAndSyncHistory } from "./chat-history-sync";
import {
  applySingleUnlockOutput,
  countLockedHistoryMessages,
  type UnlockMessageOutput,
} from "./chat-machine.helpers";

const log = new Logger("StoresChatChatUnlockFlow");

type UiMessage = import("@/data/dto/chat").UiMessage;

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 unlockMessageActor = fromPromise<
  UnlockMessageOutput,
  { messageId: string }
>(async ({ input }) => {
  const chatRepo = getChatRepository();
  const unlockResult = await chatRepo.unlockPrivateMessage(input.messageId);
  if (Result.isErr(unlockResult)) {
    log.error("[chat-machine] unlockMessageActor failed", {
      messageId: input.messageId,
      error: unlockResult.error,
    });
    throw unlockResult.error;
  }

  if (unlockResult.data.unlocked) {
    const markResult = await chatRepo.markPrivateMessageUnlockedInLocal(
      input.messageId,
      unlockResult.data.lockDetail,
    );
    if (Result.isErr(markResult)) {
      log.warn("[chat-machine] mark unlocked local message failed", {
        messageId: input.messageId,
        error: markResult.error,
      });
    }
  }

  return {
    messageId: input.messageId,
    response: unlockResult.data,
  };
});

export const markPaymentUnlockPendingAction = chatAssign(() => ({
  paymentUnlockPending: true,
  unlockHistoryError: null,
}));

export const showUnlockHistoryPromptAction = chatAssign(
  ({ context }: ChatContextArgs) => ({
    paymentUnlockPending: false,
    unlockHistoryPromptVisible: true,
    lockedHistoryCount: countLockedHistoryMessages(context.messages),
    unlockHistoryError: null,
  }),
);

export const dismissUnlockHistoryPromptAction = chatAssign(() => ({
  paymentUnlockPending: false,
  unlockHistoryPromptVisible: false,
  unlockHistoryError: null,
}));

export const markUnlockHistoryStartedAction = chatAssign(() => ({
  paymentUnlockPending: false,
  unlockHistoryPromptVisible: false,
  isUnlockingHistory: true,
  unlockHistoryError: null,
}));

export const markUnlockHistoryFailedAction = chatAssign(() => ({
  isUnlockingHistory: false,
  unlockHistoryPromptVisible: true,
  unlockHistoryError: "Failed to unlock messages. Please try again.",
}));

export const markUnlockMessageStartedAction = chatAssign(
  ({ event }: ChatActionArgs) => {
    if (event.type !== "ChatUnlockMessageRequested") return {};
    return {
      isUnlockingMessage: true,
      unlockingMessageId: event.messageId,
      unlockingMessageKind: event.kind,
      unlockMessageError: null,
      unlockPaywallRequest: null,
    };
  },
);

export const applyUnlockMessageSucceededAction = chatAssign(
  ({ context, event }: ChatActionArgs) => {
    const output = (event as { output?: UnlockMessageOutput }).output;
    if (!output) return {};
    return {
      messages: applySingleUnlockOutput(context.messages, output),
      isUnlockingMessage: false,
      unlockingMessageId: null,
      unlockingMessageKind: null,
      unlockMessageError: null,
      unlockPaywallRequest: null,
      creditBalance: output.response.creditBalance,
      creditsCharged: output.response.creditsCharged,
      requiredCredits: 0,
      shortfallCredits: 0,
    };
  },
);

export const requestUnlockPaymentFromOutputAction = chatAssign(
  ({ context, event }: ChatActionArgs) => {
    const output = (event as { output?: UnlockMessageOutput }).output;
    if (!output) return {};
    return {
      isUnlockingMessage: false,
      unlockingMessageId: null,
      unlockingMessageKind: null,
      unlockMessageError: output.response.reason,
      unlockPaywallRequest: {
        messageId: output.messageId,
        kind: context.unlockingMessageKind ?? "private",
        reason: output.response.reason,
        creditBalance: output.response.creditBalance,
        requiredCredits: output.response.requiredCredits,
        shortfallCredits: output.response.shortfallCredits,
      },
      creditBalance: output.response.creditBalance,
      requiredCredits: output.response.requiredCredits,
      shortfallCredits: output.response.shortfallCredits,
    };
  },
);

export const requestUnlockPaymentFromErrorAction = chatAssign(
  ({ context }: ChatContextArgs) => ({
    isUnlockingMessage: false,
    unlockMessageError: "unlock_failed",
    unlockPaywallRequest:
      context.unlockingMessageId && context.unlockingMessageKind
        ? {
            messageId: context.unlockingMessageId,
            kind: context.unlockingMessageKind,
            reason: "unlock_failed",
            creditBalance: context.creditBalance,
            requiredCredits: context.requiredCredits,
            shortfallCredits: context.shortfallCredits,
          }
        : null,
    unlockingMessageId: null,
    unlockingMessageKind: null,
  }),
);

export const clearUnlockPaywallRequestAction = chatAssign(() => ({
  unlockPaywallRequest: null,
}));
export type { UnlockMessageOutput } from "./chat-machine.helpers";
