import { type DoneActorEvent } from "xstate";

import { applyPromotionUnlockOutput } from "../helper/promotion";
import {
  applySingleUnlockOutput,
  countLockedHistoryMessages,
  type UnlockMessageOutput,
} from "../helper/unlock";
import type { UnlockHistoryOutput } from "./actors/unlock";
import { sendMachineSetup } from "./send-flow";
import { createChatActorActionSetup } from "./setup";

const markPaymentUnlockPendingAction = sendMachineSetup.assign(() => ({
  paymentUnlockPending: true,
  unlockHistoryError: null,
}));

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

const dismissUnlockHistoryPromptAction = sendMachineSetup.assign(() => ({
  paymentUnlockPending: false,
  unlockHistoryPromptVisible: false,
  unlockHistoryError: null,
}));

const markUnlockHistoryStartedAction = sendMachineSetup.assign(() => ({
  paymentUnlockPending: false,
  unlockHistoryPromptVisible: false,
  isUnlockingHistory: true,
  unlockHistoryError: null,
}));

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

const markUnlockMessageStartedAction = sendMachineSetup.assign(
  ({ event }) => {
    if (event.type !== "ChatUnlockMessageRequested") return {};
    return {
      isUnlockingMessage: true,
      unlockingMessageId: event.messageId,
      unlockingMessageKind: event.kind,
      unlockingRemoteMessageId:
        event.remoteMessageId ?? (event.lockType ? null : event.messageId),
      unlockingLockType: event.lockType ?? null,
      unlockingClientLockId: event.clientLockId ?? null,
      unlockMessageError: null,
      unlockPaywallRequest: null,
    };
  },
);

const applyUnlockMessageSucceededAction = sendMachineSetup.assign(
  ({ context, event }) => {
    const { output } = event as unknown as DoneActorEvent<UnlockMessageOutput>;
    return {
      messages: applySingleUnlockOutput(context.messages, output),
      promotion: applyPromotionUnlockOutput(context.promotion, output),
      isUnlockingMessage: false,
      unlockingMessageId: null,
      unlockingMessageKind: null,
      unlockingRemoteMessageId: null,
      unlockingLockType: null,
      unlockingClientLockId: null,
      unlockMessageError: null,
      unlockPaywallRequest: null,
      creditBalance: output.response.creditBalance,
      creditsCharged: output.response.creditsCharged,
      requiredCredits: 0,
      shortfallCredits: 0,
    };
  },
);

