"use client";

/** Route UI reads this projection instead of the Payment machine snapshot. */
import type { Dispatch, ReactNode } from "react";
import { createActorContext, shallowEqual } from "@xstate/react";
import type { SnapshotFrom } from "xstate";

import { paymentMachine } from "./payment-machine";
import type {
  PaymentEvent,
  PaymentState as MachineContext,
} from "./payment-machine";

export interface PaymentContextState {
  status: string;
  planCatalog: MachineContext["planCatalog"];
  plans: MachineContext["plans"];
  giftCategories: MachineContext["giftCategories"];
  giftProducts: MachineContext["giftProducts"];
  selectedGiftCategory: string | null;
  isFirstRecharge: MachineContext["isFirstRecharge"];
  commercialOffer: MachineContext["commercialOffer"];
  commercialOfferId: string | null;
  chatActionId: string | null;
  selectedPlanId: string;
  payChannel: MachineContext["payChannel"];
  autoRenew: boolean;
  agreed: boolean;
  currentOrderId: string | null;
  payParams: Record<string, unknown> | null;
  orderStatus: MachineContext["orderStatus"];
  tipMessage: MachineContext["tipMessage"];
  tipMessageError: string | null;
  errorMessage: string | null;
  launchNonce: number;
  isLoadingPlans: boolean;
  isLoadingTipMessage: boolean;
  isCreatingOrder: boolean;
  isPollingOrder: boolean;
  isPaid: boolean;
}

type PaymentSnapshot = SnapshotFrom<typeof paymentMachine>;
type PaymentSelector<T> = (snapshot: PaymentSnapshot) => T;

const PaymentActorContext = createActorContext(paymentMachine);

export interface PaymentProviderProps {
  children: ReactNode;
}

export function PaymentProvider({ children }: PaymentProviderProps) {
  return <PaymentActorContext.Provider>{children}</PaymentActorContext.Provider>;
}

export function usePaymentState(): PaymentContextState {
  return usePaymentSelector(selectPaymentState, shallowEqual);
}

export function usePaymentDispatch(): Dispatch<PaymentEvent> {
  return PaymentActorContext.useActorRef().send;
}

export function usePaymentSelector<T>(
  selector: PaymentSelector<T>,
  compare?: (previous: T, next: T) => boolean,
): T {
  return PaymentActorContext.useSelector(selector, compare);
}

function selectPaymentState(state: PaymentSnapshot): PaymentContextState {
  return {
    status: String(state.value),
    planCatalog: state.context.planCatalog,
    plans: state.context.plans,
    giftCategories: state.context.giftCategories,
    giftProducts: state.context.giftProducts,
    selectedGiftCategory: state.context.selectedGiftCategory,
    isFirstRecharge: state.context.isFirstRecharge,
    commercialOffer: state.context.commercialOffer,
    commercialOfferId: state.context.commercialOfferId,
    chatActionId: state.context.chatActionId,
    selectedPlanId: state.context.selectedPlanId,
    payChannel: state.context.payChannel,
    autoRenew: state.context.autoRenew,
    agreed: state.context.agreed,
    currentOrderId: state.context.currentOrderId,
    payParams: state.context.payParams,
    orderStatus: state.context.orderStatus,
    tipMessage: state.context.tipMessage,
    tipMessageError: state.context.tipMessageError,
    errorMessage: state.context.errorMessage,
    launchNonce: state.context.launchNonce,
    isLoadingPlans:
      state.matches("loadingCachedPlans") ||
      state.matches("loadingPlans") ||
      state.matches("loadingGiftProducts") ||
      state.matches("refreshingPlans"),
    isLoadingTipMessage: state.matches("loadingTipMessage"),
    isCreatingOrder: state.matches("creatingOrder"),
    isPollingOrder:
      state.matches("pollingOrder") || state.matches("waitingForPayment"),
    isPaid: state.context.orderStatus === "paid",
  };
}
