/* eslint-disable @typescript-eslint/indent */
/* eslint-disable no-nested-ternary */
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
import Toast from '@arcblock/ux/lib/Toast';
// eslint-disable-next-line import/no-extraneous-dependencies
import Header from '@blocklet/ui-react/lib/Header';
import type {
  TCheckoutSessionExpanded,
  TCustomer,
  TPaymentCurrency,
  TPaymentMethod,
  TPaymentIntent,
  TPaymentMethodExpanded,
} from '@blocklet/payment-types';
import { ArrowBackOutlined } from '@mui/icons-material';
import { Box, Fade, Stack, type BoxProps } from '@mui/material';
import { styled } from '@mui/system';
import { fromTokenToUnit } from '@ocap/util';
import { useSetState } from 'ahooks';
import { useEffect, useMemo, useRef, useState } from 'react';
import { FormProvider, useForm, useWatch } from 'react-hook-form';
import trim from 'lodash/trim';
import type { LiteralUnion } from 'type-fest';

import { usePaymentContext } from '../contexts/payment';
import api from '../libs/api';
import {
  findCurrency,
  formatError,
  getQueryParams,
  getStatementDescriptor,
  isMobileSafari,
  isValidCountry,
  showStaking,
} from '../libs/util';
import type { CheckoutCallbacks, CheckoutContext, CheckoutFormData } from '../types';
import PaymentError from './error';
import CheckoutFooter from './footer';
import PaymentForm, { hasDidWallet } from './form';
// import PaymentHeader from './header';
import OverviewSkeleton from './skeleton/overview';
import PaymentSkeleton from './skeleton/payment';
import PaymentSuccess from './success';
import PaymentSummary from './summary';
import { useMobile } from '../hooks/mobile';
import { formatPhone } from '../libs/phone-validator';
import { getCurrencyPreference } from '../libs/currency';

// eslint-disable-next-line react/no-unused-prop-types
type Props = CheckoutContext & CheckoutCallbacks & { completed?: boolean; error?: any; showCheckoutSummary?: boolean };