const requestUnlockPaymentFromOutputAction = sendMachineSetup.assign(
  ({ context, event }) => {
    const { output } = event as unknown as DoneActorEvent<UnlockMessageOutput>;
    return {
      promotion: applyPromotionUnlockOutput(context.promotion, output),
      isUnlockingMessage: false,
      unlockingMessageId: null,
      unlockingMessageKind: null,
      unlockingRemoteMessageId: null,
      unlockingLockType: null,
      unlockingClientLockId: null,
      unlockMessageError: output.response.reason,
      unlockPaywallRequest: {
        displayMessageId:
          output.response.messageId || output.displayMessageId,
        ...(output.response.messageId || output.request.messageId
          ? {
              messageId:
                output.response.messageId || output.request.messageId,
            }
          : {}),
        kind: context.unlockingMessageKind ?? "private",
        ...(output.request.lockType
          ? { lockType: output.request.lockType }
          : {}),
        ...(output.request.clientLockId
          ? { clientLockId: output.request.clientLockId }
          : {}),
        ...(context.promotion?.message.id === output.displayMessageId
          ? { promotion: context.promotion.session }
          : {}),
        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,
    };
  },
);

const requestUnlockPaymentFromErrorAction = sendMachineSetup.assign(
  ({ context }) => ({
    isUnlockingMessage: false,
    unlockMessageError: "unlock_failed",
    unlockPaywallRequest:
      context.unlockingMessageId && context.unlockingMessageKind
        ? {
            displayMessageId: context.unlockingMessageId,
            ...(context.unlockingRemoteMessageId
              ? { messageId: context.unlockingRemoteMessageId }
              : {}),
            kind: context.unlockingMessageKind,
            ...(context.unlockingLockType
              ? { lockType: context.unlockingLockType }
              : {}),
            ...(context.unlockingClientLockId
              ? { clientLockId: context.unlockingClientLockId }
              : {}),
            ...(context.promotion?.message.id === context.unlockingMessageId
              ? { promotion: context.promotion.session }
              : {}),
            reason: "unlock_failed",
            creditBalance: context.creditBalance,
            requiredCredits: context.requiredCredits,
            shortfallCredits: context.shortfallCredits,
          }
        : null,
    unlockingMessageId: null,
    unlockingMessageKind: null,
    unlockingRemoteMessageId: null,
    unlockingLockType: null,
    unlockingClientLockId: null,
  }),
);

const clearUnlockPaywallRequestAction = sendMachineSetup.assign(() => ({
  unlockPaywallRequest: null,
}));

export const unlockMachineSetup = sendMachineSetup.extend({
  actions: {
    markPaymentUnlockPending: markPaymentUnlockPendingAction,
    showUnlockHistoryPrompt: showUnlockHistoryPromptAction,
    dismissUnlockHistoryPrompt: dismissUnlockHistoryPromptAction,
    markUnlockHistoryStarted: markUnlockHistoryStartedAction,
    markUnlockHistoryFailed: markUnlockHistoryFailedAction,
    markUnlockMessageStarted: markUnlockMessageStartedAction,
    applyUnlockMessageSucceeded: applyUnlockMessageSucceededAction,
    requestUnlockPaymentFromOutput: requestUnlockPaymentFromOutputAction,
    requestUnlockPaymentFromError: requestUnlockPaymentFromErrorAction,
    clearUnlockPaywallRequest: clearUnlockPaywallRequestAction,
  },
});

const unlockHistoryDoneActionSetup =
  createChatActorActionSetup<DoneActorEvent<UnlockHistoryOutput>>();

const applyUnlockHistoryOutputAction =
  unlockHistoryDoneActionSetup.assign(({ event }) => {
    const lockedHistoryCount = countLockedHistoryMessages(
      event.output.messages,
    );
    const insufficientBalance =
      !event.output.unlocked &&
      event.output.reason === "insufficient_balance";
    return {
      messages: event.output.messages,
      historyLoaded: true,
      paymentUnlockPending: false,
      unlockHistoryPromptVisible: insufficientBalance,
      lockedHistoryCount,
      isUnlockingHistory: false,
      unlockHistoryError: insufficientBalance
        ? `Insufficient credits. You still need ${event.output.shortfallCredits} credits.`
        : null,
    };
  });

export const unlockingHistoryState = unlockMachineSetup.createStateConfig({
  invoke: {
    id: "unlockHistory",
    src: "unlockHistory",
    onDone: {
      target: "ready",
      actions: applyUnlockHistoryOutputAction,
    },
    onError: {
      target: "ready",
      actions: "markUnlockHistoryFailed",
    },
  },
});

export const unlockingMessageState = unlockMachineSetup.createStateConfig({
  invoke: {
    id: "unlockMessage",
    src: "unlockMessage",
    input: ({ context }) => ({
      displayMessageId: context.unlockingMessageId ?? "",
      ...(context.unlockingRemoteMessageId
        ? { messageId: context.unlockingRemoteMessageId }
        : {}),
      ...(context.unlockingLockType
        ? { lockType: context.unlockingLockType }
        : {}),
      ...(context.unlockingClientLockId
        ? { clientLockId: context.unlockingClientLockId }
        : {}),
    }),
    onDone: [
      {
        guard: ({ event }) => event.output.response.unlocked,
        target: "ready",
        actions: "applyUnlockMessageSucceeded",
      },
      {
        target: "ready",
        actions: "requestUnlockPaymentFromOutput",
      },
    ],
    onError: {
      target: "ready",
      actions: "requestUnlockPaymentFromError",
    },
  },
});
