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

import type {
  GiftProductsResponse,
  PaymentPlansResponse,
} from "@/data/schemas/payment";

import {
  consumeFirstRechargeState,
  hydrateGiftProductsState,
  hydratePlansResponseState,
  refreshPlansResponseState,
  selectPlanState,
} from "../helper";
import { resetOrderState, toPaymentErrorMessage } from "../helper/order";
import type { PaymentEvent } from "../payment-events";
import type { PaymentState } from "../payment-state";
import {
  basePaymentMachineSetup,
  createPaymentActorActionSetup,
} from "./setup";

function initializeCatalogState(
  context: PaymentState,
  event: Extract<PaymentEvent, { type: "PaymentInit" }>,
): Partial<PaymentState> {
  const planCatalog = event.catalog ?? context.planCatalog;
  const giftCharacterId =
    planCatalog === "tip"
      ? (event.characterId ?? context.giftCharacterId)
      : null;
  const supportCharacterId =
    planCatalog === "default"
      ? (event.characterId ?? context.supportCharacterId)
      : null;
  const catalogChanged = planCatalog !== context.planCatalog;
  const characterChanged =
    giftCharacterId !== context.giftCharacterId ||
    supportCharacterId !== context.supportCharacterId;
  const selectionChanged =
    planCatalog === "tip"
      ? (event.category ?? null) !== context.selectedGiftCategory ||
        (event.planId ?? null) !== (context.selectedPlanId || null)
      : (event.planId ?? null) !== (context.selectedPlanId || null);
  const commercialOfferId =
    planCatalog === "default" ? (event.commercialOfferId ?? null) : null;
  const offerChanged = commercialOfferId !== context.commercialOfferId;
  const chatActionId = event.chatActionId ?? null;
  const chatActionChanged = chatActionId !== context.chatActionId;
  const initialAutoRenew =
    planCatalog === "tip" ? false : (event.autoRenew ?? true);
  const autoRenewChanged = initialAutoRenew !== context.autoRenew;

  return {
    planCatalog,
    ...(event.payChannel ? { payChannel: event.payChannel } : {}),
    giftCharacterId,
    supportCharacterId,
    commercialOfferId,
    chatActionId,
    requestedGiftCategory:
      planCatalog === "tip" ? (event.category ?? null) : null,
    requestedGiftPlanId:
      planCatalog === "tip" ? (event.planId ?? null) : null,
    ...(catalogChanged ||
    characterChanged ||
    selectionChanged ||
    offerChanged ||
    chatActionChanged ||
    autoRenewChanged
      ? {
          ...resetOrderState(),
          plans: [],
          giftCategories: [],
          giftProducts: [],
          selectedGiftCategory: null,
          selectedPlanId:
            planCatalog === "default" ? (event.planId ?? "") : "",
          isFirstRecharge: false,
          commercialOffer: null,
          autoRenew: initialAutoRenew,
        }
      : {}),
  };
}

const initializeCatalogAction = basePaymentMachineSetup.assign(
  ({ context, event }) =>
    event.type === "PaymentInit"
      ? initializeCatalogState(context, event)
      : {},
);

const refreshCatalogAction = basePaymentMachineSetup.assign(
  ({ context, event }) =>
    event.type === "PaymentInit"
      ? initializeCatalogState(context, event)
      : {},
);

const selectPlanAction = basePaymentMachineSetup.assign(
  ({ context, event }) =>
    event.type === "PaymentPlanSelected"
      ? selectPlanState(event.planId, context.plans)
      : {},
);

const changePayChannelAction = basePaymentMachineSetup.assign(({ event }) =>
  event.type === "PaymentPayChannelChanged"
    ? {
        ...resetOrderState(),
        payChannel: event.payChannel,
        errorMessage: null,
      }
    : {},
);

const changeAutoRenewAction = basePaymentMachineSetup.assign(({ event }) =>
  event.type === "PaymentAutoRenewChanged"
    ? { autoRenew: event.autoRenew, errorMessage: null }
    : {},
);

const changeAgreementAction = basePaymentMachineSetup.assign(({ event }) =>
  event.type === "PaymentAgreementChanged"
    ? { agreed: event.agreed, errorMessage: null }
    : {},
);

const showCreateOrderValidationErrorAction = basePaymentMachineSetup.assign({
  errorMessage: "Choose a plan and accept the agreement before continuing.",
});

const clearPaymentErrorAction = basePaymentMachineSetup.assign({
  errorMessage: null,
});

const resetOrderAction = basePaymentMachineSetup.assign(() =>
  resetOrderState(),
);

const consumeFirstRechargeAction = basePaymentMachineSetup.assign(
  ({ context }) => consumeFirstRechargeState(context),
);

export const catalogMachineSetup = basePaymentMachineSetup.extend({
  actions: {
    initializeCatalog: initializeCatalogAction,
    refreshCatalog: refreshCatalogAction,
    selectPlan: selectPlanAction,
    changePayChannel: changePayChannelAction,
    changeAutoRenew: changeAutoRenewAction,
    changeAgreement: changeAgreementAction,
    showCreateOrderValidationError: showCreateOrderValidationErrorAction,
    clearPaymentError: clearPaymentErrorAction,
    resetOrder: resetOrderAction,
    consumeFirstRecharge: consumeFirstRechargeAction,
  },
  guards: {
    isTipInit: ({ context, event }) =>
      event.type === "PaymentInit" &&
      (event.catalog ?? context.planCatalog) === "tip",
    isTipCatalog: ({ context }) => context.planCatalog === "tip",
  },
});

