import { ExceptionHandler } from "@/core/errors";
import { PaymentPlan, type PaymentPlansResponse } from "@/data/dto/payment";

import type { PaymentState } from "./payment-state";

export const MAX_ORDER_POLLING_MS = 5 * 60 * 1000;
export const PAYMENT_TIMEOUT_ERROR_MESSAGE =
  "Payment timed out. Please try again.";

export function toPaymentErrorMessage(error: unknown): string {
  return ExceptionHandler.message(error);
}

export function hasOrderPollingTimedOut(
  startedAt: number | null,
  now = Date.now(),
  maxDurationMs = MAX_ORDER_POLLING_MS,
): boolean {
  return startedAt !== null && now - startedAt >= maxDurationMs;
}

export function defaultAutoRenewForPlan(
  planId: string,
  plans: readonly PaymentPlan[] = [],
): boolean {
  const plan = plans.find((item) => item.planId === planId);
  if (plan?.dolAmount !== null && plan?.dolAmount !== undefined) return false;
  return (
    !planId.includes("lifetime") &&
    !planId.startsWith("dol_") &&
    !planId.startsWith("points_") &&
    !planId.startsWith("credit_")
  );
}

export function getDefaultPlanId(plans: readonly PaymentPlan[]): string {
  return (
    plans.find((plan) => plan.planId === "vip_monthly")?.planId ??
    plans.find((plan) => plan.orderType.startsWith("vip_"))?.planId ??
    plans[0]?.planId ??
    ""
  );
}

export function hydratePlansState(
  plans: PaymentPlan[],
  selectedPlanId: string,
): Pick<
  PaymentState,
  "plans" | "selectedPlanId" | "autoRenew" | "errorMessage"
> {
  const nextSelectedPlanId = selectedPlanId || getDefaultPlanId(plans);
  return {
    plans,
    selectedPlanId: nextSelectedPlanId,
    autoRenew: defaultAutoRenewForPlan(nextSelectedPlanId, plans),
    errorMessage: null,
  };
}

export function hydratePlansResponseState(
  response: PaymentPlansResponse,
  selectedPlanId: string,
): Pick<
  PaymentState,
  | "plans"
  | "isFirstRecharge"
  | "selectedPlanId"
  | "autoRenew"
  | "errorMessage"
> {
  const plans = normalizeFirstRechargePlans(
    response.plans,
    response.isFirstRecharge,
  );
  return {
    ...hydratePlansState(plans, selectedPlanId),
    isFirstRecharge: response.isFirstRecharge,
  };
}

export function refreshPlansState(
  plans: PaymentPlan[],
  selectedPlanId: string,
): Pick<
  PaymentState,
  "plans" | "selectedPlanId" | "autoRenew" | "errorMessage"
> {
  const nextSelectedPlanId = plans.some((plan) => plan.planId === selectedPlanId)
    ? selectedPlanId
    : getDefaultPlanId(plans);
  return {
    plans,
    selectedPlanId: nextSelectedPlanId,
    autoRenew: defaultAutoRenewForPlan(nextSelectedPlanId, plans),
    errorMessage: null,
  };
}

export function refreshPlansResponseState(
  response: PaymentPlansResponse,
  selectedPlanId: string,
): Pick<
  PaymentState,
  | "plans"
  | "isFirstRecharge"
  | "selectedPlanId"
  | "autoRenew"
  | "errorMessage"
> {
  const plans = normalizeFirstRechargePlans(
    response.plans,
    response.isFirstRecharge,
  );
  return {
    ...refreshPlansState(plans, selectedPlanId),
    isFirstRecharge: response.isFirstRecharge,
  };
}

export function normalizeFirstRechargePlans(
  plans: readonly PaymentPlan[],
  isFirstRecharge: boolean,
): PaymentPlan[] {
  if (isFirstRecharge) return [...plans];
  return plans.map(consumeFirstRechargePlan);
}

export function consumeFirstRechargePlan(plan: PaymentPlan): PaymentPlan {
  if (!plan.isFirstRechargeOffer) {
    return PaymentPlan.from({
      ...plan.toJson(),
      isFirstRechargeOffer: false,
      firstRechargeDiscountPercent: null,
      promotionType: null,
    });
  }

  const originalAmountCents = plan.originalAmountCents;
  return PaymentPlan.from({
    ...plan.toJson(),
    amountCents: originalAmountCents ?? plan.amountCents,
    originalAmountCents: originalAmountCents ?? plan.originalAmountCents,
    isFirstRechargeOffer: false,
    firstRechargeDiscountPercent: null,
    promotionType: null,
  });
}

export function consumeFirstRechargeState(
  context: PaymentState,
): Pick<
  PaymentState,
  | "plans"
  | "isFirstRecharge"
  | "selectedPlanId"
  | "autoRenew"
  | "errorMessage"
> {
  const plans = context.plans.map(consumeFirstRechargePlan);
  return {
    ...refreshPlansState(plans, context.selectedPlanId),
    isFirstRecharge: false,
  };
}

export function selectPlanState(
  planId: string,
  plans: readonly PaymentPlan[],
): Partial<PaymentState> {
  return {
    ...resetOrderState(),
    selectedPlanId: planId,
    autoRenew: defaultAutoRenewForPlan(planId, plans),
  };
}

export function resetOrderState(): Partial<PaymentState> {
  return {
    currentOrderId: null,
    payParams: null,
    orderStatus: null,
    orderPollingStartedAt: null,
    errorMessage: null,
  };
}
