/**
 * Payment 状态机（XState v5）
 */
import { assign, setup } from "xstate";

import type { PaymentEvent } from "./payment-events";
import { initialState, type PaymentState } from "./payment-state";
import {
  createPaymentOrderActor,
  loadCachedPaymentPlansActor,
  pollPaymentOrderStatusActor,
  refreshPaymentPlansActor,
} from "./payment-machine.actors";
import {
  defaultAutoRenewForPlan,
  getDefaultPlanId,
  hasOrderPollingTimedOut,
  PAYMENT_TIMEOUT_ERROR_MESSAGE,
  resetOrderState,
  toPaymentErrorMessage,
} from "./payment-machine.helpers";

export type { PaymentEvent } from "./payment-events";
export type { PaymentState } from "./payment-state";
export { initialState } from "./payment-state";

const POLL_DELAY_MS = 4000;

export const paymentMachine = setup({
  types: {
    context: {} as PaymentState,
    events: {} as PaymentEvent,
  },
  actors: {
    loadCachedPlans: loadCachedPaymentPlansActor,
    refreshPlans: refreshPaymentPlansActor,
    createOrder: createPaymentOrderActor,
    pollOrderStatus: pollPaymentOrderStatusActor,
  },
}).createMachine({
  /** @xstate-layout N4IgpgJg5mDOIC5QAcCGBPAtmAdgFwGIAFDbfAGVQFccBjACwDFUBLAG0gG0AGAXURQB7WCzwtBOASAAeiACwAmADQh0iAIwA2bnIB0CuQE5j67t20AOBQF9rKtFlx5dLCB2KknASRyie-JBBkYVFxSUDZBAVDPUMFdQs5dXUAVk0Adk0AZhSUlTUEdUMLXXV0w00LCyzzc00U23tPfBc3MA9HfAAlMAAzACc4egA1FmQAZTxUPCpYfylgkTEJKUjo2PjE5LTMnLzVRBTq3R1iuRTdzWz0xqDm5zZBVAgWHCgiNlQcWAIICTAXDgAG6CADWAIcZAeTxebw+X1gCFeINo0zC-nmgUWoRWEQ0WQsKV0lXSSQuFk0MUM6XyiAs6l0RmMxXSWS0FIUNjsd060Oer3en2+BDA-X6gn6umQnzwvQlmCl910j35cKFiORglRyxwGL4CxCOtW+MJxIspNS6QpVJpB0KpMZKTZBiSWXiMQst0hTl0g2e6A6UJ8fn1WMNYWNhRShiyujOCiq5yOSXUtKihm4jOilTkbKO6Ru3O9LT9EADJF58Jw4zAHFoeC4oaESwjeMKhlMunS3BqCiycm4VviWTTpnSCn02iycSdigMmi9StL5fuFYAwvQvjhaxuvjAIJjmzjwqBIgkrPoe9wFNxUhk5HI09TNLoauOMtxzRYO4ZF7zfWA-qBk4ACCVB4IIPTbgA7rubyNgER5Gm256xpocjVDkCgXNoCiaGmVSZjkSRJOhqTRlyTT-suwH4CBUCDGAUJwfuh5BOGuKnhoVREnhuTxCkWg4WmVp6AOphaEUKScvUf5QgBQEVsxfoNgA8v0ECiuMVAAEaYKIDYHk27EtpxMiIJyRRxuY6iKPScQ1I+drJOOXbqFh6RHLeTpyT6NFKU4a4qWA6maf02l6QZXDqIhJnHpGCQJG5FLnBUeHlPhdqVCUD5WhmpJVDGnpFkugFlrReA9AMQyjBMUwzHMxnYshXGFEYmZ8YJGbnOkWjKHa-aZnEFRWPEZR9b5JZlSuvIAKJihKa4cKggxGbFzWtq1RTqBOBjFISBYKFamRphRjIxJkTrXmkySTc4tAqQKoWir8-yAiC4KKv+D2AWIbzPf0SLAlqaISHq60cSe5lRCRr47VkGQUgS2SjtGeiksyn6JvSDQld9j3-RpL2iuKkrStMcr9Aqxb3QTUAA0DKKg7qfBsRtZlrGSxJpNwHb0vehijm+XYjfUD7xBhxVUfJwRsGwT1E-0r3bu9YIQkqsvy4TYWMyDOrgwaplQ2sMRxpsKY7NkuRptksYpEYiQI9GKTXoW0s+prCthcrAKap9NNSoIcte6KuvauirMxYb8VtusZsJBbGRW-sBTuTojJyJU1xuuh0Z3YHwfay9fwq376v-p7ReA5q4dg6zCgQ0bkZx3ECfbEnexPjUjKsjGmimAOWT9vnlf04rIoLWTMqU9TGtB1rY86zXzMG2GTex6brdbHeuzW85Y3nVkrIWNwgnJHh+fQawf1QIwEoBfgFU9LAYB4GzkORhSmZWjtO3GAO9QLBPj7K+DM-dM4fmqFLHk8kr6hDeHffoD9CDSFgPVAEqBegNn6AACgHGYAAlAQAOcCb6IOQe-derUjB6G-KfeoToMhD3OARC81JpxH2pNEGMbsYE+gYGAWgoIBS1UmNMWYPtVb+yVAIoRIixhiIamHFerMmofzbF-Ls9J4jcIAUcESHYTjTnoaYMwpJeEB1kcIt4oj6oSJLr7YG0jvr0EEdYqAtjxEamBrXFmvBOBRzXjHVqmif46P-uYfRdpM4MgyCNbYFQcgWJka4uRNiFF2J+CTCUUpp7yi+vJKx8i6peOUfrVRjdgnQ1Cdov+MRIlAKysUE4hIYw6ASPbWy+cinpJKQ1CepNckU3yZY1J7jPFKOXuU-xgSkKbWqdoLRv9dENNOvbYkboT45mSJSEerAIBPzgK-ShVSzwEiJCSMkVpKRGFtAUAshg4y2VPkUXMHZPJ7NcIc6qsARgZK8Sclq0MOwvk6kcXIMRNg2zkOkXQ4KYzYU5N2Ps+deisA4Ac5BQVfohUVhFfSeBDKAvmWc00lzLTWluaOB8jyCz9gTAyjsaRUXosgBVeapMlqAVWsSjmJoLnmiuZS6ko46i6ApL1OlGZsLXhZewNlyDn7HLUVQ6G7kyWCopTckVzk+ripvL2aogDoi2G5DgQQml4BYnuNHIFkQAC0mUCj2qJMyN17qMyn3zq4DgtqSXcVdZsPMmdFAJBHHaC4sL3JskMNGdp05fx43kiqWEgoER+r5YUMotKEiVHtrsQko5PKlAyjGfsmF7z52XBm42GhqQMm7L2TkVhpyslHGUWMw5BLlBPrkVk3S6YAxrc3fusKeE5DiZ61Mzk0hEhzmkct15eaUT4S0UeQ6gl2vkP1VON49BulJGYG6R7L7XwFOQm1m7-UIE0HhOG84Bx7quE6xAMQXyjp0e+bQPZuljOKYo2Yw62z9xKPUW95hEnfkaQUfu762naLiKyDInyIBAa2jeF8v9rzfmMAJHddIeylAATZKSJI5UYrQ2qvsRIew7JqIlKoQs70dqSCfbC34CymusEAA */
  id: "payment",
  initial: "idle",
  context: initialState,
  states: {
    idle: {
      on: {
        PaymentInit: "loadingCachedPlans",
      },
    },

    loadingCachedPlans: {
      invoke: {
        src: "loadCachedPlans",
        onDone: [
          {
            guard: ({ event }) => (event.output?.plans.length ?? 0) > 0,
            target: "refreshingPlans",
            actions: assign(({ context, event }) => {
              const plans = event.output?.plans ?? [];
              const selectedPlanId =
                context.selectedPlanId || getDefaultPlanId(plans);
              return {
                plans,
                selectedPlanId,
                autoRenew: defaultAutoRenewForPlan(selectedPlanId, plans),
                errorMessage: null,
              };
            }),
          },
          {
            target: "loadingPlans",
          },
        ],
        onError: {
          target: "loadingPlans",
        },
      },
    },

    loadingPlans: {
      invoke: {
        src: "refreshPlans",
        onDone: {
          target: "ready",
          actions: assign(({ context, event }) => {
            const plans = event.output.plans;
            const selectedPlanId =
              context.selectedPlanId || getDefaultPlanId(plans);
            return {
              plans,
              selectedPlanId,
              autoRenew: defaultAutoRenewForPlan(selectedPlanId, plans),
              errorMessage: null,
            };
          }),
        },
        onError: {
          target: "ready",
          actions: assign(({ event }) => ({
            errorMessage: toPaymentErrorMessage(event.error),
          })),
        },
      },
    },

    refreshingPlans: {
      invoke: {
        src: "refreshPlans",
        onDone: {
          target: "ready",
          actions: assign(({ context, event }) => {
            const plans = event.output.plans;
            const selectedPlanId =
              plans.some((plan) => plan.planId === context.selectedPlanId)
                ? context.selectedPlanId
                : getDefaultPlanId(plans);
            return {
              plans,
              selectedPlanId,
              autoRenew: defaultAutoRenewForPlan(selectedPlanId, plans),
              errorMessage: null,
            };
          }),
        },
        onError: {
          target: "ready",
          actions: assign(({ event }) => ({
            errorMessage: toPaymentErrorMessage(event.error),
          })),
        },
      },
    },

    ready: {
      on: {
        PaymentInit: "refreshingPlans",
        PaymentPlanSelected: {
          actions: assign(({ context, event }) => ({
            ...resetOrderState(),
            selectedPlanId: event.planId,
            autoRenew: defaultAutoRenewForPlan(event.planId, context.plans),
          })),
        },
        PaymentPayChannelChanged: {
          actions: assign(({ event }) => ({
            ...resetOrderState(),
            payChannel: event.payChannel,
            errorMessage: null,
          })),
        },
        PaymentAutoRenewChanged: {
          actions: assign(({ event }) => ({
            autoRenew: event.autoRenew,
            errorMessage: null,
          })),
        },
        PaymentAgreementChanged: {
          actions: assign(({ event }) => ({
            agreed: event.agreed,
            errorMessage: null,
          })),
        },
        PaymentCreateOrderSubmitted: [
          {
            guard: ({ context }) =>
              context.agreed && context.selectedPlanId.length > 0,
            target: "creatingOrder",
          },
          {
            actions: assign({
              errorMessage:
                "Choose a plan and accept the agreement before continuing.",
            }),
          },
        ],
        PaymentErrorCleared: {
          actions: assign({ errorMessage: null }),
        },
      },
    },

    creatingOrder: {
      invoke: {
        src: "createOrder",
        input: ({ context }) => ({
          planId: context.selectedPlanId,
          payChannel: context.payChannel,
          autoRenew: context.autoRenew,
        }),
        onDone: {
          target: "pollingOrder",
          actions: assign(({ context, event }) => ({
            currentOrderId: event.output.orderId,
            payParams: event.output.payParams,
            orderStatus: "pending",
            orderPollingStartedAt: Date.now(),
            errorMessage: null,
            launchNonce: context.launchNonce + 1,
          })),
        },
        onError: {
          target: "failed",
          actions: assign(({ event }) => ({
            errorMessage: toPaymentErrorMessage(event.error),
          })),
        },
      },
    },

    pollingOrder: {
      invoke: {
        src: "pollOrderStatus",
        input: ({ context }) => ({
          orderId: context.currentOrderId ?? "",
        }),
        onDone: [
          {
            guard: ({ event }) => event.output.status === "paid",
            target: "paid",
            actions: assign(({ event }) => ({
              orderStatus: event.output.status,
              orderPollingStartedAt: null,
              errorMessage: null,
            })),
          },
          {
            guard: ({ event }) => event.output.status === "failed",
            target: "failed",
            actions: assign(({ event }) => ({
              orderStatus: event.output.status,
              orderPollingStartedAt: null,
              errorMessage: "Payment failed or was cancelled.",
            })),
          },
          {
            guard: ({ context }) =>
              hasOrderPollingTimedOut(context.orderPollingStartedAt),
            target: "failed",
            actions: assign({
              orderStatus: "failed",
              orderPollingStartedAt: null,
              errorMessage: PAYMENT_TIMEOUT_ERROR_MESSAGE,
            }),
          },
          {
            target: "waitingForPayment",
            actions: assign(({ event }) => ({
              orderStatus: event.output.status,
              errorMessage: null,
            })),
          },
        ],
        onError: {
          target: "failed",
          actions: assign(({ event }) => ({
            errorMessage: toPaymentErrorMessage(event.error),
          })),
        },
      },
      on: {
        PaymentReset: {
          target: "ready",
          actions: assign(resetOrderState),
        },
      },
    },

    waitingForPayment: {
      after: {
        [POLL_DELAY_MS]: "pollingOrder",
      },
      on: {
        PaymentReset: {
          target: "ready",
          actions: assign(resetOrderState),
        },
      },
    },

    paid: {
      on: {
        PaymentPlanSelected: {
          target: "ready",
          actions: assign(({ context, event }) => ({
            ...resetOrderState(),
            selectedPlanId: event.planId,
            autoRenew: defaultAutoRenewForPlan(event.planId, context.plans),
          })),
        },
        PaymentPayChannelChanged: {
          target: "ready",
          actions: assign(({ event }) => ({
            ...resetOrderState(),
            payChannel: event.payChannel,
            errorMessage: null,
          })),
        },
        PaymentCreateOrderSubmitted: [
          {
            guard: ({ context }) =>
              context.agreed && context.selectedPlanId.length > 0,
            target: "creatingOrder",
            actions: assign(resetOrderState),
          },
          {
            target: "ready",
            actions: assign({
              errorMessage:
                "Choose a plan and accept the agreement before continuing.",
            }),
          },
        ],
        PaymentReset: {
          target: "ready",
          actions: assign(resetOrderState),
        },
      },
    },

    failed: {
      on: {
        PaymentCreateOrderSubmitted: [
          {
            guard: ({ context }) =>
              context.agreed && context.selectedPlanId.length > 0,
            target: "creatingOrder",
            actions: assign(resetOrderState),
          },
        ],
        PaymentErrorCleared: {
          target: "ready",
          actions: assign({ errorMessage: null }),
        },
        PaymentReset: {
          target: "ready",
          actions: assign(resetOrderState),
        },
      },
    },
  },
  on: {
    PaymentReturned: {
      target: ".pollingOrder",
      actions: assign(({ event }) => ({
        currentOrderId: event.orderId,
        payParams: null,
        orderStatus: "pending",
        orderPollingStartedAt: event.createdAt ?? Date.now(),
        errorMessage: null,
      })),
    },
    PaymentLaunchFailed: {
      target: ".failed",
      actions: assign(({ event }) => ({
        errorMessage: event.errorMessage,
      })),
    },
  },
});

export type PaymentMachine = typeof paymentMachine;
