/* eslint-disable react/require-default-props */
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
import Toast from '@arcblock/ux/lib/Toast';
import Dialog from '@arcblock/ux/lib/Dialog/dialog';
import type { Customer, TPaymentMethod, TInvoiceExpanded } from '@blocklet/payment-types';
import { Button, Typography } from '@mui/material';
import { useSetState } from 'ahooks';
import { useEffect, useRef } from 'react';

import StripeForm from '../payment/form/stripe';
import api from '../libs/api';
import { formatError } from '../libs/util';

export interface StripePaymentActionProps {
  invoice?: TInvoiceExpanded;
  invoiceIds?: string[];
  subscriptionId?: string;
  customerId?: string;
  currencyId?: string;
  paymentMethod?: TPaymentMethod;

  autoTrigger?: boolean;
  onExternalPayment?: (invoiceId?: string) => void;

  onSuccess?: () => void;
  onError?: (error: Error) => void;
  onClose?: () => void;

  children?: (onPay: () => void, loading: boolean) => React.ReactNode;
}

export default function StripePaymentAction(props: StripePaymentActionProps) {
  const {
    invoice,
    invoiceIds,
    subscriptionId,
    customerId,
    currencyId,
    paymentMethod,
    autoTrigger = false,
    onExternalPayment,
    onSuccess,
    onError,
    onClose,
    children,
  } = props;
  const { t } = useLocaleContext();

  const [state, setState] = useSetState<{
    paying: boolean;
    confirmDialog: boolean;
    stripeDialog: boolean;
    clientSecret: string | null;
    publishableKey: string | null;
    customer: Customer | null;
  }>({
    paying: false,
    confirmDialog: false,
    stripeDialog: false,
    clientSecret: null,
    publishableKey: null,
    customer: null,
  });

  const autoTriggerRef = useRef(false);

  useEffect(() => {
    if (autoTrigger && !autoTriggerRef.current) {
      autoTriggerRef.current = true;
      handlePay();
    }
  }, [autoTrigger]); // eslint-disable-line react-hooks/exhaustive-deps

  const handlePay = async () => {
    if (state.paying) {
      return;
    }

    const hasSubscription = !!(subscriptionId || invoice?.subscription_id);
    const method = paymentMethod || invoice?.paymentMethod;
    const shouldShowConfirm = hasSubscription && method?.type === 'stripe';

    if (shouldShowConfirm) {
      setState({ confirmDialog: true });
      return;
    }

    await proceedWithPayment();
  };

  const proceedWithPayment = async () => {
    setState({ paying: true, confirmDialog: false });

    const derivedCurrencyId = currencyId || invoice?.currency_id || invoice?.paymentCurrency?.id;
    const derivedPaymentMethod = paymentMethod || invoice?.paymentMethod;
    const isStripePayment = derivedPaymentMethod?.type === 'stripe';

    if (isStripePayment && derivedCurrencyId) {
      const stripePayload: Record<string, any> = {};

      if (invoiceIds && invoiceIds.length > 0) {
        stripePayload.invoice_ids = invoiceIds;
      } else if (invoice) {
        stripePayload.invoice_ids = [invoice.id];
      } else if (subscriptionId) {
        stripePayload.subscription_id = subscriptionId;
      } else if (customerId) {
        stripePayload.customer_id = customerId;
      }

      if (derivedCurrencyId) {
        stripePayload.currency_id = derivedCurrencyId;
      }

      try {
        const { data: paymentData } = await api.post('/api/invoices/pay-stripe', stripePayload);
        setState({
          paying: false,
          stripeDialog: true,
          clientSecret: paymentData.client_secret,
          publishableKey: paymentData.publishable_key,
          customer: paymentData.customer || null,
        });
        return;
      } catch (err: any) {
        const error = formatError(err);
        Toast.error(error);
        setState({ paying: false });
        onError?.(error);
        return;
      }
    }

    setState({ paying: false });
    if (onExternalPayment) {
      onExternalPayment(invoice?.id);
      return;
    }

    Toast.error(t('payment.customer.invoice.payError'));
    onError?.(new Error('EXTERNAL_PAYMENT_HANDLER_NOT_PROVIDED'));
  };

  const handleConfirmCancel = () => {
    setState({ confirmDialog: false, paying: false });
    onClose?.();
  };

  const handleStripeConfirm = () => {
    Toast.success(t('payment.customer.invoice.payProcessing'));
    setState({
      paying: false,
      stripeDialog: false,
      clientSecret: null,
      publishableKey: null,
      customer: null,
    });

    setTimeout(() => {
      onSuccess?.();
    }, 2000);
  };

  const handleStripeCancel = () => {
    setState({
      paying: false,
      stripeDialog: false,
      clientSecret: null,
      publishableKey: null,
      customer: null,
    });
    onClose?.();
  };

  return (
    <>
      {children?.(handlePay, state.paying)}

      {state.confirmDialog && (
        <Dialog
          open={state.confirmDialog}
          title={t('payment.customer.invoice.paymentConfirmTitle')}
          onClose={handleConfirmCancel}
          maxWidth="sm"
          PaperProps={{
            style: {
              minHeight: 0,
            },
          }}
          actions={[
            <Button key="cancel" variant="outlined" onClick={handleConfirmCancel}>
              {t('common.cancel')}
            </Button>,
            <Button key="continue" variant="contained" onClick={proceedWithPayment}>
              {t('payment.customer.invoice.continue')}
            </Button>,
          ]}>
          <Typography variant="body1" sx={{ color: 'text.secondary', mt: -2 }}>
            {t('payment.customer.invoice.paymentConfirmDescription')}
          </Typography>
        </Dialog>
      )}

      {state.stripeDialog && state.clientSecret && state.publishableKey && state.customer && (
        <StripeForm
          clientSecret={state.clientSecret}
          intentType="setup_intent"
          publicKey={state.publishableKey}
          customer={state.customer}
          mode="setup"
          title={t('payment.customer.invoice.pay')}
          submitButtonText={t('common.submit')}
          onConfirm={handleStripeConfirm}
          onCancel={handleStripeCancel}
          returnUrl={window.location.href}
        />
      )}
    </>
  );
}
