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

import type { PaymentPlansResponse } from "@/data/dto/payment";

import {
  consumeFirstRechargeState,
  hydratePlansResponseState,
  refreshPlansResponseState,
  selectPlanState,
} from "../helper/catalog";
import {
  resetOrderState,
  toPaymentErrorMessage,
} from "../helper/order";
import {
  basePaymentMachineSetup,
  createPaymentActorActionSetup,
} from "./setup";

const initializeCatalogAction = basePaymentMachineSetup.assign(
  ({ context, event }) => {
    if (event.type !== "PaymentInit") return {};
    return {
      planCatalog: event.catalog ?? context.planCatalog,
      ...(event.payChannel ? { payChannel: event.payChannel } : {}),
    };
  },
);

const refreshCatalogAction = basePaymentMachineSetup.assign(
  ({ context, event }) => {
    if (event.type !== "PaymentInit") return {};
    const planCatalog = event.catalog ?? context.planCatalog;
    const catalogChanged = planCatalog !== context.planCatalog;
    return {
      planCatalog,
      ...(event.payChannel ? { payChannel: event.payChannel } : {}),
      ...(catalogChanged
        ? {
            plans: [],
            selectedPlanId: "",
            isFirstRecharge: false,
            autoRenew: planCatalog !== "tip",
          }
        : {}),
    };
  },
);

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,
  },
});

const cachedPlansDoneSetup =
  createPaymentActorActionSetup<
    DoneActorEvent<PaymentPlansResponse | null>
  >();
const plansDoneSetup =
  createPaymentActorActionSetup<DoneActorEvent<PaymentPlansResponse>>();
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 applyPlansErrorAction = plansErrorSetup.assign(({ event }) => ({
  errorMessage: toPaymentErrorMessage(event.error),
}));

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

export const loadingCachedPlansState =
  catalogMachineSetup.createStateConfig({
    invoke: {
      src: "loadCachedPlans",
      input: ({ context }) => ({ catalog: context.planCatalog }),
      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 }),
    onDone: {
      target: "ready",
      actions: hydratePlansAction,
    },
    onError: {
      target: "ready",
      actions: applyPlansErrorAction,
    },
  },
});

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

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