"use client";

import { type Dispatch, useEffect, useRef, useState } from "react";

import {
  getPaymentUrl,
  getStripeClientSecret,
  isEzpayPayment,
  launchEzpayRedirect,
} from "@/lib/payment/payment_launch";
import { behaviorAnalytics } from "@/lib/analytics";
import type {
  PendingPaymentReturnTo,
  PendingPaymentSubscriptionType,
  PendingPaymentTipCoffeeType,
} from "@/lib/payment/pending_payment_order";
import type {
  PaymentContextState,
} from "@/stores/payment/payment-context";
import type { PaymentEvent } from "@/stores/payment/payment-events";
import { AppEnvUtil } from "@/utils/app-env";
import { Logger } from "@/utils/logger";

const UNSUPPORTED_PAYMENT_PARAMS_MESSAGE =
  "Payment parameters did not include a supported URL or Stripe client secret.";

export interface ShouldShowEzpayConfirmationInput {
  currentOrderId: string | null;
  isProduction: boolean;
  payParams: Record<string, unknown> | null;
  paymentUrl: string | null;
}

export interface UsePaymentLaunchFlowInput {
  log: Logger;
  logScope: string;
  payment: PaymentContextState;
  paymentDispatch: Dispatch<PaymentEvent>;
  returnTo?: PendingPaymentReturnTo;
  subscriptionType: PendingPaymentSubscriptionType;
  tipCoffeeType?: PendingPaymentTipCoffeeType;
}

export interface PaymentLaunchFlow {
  ezpayPaymentUrl: string | null;
  handleEzpayCancel: () => void;
  handleEzpayConfirm: () => void;
  handleStripeClose: () => void;
  handleStripeConfirmed: () => void;
  isConfirmingEzpay: boolean;
  resetLaunchState: () => void;
  shouldShowEzpayConfirmDialog: boolean;
  shouldShowStripeDialog: boolean;
  stripeClientSecret: string | null;
}

function trackPaymentCheckoutOpened(
  payment: PaymentContextState,
  checkoutUrl: string,
): void {
  const plan = payment.plans.find(
    (item) => item.planId === payment.selectedPlanId,
  );
  if (!payment.currentOrderId || !plan) return;
  behaviorAnalytics.checkoutOpened({
    orderId: payment.currentOrderId,
    plan,
    payChannel: payment.payChannel,
    checkoutUrl,
  });
}

function trackPaymentCheckoutFailed(
  payment: PaymentContextState,
  reason: "missing_checkout_url" | "payment_redirect_failed",
): void {
  const plan = payment.plans.find(
    (item) => item.planId === payment.selectedPlanId,
  );
  if (!payment.currentOrderId || !plan) return;
  behaviorAnalytics.checkoutFailed({
    orderId: payment.currentOrderId,
    plan,
    payChannel: payment.payChannel,
    reason,
  });
}

export function shouldShowEzpayConfirmation({
  currentOrderId,
  isProduction,
  payParams,
  paymentUrl,
}: ShouldShowEzpayConfirmationInput): boolean {
  return Boolean(
    !isProduction &&
      payParams &&
      isEzpayPayment(payParams) &&
      paymentUrl &&
      currentOrderId,
  );
}

