/* 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,
  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, useState, useMemo } 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,
}: MainProps) {
  const { t } = useLocaleContext();
  const { settings, session } = usePaymentContext();
  const { isMobile } = useMobile();
  const [state, setState] = useSetState<{ checkoutSession: TCheckoutSessionExpanded }>({ checkoutSession });
  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(() => {
    // 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.currency_id, 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(() => {
    if (defaultCurrencyId) {
      methods.setValue('payment_currency', defaultCurrencyId);
    }
    if (defaultMethodId) {
      methods.setValue('payment_method', defaultMethodId);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [defaultCurrencyId, defaultMethodId]);

  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;

  useEffect(() => {
    if (onChange) {
      onChange(methods.getValues());
    }
  }, [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 });
      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 });
      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 });
      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,
      });
      setState({ checkoutSession: data });
    } 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`);
      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(),
      });
      setState({ checkoutSession: data });
    } catch (err) {
      console.error(err);
      Toast.error(formatError(err));
    }
  };

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

  // 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;
  }

  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}
                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}
              />
              {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}
              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={paymentIntent}
              paymentLink={paymentLink}
              customer={customer}
              onPaid={handlePaid}
              onError={onError}
              mode={mode}
              action={action}
            />
          )}
        </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,
}: 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}
      />
    );
  };

  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: 'grey.100' }}
        />
      ) : 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.grey[100]};
    .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`};
    }
  }

  @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.grey[100]};
      &: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;
    }
  }
`;
