import type { TCheckoutSessionExpanded, TPaymentMethodExpanded } from '@blocklet/payment-types';
import { useRequest, useSetState } from 'ahooks';
import noop from 'lodash/noop';
import { useEffect } from 'react';
import { joinURL } from 'ufo';

import { ReactGA } from '@arcblock/ux/lib/withTracker';
import type { PayFailedEvent, PaySuccessEvent } from '@arcblock/ux/lib/withTracker/action/pay';
import api from '../libs/api';
import { getPrefix, mergeExtraParams } from '../libs/util';
import Payment from '../payment';
import type { CheckoutContext, CheckoutProps } from '../types';
import { PaymentThemeProvider } from '../theme';
import DonationForm from '../payment/donation-form';

const promises: { [key: string]: Promise<any> } = {};
const startFromPaymentLink = (id: string, params?: Record<string, any>): Promise<CheckoutContext> => {
  if (!promises[id]) {
    promises[id] = api
      .post(`/api/checkout-sessions/start/${id}?${mergeExtraParams(params)}`)
      .then((res: any) => res?.data)
      .finally(() => {
        setTimeout(() => {
          delete promises[id];
        }, 3000);
      });
  }

  return promises[id];
};

const fetchCheckoutSession = async (id: string): Promise<CheckoutContext> => {
  const { data } = await api.get(`/api/checkout-sessions/retrieve/${id}`);
  return data;
};

export default function CheckoutForm({
  id,
  mode = 'inline',
  onPaid = noop,
  onError = console.error,
  onChange,
  goBack,
  extraParams = {},
  action = '',
  theme = 'default',
  formType = 'payment',
  ...restProps
}: CheckoutProps) {
  if (!id.startsWith('plink_') && !id.startsWith('cs_')) {
    throw new Error('Either a checkoutSession or a paymentLink id is required.');
  }

  const type = id.startsWith('plink_') ? 'paymentLink' : 'checkoutSession';

  const [state, setState] = useSetState({ completed: false, appError: null });

  const { error: apiError, data } = useRequest(() =>
    type === 'paymentLink' ? startFromPaymentLink(id, extraParams) : fetchCheckoutSession(id)
  );

  useEffect(() => {
    if (type === 'paymentLink' && mode === 'standalone' && data) {
      window.history.replaceState(
        null,
        '',
        joinURL(getPrefix(), `/checkout/pay/${data.checkoutSession.id}?${mergeExtraParams(extraParams)}`)
      );
    }
  }, [type, mode, data, extraParams]);

  const handlePaid = (result: CheckoutContext) => {
    setState({ completed: true });
    onPaid?.(result as CheckoutContext);

    const paySuccessEvent: PaySuccessEvent = {
      action: 'paySuccess',
      // @ts-ignore 后续升级的话就会报错了，移除这个 lint 即可
      mode: data?.checkoutSession?.mode!,
      success: true,
    };
    ReactGA.event(paySuccessEvent.action, paySuccessEvent);
  };

  const handleError = (err: any) => {
    console.error(err);
    setState({ appError: err });
    onError?.(err);

    const payFailedEvent: PayFailedEvent = {
      action: 'payFailed',
      // @ts-ignore后续升级的话就会报错了，移除这个 lint 即可
      mode: data?.checkoutSession?.mode!,
      errorMessage: err.message,
      success: false,
    };
    ReactGA.event(payFailedEvent.action, payFailedEvent);
  };

  const Checkout =
    formType === 'donation' ? (
      <DonationForm
        checkoutSession={data?.checkoutSession as TCheckoutSessionExpanded}
        paymentMethods={data?.paymentMethods as TPaymentMethodExpanded[]}
        paymentIntent={data?.paymentIntent}
        paymentLink={data?.paymentLink}
        customer={data?.customer}
        completed={state.completed}
        error={apiError}
        onPaid={handlePaid}
        onError={handleError}
        onChange={onChange}
        goBack={goBack}
        mode={mode as string}
        action={action}
        id={id}
        {...restProps}
      />
    ) : (
      <Payment
        checkoutSession={data?.checkoutSession as TCheckoutSessionExpanded}
        paymentMethods={data?.paymentMethods as TPaymentMethodExpanded[]}
        paymentIntent={data?.paymentIntent}
        paymentLink={data?.paymentLink}
        customer={data?.customer}
        completed={state.completed}
        error={apiError}
        onPaid={handlePaid}
        onError={handleError}
        onChange={onChange}
        goBack={goBack}
        mode={mode as string}
        action={action}
        {...restProps}
      />
    );

  if (theme === 'inherit') {
    return Checkout;
  }
  if (theme && typeof theme === 'object') {
    return <PaymentThemeProvider theme={theme}>{Checkout}</PaymentThemeProvider>;
  }
  return <PaymentThemeProvider>{Checkout}</PaymentThemeProvider>;
}