function PaymentInner({
  checkoutSession,
  paymentMethods,
  paymentLink,
  paymentIntent,
  customer,
  completed = false,
  mode,
  onPaid,
  onError,
  onChange,
  action,
  showCheckoutSummary = true,
  quotes,
  rateUnavailable,
  rateError,
}: MainProps) {
  const { t } = useLocaleContext();
  const { settings, session } = usePaymentContext();
  const { isMobile } = useMobile();
  const [state, setState] = useSetState<{
    checkoutSession: TCheckoutSessionExpanded;
    quotes?: any;
    rateUnavailable?: boolean;
    rateError?: string;
    paymentIntent?: TPaymentIntent | null;
    liveRateInfo?: {
      rate?: string;
      provider_id?: string;
      provider_name?: string;
      base_currency?: string;
      timestamp_ms?: number;
      volatility_threshold?: number;
    };
    liveQuoteSnapshot?: {
      id: string;
      quoted_amount: string;
      exchange_rate: string;
      expires_at: number;
      rate_timestamp_ms?: number | null;
      renewed?: boolean;
    };
    liveRateUnavailable?: boolean;
    liveRateError?: string;
    isRateLoading?: boolean;
  }>({
    checkoutSession,
    quotes,
    rateUnavailable,
    rateError,
    paymentIntent,
    liveRateInfo: undefined,
    liveQuoteSnapshot: undefined,
    liveRateUnavailable: false,
    liveRateError: undefined,
    isRateLoading: false,
  });
  // Track if currency is being switched (vs just rate refresh)
  // Only show skeleton during currency switch, not during rate refresh
  const isCurrencySwitchRef = useRef(false);
  const prevCurrencyIdRef = useRef<string | null>(null);
  const query = getQueryParams(window.location.href);

  const availableCurrencyIds = useMemo(() => {
    const currencyIds = new Set<string>();
    paymentMethods.forEach((method) => {
      method.payment_currencies.forEach((currency) => {
        if (currency.active) {
          currencyIds.add(currency.id);
        }
      });
    });
    return Array.from(currencyIds);
  }, [paymentMethods]);

  const defaultCurrencyId = useMemo(() => {
    // Keep session currency stable when a promotion is already applied.
    // Otherwise auto-picking from URL/local preference/no-wallet may switch currency
    // on refresh and trigger a second recalculate-promotion that removes discount
    // with `currency_incompatible`.
    const hasAppliedDiscount = Boolean((state.checkoutSession as any)?.discounts?.length);
    if (
      hasAppliedDiscount &&
      state.checkoutSession.currency_id &&
      availableCurrencyIds.includes(state.checkoutSession.currency_id)
    ) {
      return state.checkoutSession.currency_id;
    }

    // 1. first check url currencyId
    if (query.currencyId && availableCurrencyIds.includes(query.currencyId)) {
      return query.currencyId;
    }

    // 2. if user has no wallet, use the first available currency of stripe payment method
    if (session?.user && !hasDidWallet(session.user)) {
      const stripeCurrencyId = paymentMethods
        .find((m) => m.type === 'stripe')
        ?.payment_currencies.find((c) => c.active)?.id;
      if (stripeCurrencyId) {
        return stripeCurrencyId;
      }
    }

    // 3. then check user's saved currency preference
    const savedPreference = getCurrencyPreference(session?.user?.did, availableCurrencyIds);
    if (savedPreference) {
      return savedPreference;
    }

    // 4. finally use the currency in checkoutSession or the first available currency
    if (state.checkoutSession.currency_id && availableCurrencyIds.includes(state.checkoutSession.currency_id)) {
      return state.checkoutSession.currency_id;
    }
    return availableCurrencyIds?.[0];
  }, [query.currencyId, availableCurrencyIds, session?.user, state.checkoutSession, paymentMethods]);

  const defaultMethodId = paymentMethods.find((m) => m.payment_currencies.some((c) => c.id === defaultCurrencyId))?.id;
  const hideSummaryCard = mode.endsWith('-minimal') || !showCheckoutSummary;

  const methods = useForm<CheckoutFormData>({
    defaultValues: {
      customer_name: customer?.name || session?.user?.fullName || '',
      customer_email: customer?.email || session?.user?.email || '',
      customer_phone: formatPhone(customer?.phone || session?.user?.phone || ''),
      payment_method: defaultMethodId,
      payment_currency: defaultCurrencyId,
      billing_address: Object.assign(
        {
          country: session?.user?.address?.country || '',
          state: session?.user?.address?.province || '',
          city: session?.user?.address?.city || '',
          line1: session?.user?.address?.line1 || '',
          line2: session?.user?.address?.line2 || '',
          postal_code: session?.user?.address?.postalCode || '',
        },
        customer?.address || {},
        {
          country: isValidCountry(customer?.address?.country || session?.user?.address?.country || '')
            ? customer?.address?.country
            : 'us',
        }
      ),
    },
  });

  useEffect(() => {
    const hasAppliedDiscount = Boolean((state.checkoutSession as any)?.discounts?.length);
    const currentCurrency = methods.getValues('payment_currency');
    const currentMethod = methods.getValues('payment_method');

    if (defaultCurrencyId) {
      // Avoid overriding current currency on refresh when a promotion is already applied.
      if (!hasAppliedDiscount || !currentCurrency) {
        methods.setValue('payment_currency', defaultCurrencyId);
      }
    }
    if (defaultMethodId) {
      if (!hasAppliedDiscount || !currentMethod) {
        methods.setValue('payment_method', defaultMethodId);
      }
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [defaultCurrencyId, defaultMethodId, state.checkoutSession.discounts]);

  useEffect(() => {
    if (!isMobileSafari()) {
      return () => {};
    }
    let scrollTop = 0;
    const focusinHandler = () => {
      scrollTop = window.scrollY;
    };
    const focusoutHandler = () => {
      window.scrollTo(0, scrollTop);
    };
    document.body.addEventListener('focusin', focusinHandler);
    document.body.addEventListener('focusout', focusoutHandler);
    return () => {
      document.body.removeEventListener('focusin', focusinHandler);
      document.body.removeEventListener('focusout', focusoutHandler);
    };
  }, []);

  const currencyId = useWatch({ control: methods.control, name: 'payment_currency', defaultValue: defaultCurrencyId });
  const currency =
    (findCurrency(paymentMethods as TPaymentMethodExpanded[], currencyId as string) as TPaymentCurrency) ||
    settings.baseCurrency;
  const method = paymentMethods.find((x: any) => x.id === currency.payment_method_id) as TPaymentMethod;
  const hasDynamicPricing = useMemo(
    () =>
      state.checkoutSession.line_items.some(
        (item: any) => (item.upsell_price || item.price)?.pricing_type === 'dynamic'
      ),
    [state.checkoutSession.line_items]
  );
  // Stripe/USD payments don't need exchange rate - base_amount is already in USD
  const isStripePayment = method?.type === 'stripe';
  const needsExchangeRate = hasDynamicPricing && !isStripePayment;
  const effectiveRateUnavailable = needsExchangeRate && (state.rateUnavailable || state.liveRateUnavailable);
  // Don't expose technical errors to UI - only log them for debugging
  if (state.liveRateError || state.rateError) {
    console.error('[Rate Error]', { liveRateError: state.liveRateError, rateError: state.rateError });
  }

  // Manual refresh function - exposed for retry button
  const refreshRateRef = useRef<(() => Promise<void>) | null>(null);
  useEffect(() => {
    if (!currencyId) {
      return;
    }
    // Detect currency switch (not initial load)
    const isCurrencySwitch = prevCurrencyIdRef.current !== null && prevCurrencyIdRef.current !== currencyId;
    prevCurrencyIdRef.current = currencyId;

    if (isCurrencySwitch) {
      isCurrencySwitchRef.current = true;
      // Show skeleton during currency switch
      setState({ isRateLoading: true });
    }

    if (needsExchangeRate) {
      setState({
        liveRateInfo: undefined,
        liveQuoteSnapshot: undefined,
        liveRateUnavailable: false,
        liveRateError: undefined,
      });
      liveRateRefreshRef.current = false;
      refreshRateRef.current?.();
    } else {
      setState({
        liveRateInfo: undefined,
        liveQuoteSnapshot: undefined,
        liveRateUnavailable: false,
        liveRateError: undefined,
      });
      liveRateRefreshRef.current = false;
      // For Stripe payments (no exchange rate needed), clear loading after promotion recalc
      // If no discounts, clear immediately
      if (isCurrencySwitch && !(state.checkoutSession as any)?.discounts?.length) {
        setState({ isRateLoading: false });
        isCurrencySwitchRef.current = false;
      }
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [currencyId, needsExchangeRate]);

  useEffect(() => {
    // Skip exchange rate fetching for Stripe payments (USD base_amount is used directly)
    if (!state.checkoutSession?.id || completed || !needsExchangeRate) {
      return undefined;
    }
    let cancelled = false;
    let consecutiveFailures = 0;
    const baseInterval = 30000;
    const MAX_INTERVAL = 5 * 60 * 1000;
    const QUICK_RETRY_DELAY = 1000; // 1s quick retry
    const MAX_QUICK_RETRIES = 2; // Quick retry 2 times before exponential backoff
    let currentInterval = baseInterval;
    let timer: ReturnType<typeof setInterval> | null = null;

    const scheduleNext = () => {
      if (timer) {
        clearInterval(timer);
      }
      timer = window.setInterval(() => {
        fetchRate(false);
      }, currentInterval) as unknown as ReturnType<typeof setInterval>;
    };

    const fetchRate = async (isManualRetry = false) => {
      if (document.hidden && !isManualRetry) {
        return;
      }

      if (typeof navigator !== 'undefined' && !navigator.onLine) {
        return;
      }

      if (liveRateRefreshRef.current) {
        return;
      }
      liveRateRefreshRef.current = true;

      // Quick retry logic: try up to MAX_QUICK_RETRIES times with short delays
      let quickRetryCount = 0;
      let lastError: any = null;

      try {
        // eslint-disable-next-line no-await-in-loop
        while (quickRetryCount <= MAX_QUICK_RETRIES) {
          try {
            // eslint-disable-next-line no-await-in-loop
            const { data } = await api.get(`/api/checkout-sessions/${state.checkoutSession.id}/exchange-rate`, {
              params: currencyId ? { currency_id: currencyId } : undefined,
            });
            if (cancelled) {
              return;
            }
            consecutiveFailures = 0;
            currentInterval = baseInterval;
            // Final Freeze: Only store rate info, no quote_snapshot
            setState({
              liveRateInfo: data,
              liveQuoteSnapshot: undefined, // Quote is created at submit time only
              liveRateUnavailable: false,
              liveRateError: undefined,
            });
            // Clear loading state after rate fetch - but only if this was a currency switch
            // and no discounts need recalculation (recalculatePromotion will clear it otherwise)
            if (isCurrencySwitchRef.current && !(state.checkoutSession as any)?.discounts?.length) {
              setState({ isRateLoading: false });
              isCurrencySwitchRef.current = false;
            }
            scheduleNext();
            return;
          } catch (err: any) {
            lastError = err;
            quickRetryCount++;
            if (quickRetryCount <= MAX_QUICK_RETRIES && !cancelled) {
              // eslint-disable-next-line no-console
              console.log(
                `[Exchange Rate] Quick retry ${quickRetryCount}/${MAX_QUICK_RETRIES} after ${QUICK_RETRY_DELAY}ms`
              );
              // eslint-disable-next-line no-await-in-loop
              await new Promise((resolve) => {
                setTimeout(resolve, QUICK_RETRY_DELAY);
              });
            }
          }
        }

        // All quick retries failed
        if (cancelled) {
          return;
        }
        consecutiveFailures++;
        const technicalError = lastError?.response?.data?.error || formatError(lastError);
        console.error('[Exchange Rate Fetch Error]', {
          error: technicalError,
          consecutiveFailures,
          sessionId: state.checkoutSession?.id,
          currencyId,
        });

        setState({
          liveRateUnavailable: true,
          liveRateError: undefined,
        });

        if (consecutiveFailures >= 3) {
          console.warn('Exchange rate fetch failed multiple times', { consecutiveFailures, technicalError });
        }

        const nextInterval = Math.min(baseInterval * 2 ** (consecutiveFailures - 1), MAX_INTERVAL);
        currentInterval = nextInterval;
        scheduleNext();
      } finally {
        liveRateRefreshRef.current = false;
      }
    };

    // Expose refresh function for manual retry
    refreshRateRef.current = async () => {
      liveRateRefreshRef.current = false; // Allow immediate retry
      await fetchRate(true);
    };

    fetchRate(false);

    const handleVisibilityChange = () => {
      if (!document.hidden) {
        fetchRate(false);
      }
    };
    document.addEventListener('visibilitychange', handleVisibilityChange);

    return () => {
      cancelled = true;
      refreshRateRef.current = null;
      if (timer) {
        clearInterval(timer);
      }
      document.removeEventListener('visibilitychange', handleVisibilityChange);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [state.checkoutSession?.id, currencyId, completed, needsExchangeRate]);

  const recalculatePromotion = async () => {
    if ((state.checkoutSession as any)?.discounts?.length) {
      try {
        await api.post(`/api/checkout-sessions/${state.checkoutSession.id}/recalculate-promotion`, {
          currency_id: currencyId,
        });
        // Wait for session update before clearing loading state
        await onPromotionUpdate();
      } catch (err) {
        console.error('[recalculatePromotion] Error:', err);
      } finally {
        // Clear loading state after promotion recalculation AND session update complete
        if (isCurrencySwitchRef.current) {
          setState({ isRateLoading: false });
          isCurrencySwitchRef.current = false;
        }
      }
    } else if (isCurrencySwitchRef.current) {
      // No discounts to recalculate - clear loading immediately
      setState({ isRateLoading: false });
      isCurrencySwitchRef.current = false;
    }
  };

  useEffect(() => {
    if (onChange) {
      onChange(methods.getValues());
    }
    recalculatePromotion();
  }, [currencyId]); // eslint-disable-line

  const onUpsell = async (from: string, to: string) => {
    try {
      const { data } = await api.put(`/api/checkout-sessions/${state.checkoutSession.id}/upsell`, { from, to });
      if (data.discounts?.length) {
        recalculatePromotion();
        return;
      }
      setState({ checkoutSession: data });
    } catch (err) {
      console.error(err);
      Toast.error(formatError(err));
    }
  };

  const onDownsell = async (from: string) => {
    try {
      const { data } = await api.put(`/api/checkout-sessions/${state.checkoutSession.id}/downsell`, { from });
      if (data.discounts?.length) {
        recalculatePromotion();
        return;
      }
      setState({ checkoutSession: data });
    } catch (err) {
      console.error(err);
      Toast.error(formatError(err));
    }
  };

  const onApplyCrossSell = async (to: string) => {
    try {
      const { data } = await api.put(`/api/checkout-sessions/${state.checkoutSession.id}/cross-sell`, { to });
      if (data.discounts?.length) {
        recalculatePromotion();
        return;
      }
      setState({ checkoutSession: data });
    } catch (err) {
      console.error(err);
      Toast.error(formatError(err));
    }
  };

  const onQuantityChange = async (itemId: string, quantity: number) => {
    try {
      const { data } = await api.put(`/api/checkout-sessions/${state.checkoutSession.id}/adjust-quantity`, {
        itemId,
        quantity,
        currency_id: currencyId,
      });
      if (data.discounts?.length) {
        recalculatePromotion();
        return;
      }
      setState({
        checkoutSession: data,
        ...(data.rateUnavailable !== undefined && { rateUnavailable: data.rateUnavailable }),
        ...(data.rateError !== undefined && { rateError: data.rateError }),
        ...(data.quotes !== undefined && { quotes: data.quotes }),
      });
    } catch (err) {
      console.error(err);
      Toast.error(formatError(err));
    }
  };

  const onCancelCrossSell = async () => {
    try {
      const { data } = await api.delete(`/api/checkout-sessions/${state.checkoutSession.id}/cross-sell`);
      if (data.discounts?.length) {
        recalculatePromotion();
        return;
      }
      setState({ checkoutSession: data });
    } catch (err) {
      console.error(err);
      Toast.error(formatError(err));
    }
  };

  const onChangeAmount = async ({ priceId, amount }: { priceId: string; amount: string }) => {
    try {
      const { data } = await api.put(`/api/checkout-sessions/${state.checkoutSession.id}/amount`, {
        priceId,
        amount: fromTokenToUnit(amount, currency.decimal).toString(),
      });
      if (data.discounts?.length) {
        recalculatePromotion();
        return;
      }
      setState({ checkoutSession: data });
    } catch (err) {
      console.error(err);
      Toast.error(formatError(err));
    }
  };

  const onPromotionUpdate = async () => {
    try {
      const { data } = await api.get(`/api/checkout-sessions/retrieve/${state.checkoutSession.id}`);
      setState({ checkoutSession: data.checkoutSession });
    } catch (err) {
      console.error(err);
      Toast.error(formatError(err));
    }
  };

  const handlePaid = (result: any) => {
    setState({ checkoutSession: result.checkoutSession });
    onPaid(result);
  };

  const handleQuoteUpdated = (payload: {
    checkoutSession: TCheckoutSessionExpanded;
    quotes?: any;
    rateUnavailable?: boolean;
    rateError?: string;
    paymentIntent?: TPaymentIntent | null;
  }) => {
    setState({
      checkoutSession: payload.checkoutSession,
      ...(payload.quotes !== undefined && { quotes: payload.quotes }),
      ...(payload.rateUnavailable !== undefined && { rateUnavailable: payload.rateUnavailable }),
      ...(payload.rateError !== undefined && { rateError: payload.rateError }),
      ...(payload.paymentIntent !== undefined && { paymentIntent: payload.paymentIntent }),
    });
  };

  const handlePaymentIntentUpdate = (intent: TPaymentIntent | null) => {
    setState({ paymentIntent: intent });
  };

  const quoteRefreshRef = useRef(false);
  const liveRateRefreshRef = useRef(false);
  const handleQuoteExpired = async (forceRefresh = false) => {
    if (quoteRefreshRef.current) {
      return;
    }
    quoteRefreshRef.current = true;
    try {
      const { data } = await api.get(`/api/checkout-sessions/retrieve/${state.checkoutSession.id}`, {
        params: forceRefresh ? { forceRefresh: '1' } : undefined,
      });
      handleQuoteUpdated({
        checkoutSession: data.checkoutSession,
        quotes: data.quotes,
        rateUnavailable: data.rateUnavailable,
        rateError: data.rateError,
        paymentIntent: data.paymentIntent,
      });
    } catch (err) {
      console.error(err);
      Toast.error(formatError(err));
    } finally {
      quoteRefreshRef.current = false;
    }
  };

  // trialing can be limited with trial_currency, which can be a list
  let trialInDays = Number(state.checkoutSession?.subscription_data?.trial_period_days || 0);
  let trialEnd = Number(state.checkoutSession?.subscription_data?.trial_end || 0);
  const trialCurrencyIds = (state.checkoutSession?.subscription_data?.trial_currency || '')
    .split(',')
    .map(trim)
    .filter(Boolean);
  if (trialCurrencyIds.length > 0 && trialCurrencyIds.includes(currencyId) === false) {
    trialInDays = 0;
    trialEnd = 0;
  }

  const showFeatures = `${paymentLink?.metadata?.show_product_features}` === 'true';

  return (
    <FormProvider {...methods}>
      <Stack className="cko-container" sx={{ gap: { sm: mode === 'standalone' ? 0 : mode === 'inline' ? 4 : 8 } }}>
        {!hideSummaryCard && (
          <Fade in>
            <Stack className="base-card cko-overview" direction="column">
              <PaymentSummary
                items={state.checkoutSession.line_items}
                trialInDays={trialInDays}
                trialEnd={trialEnd}
                billingThreshold={Math.max(
                  state.checkoutSession.subscription_data?.billing_threshold_amount || 0,
                  // @ts-ignore
                  state.checkoutSession.subscription_data?.min_stake_amount || 0
                )}
                showStaking={showStaking(method, currency, !!state.checkoutSession.subscription_data?.no_stake)}
                currency={currency}
                paymentIntent={state.paymentIntent || paymentIntent}
                onUpsell={onUpsell}
                onDownsell={onDownsell}
                onQuantityChange={onQuantityChange}
                onApplyCrossSell={onApplyCrossSell}
                onCancelCrossSell={onCancelCrossSell}
                onChangeAmount={onChangeAmount}
                checkoutSessionId={state.checkoutSession.id}
                crossSellBehavior={state.checkoutSession.cross_sell_behavior}
                donationSettings={paymentLink?.donation_settings}
                action={action}
                completed={completed}
                checkoutSession={state.checkoutSession}
                onPromotionUpdate={onPromotionUpdate}
                paymentMethods={paymentMethods as TPaymentMethodExpanded[]}
                showFeatures={showFeatures}
                rateUnavailable={effectiveRateUnavailable}
                isRateLoading={state.isRateLoading}
                liveRate={state.liveRateInfo}
                liveQuoteSnapshot={state.liveQuoteSnapshot}
                onQuoteExpired={handleQuoteExpired}
                onRefreshRate={refreshRateRef.current || undefined}
                isStripePayment={isStripePayment}
                onSlippageChange={async (slippageConfig) => {
                  try {
                    const { data } = await api.put(`/api/checkout-sessions/${state.checkoutSession.id}/slippage`, {
                      slippage_config: slippageConfig,
                    });
                    handleQuoteUpdated({
                      checkoutSession: data.checkoutSession || state.checkoutSession,
                      quotes: data.quotes,
                      rateUnavailable: data.rateUnavailable,
                      rateError: data.rateError,
                    });
                  } catch (err) {
                    console.error('Failed to update slippage', err);
                  }
                }}
              />
              {mode === 'standalone' && !isMobile && (
                <CheckoutFooter className="cko-footer" sx={{ color: 'text.lighter' }} />
              )}
            </Stack>
          </Fade>
        )}
        <Stack
          className="base-card cko-payment"
          direction="column"
          spacing={{
            xs: 2,
            sm: 3,
          }}>
          {completed && (
            <PaymentSuccess
              mode={mode}
              pageInfo={state.checkoutSession.metadata?.page_info}
              vendorCount={state.checkoutSession.line_items.reduce((total: number, item: any) => {
                return total + (item.price?.product?.vendor_config?.length || 0);
              }, 0)}
              sessionId={state.checkoutSession.id}
              payee={getStatementDescriptor(state.checkoutSession.line_items)}
              action={state.checkoutSession.mode}
              invoiceId={state.checkoutSession.invoice_id}
              subscriptionId={state.checkoutSession.subscription_id}
              subscriptions={state.checkoutSession.subscriptions}
              message={
                paymentLink?.after_completion?.hosted_confirmation?.custom_message ||
                t(
                  `payment.checkout.completed.${
                    state.checkoutSession.submit_type === 'donate' ? 'donate' : state.checkoutSession.mode
                  }`
                )
              }
            />
          )}
          {!completed && (
            <PaymentForm
              currencyId={currencyId}
              checkoutSession={state.checkoutSession}
              paymentMethods={paymentMethods as TPaymentMethodExpanded[]}
              paymentIntent={state.paymentIntent || paymentIntent}
              paymentLink={paymentLink}
              customer={customer}
              onPaid={handlePaid}
              onError={onError}
              onQuoteUpdated={handleQuoteUpdated}
              onPaymentIntentUpdate={handlePaymentIntentUpdate}
              mode={mode}
              action={action}
              rateUnavailable={effectiveRateUnavailable}
            />
          )}
        </Stack>
        {mode === 'standalone' && isMobile && <CheckoutFooter className="cko-footer" sx={{ color: 'text.lighter' }} />}
      </Stack>
    </FormProvider>
  );
}

export default function Payment({
  checkoutSession,
  paymentMethods,
  paymentIntent,
  paymentLink,
  customer,
  completed = false,
  error = null,
  mode,
  onPaid,
  onError,
  onChange,
  goBack,
  action,
  showCheckoutSummary = true,
  quotes,
  rateUnavailable,
  rateError,
}: Props) {
  const { t } = useLocaleContext();
  const { refresh, livemode, setLivemode } = usePaymentContext();
  const [delay, setDelay] = useState(false);
  const { isMobile } = useMobile();
  const hideSummaryCard = mode.endsWith('-minimal') || !showCheckoutSummary;
  const isMobileSafariEnv = isMobileSafari();

  useEffect(() => {
    setTimeout(() => {
      // 骨架屏 delay
      setDelay(true);
    }, 500);
  }, []);

  useEffect(() => {
    if (checkoutSession) {
      if (livemode !== checkoutSession.livemode) {
        setLivemode(checkoutSession.livemode);
      }
    }
  }, [checkoutSession, livemode, setLivemode, refresh]);

  const renderContent = () => {
    if (error) {
      return <PaymentError mode={mode} title="Oops" description={formatError(error)} />;
    }
    if (!checkoutSession || !delay) {
      return (
        <Stack className="cko-container" sx={{ gap: { sm: mode === 'standalone' ? 0 : mode === 'inline' ? 4 : 8 } }}>
          {!hideSummaryCard && (
            <Stack className="base-card cko-overview">
              <Box className="cko-product">
                <OverviewSkeleton />
              </Box>
            </Stack>
          )}
          <Stack className="base-card cko-payment">
            <PaymentSkeleton />
          </Stack>
        </Stack>
      );
    }
    if (checkoutSession.expires_at <= Math.round(Date.now() / 1000)) {
      return (
        <PaymentError
          mode={mode}
          title={t('payment.checkout.expired.title')}
          description={t('payment.checkout.expired.description')}
        />
      );
    }
    if (!checkoutSession.line_items.length) {
      return (
        <PaymentError
          mode={mode}
          title={t('payment.checkout.emptyItems.title')}
          description={t('payment.checkout.emptyItems.description')}
        />
      );
    }
    return (
      <PaymentInner
        checkoutSession={checkoutSession}
        paymentMethods={paymentMethods as TPaymentMethodExpanded[]}
        paymentLink={paymentLink}
        paymentIntent={paymentIntent}
        completed={completed || checkoutSession.status === 'complete'}
        customer={customer as TCustomer}
        onPaid={onPaid}
        onError={onError}
        onChange={onChange}
        goBack={goBack}
        mode={mode}
        action={action}
        showCheckoutSummary={showCheckoutSummary}
        quotes={quotes}
        rateUnavailable={rateUnavailable}
        rateError={rateError}
      />
    );
  };

  return (
    <Stack
      sx={{
        display: 'flex',
        flexDirection: 'column',
        height: mode === 'standalone' ? '100vh' : 'auto',
        overflow: isMobileSafariEnv ? 'visible' : 'hidden',
      }}>
      {mode === 'standalone' ? (
        <Header
          meta={undefined}
          addons={undefined}
          sessionManagerProps={undefined}
          homeLink={undefined}
          theme={undefined}
          hideNavMenu={undefined}
          maxWidth={false}
          sx={{ borderBottom: '1px solid', borderColor: 'divider' }}
        />
      ) : null}
      <Root
        mode={mode}
        sx={{
          flex: 1,
          overflow: {
            xs: isMobileSafariEnv ? 'visible' : 'auto',
            md: 'hidden',
          },
          ...(isMobile && mode === 'standalone'
            ? {
                '.cko-payment-submit-btn': {
                  position: 'fixed',
                  bottom: 20,
                  left: 0,
                  right: 0,
                  zIndex: 999,
                  backgroundColor: 'background.paper',
                  padding: '10px',
                  textAlign: 'center',
                  button: {
                    maxWidth: 542,
                  },
                },
                '.cko-footer': {
                  position: 'fixed',
                  bottom: 0,
                  left: 0,
                  right: 0,
                  zIndex: 999,
                  backgroundColor: 'background.paper',
                  marginBottom: 0,
                },
                '.cko-payment': {
                  paddingBottom: '100px',
                },
              }
            : {}),
        }}>
        {goBack && (
          <ArrowBackOutlined
            sx={{ mr: 0.5, color: 'text.secondary', alignSelf: 'flex-start', margin: '16px 0', cursor: 'pointer' }}
            onClick={goBack}
            fontSize="medium"
          />
        )}
        <Stack className="cko-container" sx={{ gap: { sm: mode === 'standalone' ? 0 : mode === 'inline' ? 4 : 8 } }}>
          {renderContent()}
        </Stack>
      </Root>
    </Stack>
  );
}

type MainProps = CheckoutContext & CheckoutCallbacks & { completed?: boolean; showCheckoutSummary?: boolean };

type RootProps = { mode: LiteralUnion<'standalone' | 'inline' | 'popup', string> } & BoxProps;
export const Root: React.FC<RootProps> = styled(Box)<RootProps>`
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  overflow: hidden;
  position: relative;
  .cko-container {
    overflow: hidden;
    width: 100%;
    display: flex;
    flex-direction: row;
    justify-content: center;
    position: relative;
    flex: 1;
    padding: 1px;
  }

  .base-card {
    border: none;
    border-radius: 0;
    padding: ${(props) => (props.mode === 'standalone' ? '100px 40px 20px' : '20px 0')};
    box-shadow: none;
    flex: 1;
    max-width: 582px;
  }

  .cko-overview {
    position: relative;
    flex-direction: column;
    display: ${(props) => (props.mode.endsWith('-minimal') ? 'none' : 'flex')};
    background: ${({ theme }) => theme.palette.background.default};
    min-height: 'auto';
  }
  .cko-header {
    left: 0;
    margin-bottom: 0;
    position: absolute;
    right: 0;
    top: 0;
    transition:
      background-color 0.15s ease,
      box-shadow 0.15s ease-out;
  }
  .cko-product {
    flex: 1;
    overflow: hidden;
  }
  .cko-product-summary {
    width: 100%;
  }
  .cko-ellipsis {
    width: 100%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .cko-payment {
    width: 502px;
    padding-left: ${(props) => (props.mode === 'standalone' ? '40px' : '20px')};
    position: relative;
    &:before {
      -webkit-animation-fill-mode: both;
      content: '';
      height: 100%;
      position: absolute;
      left: 0px;
      top: 0px;
      transform-origin: left center;
      width: 8px;
      box-shadow: -4px 0px 8px 0px rgba(2, 7, 19, 0.04);
    }
  }
  .cko-payment-contact {
    overflow: hidden;
  }
  .cko-footer {
    display: ${(props) => (props.mode.endsWith('-minimal') ? 'none' : 'block')};
    text-align: center;
    margin-top: 20px;
  }

  .cko-payment-form {
    .MuiFormLabel-root {
      color: ${({ theme }) => theme.palette.grey.A700};
      font-weight: 500;
      margin-top: 12px;
      margin-bottom: 4px;
    }
    .MuiBox-root {
      margin: 0;
    }

    .MuiFormHelperText-root {
      margin-left: 14px;
    }
  }

  .cko-payment-methods {
  }

  .cko-payment-submit {
    .MuiButtonBase-root {
      font-size: 1.3rem;
      position: relative;
    }

    .cko-submit-progress {
      position: absolute;
      top: 0;
      width: 100%;
      height: 100%;
      opacity: 0.3;
    }
  }

  .cko-header {
  }

  .product-item {
    border-radius: ${({ theme }) => `${2 * (theme.shape.borderRadius as number)}px`};
    border: 1px solid;
    border-color: ${({ theme }) => theme.palette.divider};
    .product-item-content {
      padding: 16px;
      background: ${({ theme }) => theme.palette.grey[50]};
      border-top-left-radius: ${({ theme }) => `${2 * (theme.shape.borderRadius as number)}px`};
      border-top-right-radius: ${({ theme }) => `${2 * (theme.shape.borderRadius as number)}px`};
    }
    .product-item-upsell {
      margin-top: 0;
      padding: 16px;
      background: ${({ theme }) => theme.palette.grey[100]};
      border-bottom-left-radius: ${({ theme }) => `${2 * (theme.shape.borderRadius as number)}px`};
      border-bottom-right-radius: ${({ theme }) => `${2 * (theme.shape.borderRadius as number)}px`};
    }
    .product-item-content:only-child {
      border-radius: ${({ theme }) => `${2 * (theme.shape.borderRadius as number)}px`};
    }
  }

  @media (max-width: ${({ theme }) => theme.breakpoints.values.md}px) {
    padding-top: 0;
    overflow: auto;
    &:before {
      display: none;
    }
    .cko-container {
      flex-direction: column;
      align-items: center;
      justify-content: flex-start;
      gap: 0;
      overflow: visible;
      min-width: 200px;
    }
    .cko-overview {
      width: 100%;
      min-height: auto;
      flex: none;
    }
    .cko-payment {
      width: 100%;
      height: fit-content;
      flex: none;
      border-top: 1px solid;
      border-color: ${({ theme }) => theme.palette.divider};
      &:before {
        display: none;
      }
    }

    .cko-footer {
      position: relative;
      margin-bottom: 4px;
      margin-top: 0;
      bottom: 0;
      left: 0;
      transform: translateX(0);
    }
    .base-card {
      box-shadow: none;
      border-radius: 0;
      padding: 20px;
    }
  }
`;