export function usePaymentLaunchFlow({
  log,
  logScope,
  payment,
  paymentDispatch,
  returnTo,
  subscriptionType,
  tipCoffeeType,
}: UsePaymentLaunchFlowInput): PaymentLaunchFlow {
  const launchedNonceRef = useRef(0);
  const [hiddenStripeClientSecret, setHiddenStripeClientSecret] = useState<
    string | null
  >(null);
  const [isConfirmingEzpay, setIsConfirmingEzpay] = useState(false);
  const stripeClientSecret = payment.payParams
    ? getStripeClientSecret(payment.payParams)
    : null;
  const ezpayPaymentUrl = payment.payParams
    ? getPaymentUrl(payment.payParams)
    : null;
  useEffect(() => {
    if (!payment.payParams || payment.launchNonce <= launchedNonceRef.current) {
      return;
    }

    launchedNonceRef.current = payment.launchNonce;
    const clientSecret = getStripeClientSecret(payment.payParams);
    if (clientSecret) {
      trackPaymentCheckoutOpened(payment, "stripe_embedded");
      return;
    }

    const paymentUrl = getPaymentUrl(payment.payParams);
    if (paymentUrl) {
      const isEzpay = isEzpayPayment(payment.payParams);

      if (!AppEnvUtil.isProduction() && isEzpay) {
        log.debug(`[${logScope}] ezpay confirmation required`, {
          orderId: payment.currentOrderId,
          paymentUrl,
          subscriptionType,
        });
        return;
      }

      if (isEzpay) {
        void launchEzpayRedirect({
          orderId: payment.currentOrderId,
          paymentUrl,
          subscriptionType,
          ...(tipCoffeeType ? { tipCoffeeType } : {}),
          ...(returnTo ? { returnTo } : {}),
          onOpened: () => trackPaymentCheckoutOpened(payment, paymentUrl),
          onFailed: (errorMessage) => {
            trackPaymentCheckoutFailed(payment, "payment_redirect_failed");
            paymentDispatch({ type: "PaymentLaunchFailed", errorMessage });
          },
        });
        return;
      }

      try {
        window.location.href = paymentUrl;
        trackPaymentCheckoutOpened(payment, paymentUrl);
      } catch {
        trackPaymentCheckoutFailed(payment, "payment_redirect_failed");
        paymentDispatch({
          type: "PaymentLaunchFailed",
          errorMessage: "Could not open the payment page. Please try again.",
        });
      }
      return;
    }

    trackPaymentCheckoutFailed(payment, "missing_checkout_url");
    paymentDispatch({
      type: "PaymentLaunchFailed",
      errorMessage: UNSUPPORTED_PAYMENT_PARAMS_MESSAGE,
    });
  }, [
    log,
    logScope,
    payment.currentOrderId,
    payment,
    payment.launchNonce,
    payment.payChannel,
    payment.payParams,
    payment.plans,
    payment.selectedPlanId,
    paymentDispatch,
    returnTo,
    subscriptionType,
    tipCoffeeType,
  ]);

  const shouldShowStripeDialog =
    stripeClientSecret !== null &&
    stripeClientSecret !== hiddenStripeClientSecret &&
    !payment.isPaid;
  const shouldShowEzpayConfirmDialog = shouldShowEzpayConfirmation({
    currentOrderId: payment.currentOrderId,
    isProduction: AppEnvUtil.isProduction(),
    payParams: payment.payParams,
    paymentUrl: ezpayPaymentUrl,
  });

  function resetLaunchState(): void {
    setIsConfirmingEzpay(false);
    setHiddenStripeClientSecret(null);
  }

  function handleStripeClose(): void {
    if (stripeClientSecret) {
      setHiddenStripeClientSecret(stripeClientSecret);
    }
    if (payment.orderStatus !== "paid") {
      paymentDispatch({ type: "PaymentReset" });
    }
  }

  function handleStripeConfirmed(): void {
    if (stripeClientSecret) {
      setHiddenStripeClientSecret(stripeClientSecret);
    }
  }

  function handleEzpayConfirm(): void {
    if (!payment.currentOrderId || !ezpayPaymentUrl) return;
    setIsConfirmingEzpay(true);
    void launchEzpayRedirect({
      orderId: payment.currentOrderId,
      paymentUrl: ezpayPaymentUrl,
      subscriptionType,
      ...(tipCoffeeType ? { tipCoffeeType } : {}),
      ...(returnTo ? { returnTo } : {}),
      onOpened: () => trackPaymentCheckoutOpened(payment, ezpayPaymentUrl),
      onFailed: (errorMessage) => {
        trackPaymentCheckoutFailed(payment, "payment_redirect_failed");
        setIsConfirmingEzpay(false);
        paymentDispatch({ type: "PaymentLaunchFailed", errorMessage });
      },
    });
  }

  function handleEzpayCancel(): void {
    log.debug(`[${logScope}] ezpay confirmation cancelled`, {
      orderId: payment.currentOrderId,
      subscriptionType,
    });
    setIsConfirmingEzpay(false);
    paymentDispatch({ type: "PaymentReset" });
  }

  return {
    ezpayPaymentUrl,
    handleEzpayCancel,
    handleEzpayConfirm,
    handleStripeClose,
    handleStripeConfirmed,
    isConfirmingEzpay,
    resetLaunchState,
    shouldShowEzpayConfirmDialog,
    shouldShowStripeDialog,
    stripeClientSecret,
  };
}
