import type { DoneActorEvent, ErrorActorEvent } from "xstate";

import type { PrivateAlbumUnlockResponse } from "@/data/schemas/private-zone";

import {
  getPendingAlbum,
  patchAlbumInList,
  removeAlbum,
} from "../helper/albums";
import {
  toPaywallRequest,
  toPrivateZoneErrorMessage,
  toUnlockErrorMessage,
} from "../helper/unlock";
import {
  albumMachineSetup,
  failedState,
  idleState,
  loadingState,
} from "./album-flow";
import { createPrivateZoneActorActionSetup } from "./setup";

const requestUnlockAction = albumMachineSetup.assign(({ event }) =>
  event.type === "PrivateZoneUnlockRequested"
    ? {
        pendingConfirmAlbumId: event.albumId,
        unlockErrorMessage: null,
        unlockPaywallRequest: null,
      }
    : {},
);

const cancelUnlockAction = albumMachineSetup.assign({
  pendingConfirmAlbumId: null,
});

const clearUnlockPaywallAction = albumMachineSetup.assign({
  unlockPaywallRequest: null,
});

const beginUnlockAction = albumMachineSetup.assign(({ context }) => ({
  unlockingAlbumId: context.pendingConfirmAlbumId,
  unlockErrorMessage: null,
  unlockPaywallRequest: null,
}));

export const privateZoneMachineSetup = albumMachineSetup.extend({
  actions: {
    requestUnlock: requestUnlockAction,
    cancelUnlock: cancelUnlockAction,
    clearUnlockPaywall: clearUnlockPaywallAction,
    beginUnlock: beginUnlockAction,
  },
});

const unlockDoneSetup =
  createPrivateZoneActorActionSetup<
    DoneActorEvent<PrivateAlbumUnlockResponse>
  >();
const unlockErrorSetup =
  createPrivateZoneActorActionSetup<ErrorActorEvent>();

const applyUnlockSuccessAction = unlockDoneSetup.assign(
  ({ context, event }) => ({
    items: patchAlbumInList(context.items, event.output),
    creditBalance: event.output.creditBalance,
    pendingConfirmAlbumId: null,
    unlockingAlbumId: null,
    unlockErrorMessage: null,
    unlockSuccessNonce: context.unlockSuccessNonce + 1,
  }),
);

const applyUnlockPaywallAction = unlockDoneSetup.assign(
  ({ context, event }) => ({
    items: patchAlbumInList(context.items, event.output),
    creditBalance: event.output.creditBalance,
    pendingConfirmAlbumId: null,
    unlockingAlbumId: null,
    unlockErrorMessage: null,
    unlockPaywallRequest: toPaywallRequest(event.output),
  }),
);

const applyCostChangedAction = unlockDoneSetup.assign(({ event }) => ({
  pendingConfirmAlbumId: null,
  unlockingAlbumId: null,
  unlockErrorMessage: toUnlockErrorMessage(event.output),
}));

const applyAlbumNotFoundAction = unlockDoneSetup.assign(
  ({ context, event }) => ({
    items: removeAlbum(context.items, event.output.albumId),
    creditBalance: event.output.creditBalance,
    pendingConfirmAlbumId: null,
    unlockingAlbumId: null,
    unlockErrorMessage: toUnlockErrorMessage(event.output),
  }),
);

const applyUnlockFailureAction = unlockDoneSetup.assign(
  ({ context, event }) => ({
    items: patchAlbumInList(context.items, event.output),
    creditBalance: event.output.creditBalance,
    pendingConfirmAlbumId: null,
    unlockingAlbumId: null,
    unlockErrorMessage: toUnlockErrorMessage(event.output),
  }),
);

const applyUnlockActorErrorAction = unlockErrorSetup.assign(({ event }) => ({
  pendingConfirmAlbumId: null,
  unlockingAlbumId: null,
  unlockErrorMessage: toPrivateZoneErrorMessage(event.error),
}));

const readyState = privateZoneMachineSetup.createStateConfig({
  on: {
    PrivateZoneInit: "loading",
    PrivateZoneRefresh: "loading",
    PrivateZoneUnlockRequested: { actions: "requestUnlock" },
    PrivateZoneUnlockCancelled: { actions: "cancelUnlock" },
    PrivateZoneUnlockConfirmed: [
      {
        guard: "hasPendingAlbum",
        target: "unlocking",
      },
    ],
    PrivateZoneUnlockPaywallConsumed: { actions: "clearUnlockPaywall" },
  },
});

const unlockingState = privateZoneMachineSetup.createStateConfig({
  entry: "beginUnlock",
  invoke: {
    src: "unlockAlbum",
    input: ({ context }) => {
      const pendingAlbum = getPendingAlbum(context);
      return {
        albumId: pendingAlbum?.albumId ?? "",
        expectedCost: pendingAlbum?.unlockCost ?? 0,
      };
    },
    onDone: [
      {
        guard: ({ event }) => event.output.unlocked && !event.output.locked,
        target: "ready",
        actions: applyUnlockSuccessAction,
      },
      {
        guard: ({ event }) =>
          event.output.reason === "insufficient_credits",
        target: "ready",
        actions: applyUnlockPaywallAction,
      },
      {
        guard: ({ event }) => event.output.reason === "cost_changed",
        target: "loading",
        actions: applyCostChangedAction,
      },
      {
        guard: ({ event }) => event.output.reason === "not_found",
        target: "ready",
        actions: applyAlbumNotFoundAction,
      },
      {
        target: "ready",
        actions: applyUnlockFailureAction,
      },
    ],
    onError: {
      target: "ready",
      actions: applyUnlockActorErrorAction,
    },
  },
});

export const privateZoneRootStateConfig =
  privateZoneMachineSetup.createStateConfig({
    initial: "idle",
    states: {
      idle: idleState,
      loading: loadingState,
      failed: failedState,
      ready: readyState,
      unlocking: unlockingState,
    },
  });