const cachedPlansDoneSetup =
  createPaymentActorActionSetup<
    DoneActorEvent<PaymentPlansResponse | null>
  >();
const plansDoneSetup =
  createPaymentActorActionSetup<DoneActorEvent<PaymentPlansResponse>>();
const giftProductsDoneSetup =
  createPaymentActorActionSetup<DoneActorEvent<GiftProductsResponse>>();
const plansErrorSetup = createPaymentActorActionSetup<ErrorActorEvent>();

const hydrateCachedPlansAction = cachedPlansDoneSetup.assign(
  ({ context, event }) =>
    event.output
      ? hydratePlansResponseState(event.output, context.selectedPlanId)
      : {},
);

const hydratePlansAction = plansDoneSetup.assign(({ context, event }) =>
  hydratePlansResponseState(event.output, context.selectedPlanId),
);

const refreshPlansAction = plansDoneSetup.assign(({ context, event }) =>
  refreshPlansResponseState(event.output, context.selectedPlanId),
);

const hydrateGiftProductsAction = giftProductsDoneSetup.assign(
  ({ context, event }) =>
    hydrateGiftProductsState(
      event.output,
      context.giftCharacterId ?? "",
      context.requestedGiftCategory ?? context.selectedGiftCategory,
      context.requestedGiftPlanId ?? context.selectedPlanId,
    ),
);

const applyPlansErrorAction = plansErrorSetup.assign(({ event }) => ({
  errorMessage: toPaymentErrorMessage(event.error),
}));

const applyGiftProductsErrorAction = plansErrorSetup.assign(({ event }) => ({
  plans: [],
  giftCategories: [],
  giftProducts: [],
  selectedGiftCategory: null,
  selectedPlanId: "",
  errorMessage: toPaymentErrorMessage(event.error),
}));

export const idleState = catalogMachineSetup.createStateConfig({
  on: {
    PaymentInit: [
      {
        guard: "isTipInit",
        target: "loadingGiftProducts",
        actions: "initializeCatalog",
      },
      {
        target: "loadingCachedPlans",
        actions: "initializeCatalog",
      },
    ],
  },
});

export const loadingCachedPlansState =
  catalogMachineSetup.createStateConfig({
    invoke: {
      src: "loadCachedPlans",
      input: ({ context }) => ({
        catalog: context.planCatalog,
        commercialOfferId: context.commercialOfferId,
        supportCharacterId: context.supportCharacterId,
      }),
      onDone: [
        {
          guard: ({ event }) => (event.output?.plans.length ?? 0) > 0,
          target: "refreshingPlans",
          actions: hydrateCachedPlansAction,
        },
        { target: "loadingPlans" },
      ],
      onError: { target: "loadingPlans" },
    },
  });

export const loadingPlansState = catalogMachineSetup.createStateConfig({
  invoke: {
    src: "refreshPlans",
      input: ({ context }) => ({
        catalog: context.planCatalog,
        commercialOfferId: context.commercialOfferId,
        supportCharacterId: context.supportCharacterId,
    }),
    onDone: {
      target: "ready",
      actions: hydratePlansAction,
    },
    onError: {
      target: "ready",
      actions: applyPlansErrorAction,
    },
  },
});

export const refreshingPlansState = catalogMachineSetup.createStateConfig({
  invoke: {
    src: "refreshPlans",
    input: ({ context }) => ({
      catalog: context.planCatalog,
      commercialOfferId: context.commercialOfferId,
      supportCharacterId: context.supportCharacterId,
    }),
    onDone: {
      target: "ready",
      actions: refreshPlansAction,
    },
    onError: {
      target: "ready",
      actions: applyPlansErrorAction,
    },
  },
});

export const loadingGiftProductsState =
  catalogMachineSetup.createStateConfig({
    invoke: {
      src: "loadGiftProducts",
      input: ({ context }) => ({
        characterId: context.giftCharacterId ?? "",
      }),
      onDone: {
        target: "ready",
        actions: hydrateGiftProductsAction,
      },
      onError: {
        target: "ready",
        actions: applyGiftProductsErrorAction,
      },
    },
  });

export const readyState = catalogMachineSetup.createStateConfig({
  on: {
    PaymentInit: [
      {
        guard: "isTipInit",
        target: "loadingGiftProducts",
        actions: "refreshCatalog",
      },
      {
        target: "refreshingPlans",
        actions: "refreshCatalog",
      },
    ],
    PaymentCatalogRetryRequested: [
      {
        guard: "isTipCatalog",
        target: "loadingGiftProducts",
      },
      { target: "refreshingPlans" },
    ],
    PaymentPlanSelected: { actions: "selectPlan" },
    PaymentPayChannelChanged: { actions: "changePayChannel" },
    PaymentAutoRenewChanged: { actions: "changeAutoRenew" },
    PaymentAgreementChanged: { actions: "changeAgreement" },
    PaymentCreateOrderSubmitted: [
      {
        guard: "canCreateOrder",
        target: "creatingOrder",
      },
      { actions: "showCreateOrderValidationError" },
    ],
    PaymentErrorCleared: { actions: "clearPaymentError" },
  },
});
