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 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 = () => {
    setState({ completed: true });
    onPaid?.(data as CheckoutContext);
  };

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

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