/* eslint-disable @typescript-eslint/indent */
import { useEffect, useMemo, useRef, useState } from 'react';
import { Button, Typography, Stack, Alert, SxProps, useTheme } from '@mui/material';
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
import Toast from '@arcblock/ux/lib/Toast';
import { joinURL } from 'ufo';
import type {
  Customer,
  Invoice,
  PaymentCurrency,
  PaymentMethod,
  Subscription,
  TInvoiceExpanded,
} from '@blocklet/payment-types';
import { useRequest } from 'ahooks';
import pWaitFor from 'p-wait-for';
import { Dialog } from '@arcblock/ux';
import { CheckCircle as CheckCircleIcon } from '@mui/icons-material';
import debounce from 'lodash/debounce';
import { usePaymentContext } from '../contexts/payment';
import { formatAmount, formatError, getPrefix, isCrossOrigin } from '../libs/util';
import { useSubscription } from '../hooks/subscription';
import api from '../libs/api';
import LoadingButton from './loading-button';

type DialogProps = {
  open?: boolean;
  onClose?: () => void;
  title?: string;
};

type DetailLinkOptions = {
  enabled?: boolean;
  onClick?: (e: React.MouseEvent) => void;
  title?: string;
};

type Props = {
  subscriptionId?: string;
  customerId?: string;
  mode?: 'default' | 'custom';
  onPaid?: (id: string, currencyId: string, type: 'subscription' | 'customer') => void;
  dialogProps?: DialogProps;
  detailLinkOptions?: DetailLinkOptions;
  successToast?: boolean;
  alertMessage?: string; // only for customer
  children?: (
    handlePay: (item: SummaryItem) => void,
    data: {
      subscription?: Subscription;
      summary: { [key: string]: SummaryItem };
      invoices: Invoice[];
      subscriptionCount?: number;
      detailUrl: string;
    }
  ) => React.ReactNode;
  authToken?: string;
};

type SummaryItem = {
  amount: string;
  currency: PaymentCurrency;
  method: PaymentMethod;
};

type OverdueInvoicesResult = {
  subscription?: Subscription;
  summary: { [key: string]: SummaryItem };
  invoices: Invoice[];
  subscriptionCount?: number;
  customer?: Customer;
};

const fetchOverdueInvoices = async (params: {
  subscriptionId?: string;
  customerId?: string;
  authToken?: string;
}): Promise<OverdueInvoicesResult> => {
  if (!params.subscriptionId && !params.customerId) {
    throw new Error('Either subscriptionId or customerId must be provided');
  }

  let url;
  if (params.subscriptionId) {
    url = `/api/subscriptions/${params.subscriptionId}/overdue/invoices`;
  } else {
    url = `/api/customers/${params.customerId}/overdue/invoices`;
  }

  const res = await api.get(params.authToken ? joinURL(url, `?authToken=${params.authToken}`) : url);
  return res.data;
};

function OverdueInvoicePayment({
  subscriptionId = undefined,
  customerId = undefined,
  mode = 'default',
  dialogProps = {
    open: true,
  },
  children = undefined,
  onPaid = () => {},
  detailLinkOptions = { enabled: true },
  successToast = true,
  alertMessage = '',
  authToken = undefined,
}: Props) {
  const { t, locale } = useLocaleContext();
  const theme = useTheme();
  const { connect, session } = usePaymentContext();
  const [selectCurrencyId, setSelectCurrencyId] = useState('');
  const [payLoading, setPayLoading] = useState(false);
  const [dialogOpen, setDialogOpen] = useState(dialogProps.open || false);
  const [processedCurrencies, setProcessedCurrencies] = useState<{ [key: string]: number }>({});
  const [paymentStatus, setPaymentStatus] = useState<{ [key: string]: 'success' | 'error' | 'idle' }>({});

  const sourceType = subscriptionId ? 'subscription' : 'customer';
  const effectiveCustomerId = customerId || session?.user?.did;
  const sourceId = subscriptionId || effectiveCustomerId;
  const customerIdRef = useRef(effectiveCustomerId);
  const {
    data = {
      summary: {},
      invoices: [],
    } as OverdueInvoicesResult,
    error,
    loading,
    runAsync: refresh,
  } = useRequest(() => fetchOverdueInvoices({ subscriptionId, customerId: effectiveCustomerId, authToken }), {
    ready: !!subscriptionId || !!effectiveCustomerId,
    onSuccess: (res) => {
      if (res.customer?.id && res.customer?.id !== customerIdRef.current) {
        customerIdRef.current = res.customer?.id;
      }
    },
  });

  const detailUrl = useMemo(() => {
    if (subscriptionId) {
      return joinURL(getPrefix(), `/customer/subscription/${subscriptionId}`);
    }
    if (effectiveCustomerId) {
      return joinURL(getPrefix(), '/customer/invoice/past-due');
    }
    return '';
  }, [subscriptionId, effectiveCustomerId]);

  const summaryList = useMemo(() => {
    if (!data?.summary) {
      return [];
    }
    return Object.values(data.summary);
  }, [data?.summary]);

  const debouncedHandleInvoicePaid = debounce(
    async (currencyId: string) => {
      if (successToast) {
        Toast.close();
        Toast.success(t('payment.customer.invoice.paySuccess'));
      }
      setPayLoading(false);
      const res = await refresh();
      if (res.invoices?.length === 0) {
        setDialogOpen(false);
        onPaid(sourceId as string, currencyId, sourceType as 'subscription' | 'customer');
      }
    },
    1000,
    {
      leading: false,
      trailing: true,
      maxWait: 5000,
    }
  );

  const isCrossOriginRequest = isCrossOrigin();

  const subscription = useSubscription('events');
  const waitForInvoiceAllPaid = async () => {
    let isPaid = false;
    await pWaitFor(
      async () => {
        const res = await refresh();
        isPaid = res.invoices?.length === 0;
        return isPaid;
      },
      { interval: 2000, timeout: 3 * 60 * 1000 }
    );
    return isPaid;
  };

  const handleConnected = async () => {
    if (isCrossOriginRequest) {
      try {
        const paid = await waitForInvoiceAllPaid();
        if (successToast) {
          Toast.close();
          Toast.success(t('payment.customer.invoice.paySuccess'));
        }
        if (paid) {
          setDialogOpen(false);
          onPaid(sourceId as string, selectCurrencyId, sourceType as 'subscription' | 'customer');
        }
      } catch (err) {
        console.error('Check payment status failed:', err);
      }
    }
  };

  useEffect(() => {
    if (subscription && !isCrossOriginRequest) {
      subscription.on('invoice.paid', ({ response }: { response: TInvoiceExpanded }) => {
        const relevantId = subscriptionId || response.customer_id;
        const uniqueKey = `${relevantId}-${response.currency_id}`;

        if (
          (subscriptionId && response.subscription_id === subscriptionId) ||
          (effectiveCustomerId && effectiveCustomerId === response.customer_id) ||
          (customerIdRef.current && customerIdRef.current === response.customer_id)
        ) {
          if (!processedCurrencies[uniqueKey]) {
            setProcessedCurrencies((prev) => ({ ...prev, [uniqueKey]: 1 }));
            debouncedHandleInvoicePaid(response.currency_id);
          }
        }
      });
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [subscription, subscriptionId, effectiveCustomerId]);

  const handlePay = (item: SummaryItem) => {
    const { currency, method } = item;
    if (method.type === 'stripe') {
      Toast.error(t('payment.subscription.overdue.notSupport'));
      return;
    }
    if (payLoading) {
      return;
    }
    setSelectCurrencyId(currency.id);
    setPayLoading(true);
    setPaymentStatus((prev) => ({
      ...prev,
      [currency.id]: 'idle',
    }));

    if (['arcblock', 'ethereum', 'base'].includes(method.type)) {
      const extraParams: any = { currencyId: currency.id };

      if (subscriptionId) {
        extraParams.subscriptionId = subscriptionId;
      } else if (effectiveCustomerId) {
        extraParams.customerId = effectiveCustomerId;
      }

      connect.open({
        locale: locale as 'en' | 'zh',
        containerEl: undefined as unknown as Element,
        saveConnect: false,
        action: 'collect-batch',
        prefix: joinURL(getPrefix(), '/api/did'),
        useSocket: !isCrossOriginRequest,
        extraParams,
        messages: {
          scan: t('common.connect.defaultScan'),
          title: t('payment.customer.invoice.payBatch'),
          confirm: t('common.connect.confirm'),
        } as any,
        onSuccess: () => {
          connect.close();
          handleConnected();
          setPayLoading(false);
          setPaymentStatus((prev) => ({
            ...prev,
            [currency.id]: 'success',
          }));
        },
        onClose: () => {
          connect.close();
          setPayLoading(false);
        },
        onError: (err: any) => {
          Toast.error(formatError(err));
          setPaymentStatus((prev) => ({
            ...prev,
            [currency.id]: 'error',
          }));
          setPayLoading(false);
        },
      });
    }
  };

  const handleClose = () => {
    setDialogOpen(false);
    dialogProps.onClose?.();
  };

  const handleViewDetailClick = (e: React.MouseEvent) => {
    if (detailLinkOptions.onClick) {
      e.preventDefault();
      detailLinkOptions.onClick(e);
    } else if (!detailLinkOptions.enabled) {
      e.preventDefault();
      handleClose();
    }
  };

  if (loading) {
    return null;
  }

  const getDetailLinkText = () => {
    if (detailLinkOptions.title) {
      return detailLinkOptions.title;
    }

    if (subscriptionId) {
      return t('payment.subscription.overdue.view');
    }

    return t('payment.customer.pastDue.view');
  };

  const renderPayButton = (
    item: SummaryItem,
    primaryButton = true,
    props: {
      variant?: 'contained' | 'text';
      sx?: SxProps;
    } = {
      variant: 'contained',
    }
  ) => {
    const { currency } = item;
    const inProcess = payLoading && selectCurrencyId === currency.id;
    const status = paymentStatus[currency.id] || 'idle';

    if (status === 'success') {
      return (
        <Button
          // eslint-disable-next-line react/prop-types
          variant={props?.variant || 'contained'}
          size="small"
          {...(primaryButton
            ? {}
            : {
                color: 'success',
                startIcon: <CheckCircleIcon />,
              })}>
          {t('payment.subscription.overdue.paid')}
        </Button>
      );
    }

    if (status === 'error') {
      return (
        <Button variant="contained" size="small" onClick={() => handlePay(item)} {...props}>
          {t('payment.subscription.overdue.retry')}
        </Button>
      );
    }

    if (item.method.type === 'stripe') {
      return (
        <Button variant="contained" color="primary" onClick={() => window.open(detailUrl, '_blank')} {...props}>
          {t('payment.subscription.overdue.viewNow')}
        </Button>
      );
    }
    return (
      <LoadingButton
        variant="contained"
        size="small"
        disabled={inProcess}
        loading={inProcess}
        onClick={() => handlePay(item)}
        {...props}>
        {t('payment.subscription.overdue.payNow')}
      </LoadingButton>
    );
  };

  const getMethodText = (method: PaymentMethod) => {
    if (method.name && method.type !== 'arcblock') {
      return ` (${method.name})`;
    }
    return '';
  };

  const getOverdueTitle = () => {
    if (subscriptionId && data.subscription) {
      if (summaryList.length === 1) {
        return t('payment.subscription.overdue.title', {
          name: data.subscription?.description,
          count: data.invoices?.length,
          total: formatAmount(summaryList[0]?.amount, summaryList[0]?.currency?.decimal),
          symbol: summaryList[0]?.currency?.symbol,
          method: getMethodText(summaryList[0]?.method),
        });
      }
      return t('payment.subscription.overdue.simpleTitle', {
        name: data.subscription?.description,
        count: data.invoices?.length,
      });
    }
    if (effectiveCustomerId) {
      let title = '';
      if (summaryList.length === 1) {
        title = t('payment.customer.overdue.title', {
          subscriptionCount: data.subscriptionCount || 0,
          count: data.invoices?.length,
          total: formatAmount(summaryList[0]?.amount, summaryList[0]?.currency?.decimal),
          symbol: summaryList[0]?.currency?.symbol,
          method: getMethodText(summaryList[0]?.method),
        });
      } else {
        title = t('payment.customer.overdue.simpleTitle', {
          subscriptionCount: data.subscriptionCount || 0,
          count: data.invoices?.length,
        });
      }
      if (alertMessage) {
        return `${title}${alertMessage}`;
      }
      return `${title}${t('payment.customer.overdue.defaultAlert')}`;
    }

    return '';
  };

  const getEmptyStateMessage = () => {
    if (subscriptionId && data.subscription) {
      return t('payment.subscription.overdue.empty', {
        name: data.subscription?.description,
      });
    }
    return t('payment.customer.overdue.empty');
  };

  if (mode === 'custom' && children && typeof children === 'function') {
    return (
      <Stack>
        {children(handlePay, {
          subscription: data?.subscription,
          summary: data?.summary as { [key: string]: SummaryItem },
          invoices: data?.invoices as Invoice[],
          subscriptionCount: data?.subscriptionCount,
          detailUrl,
        })}
      </Stack>
    );
  }

  return (
    <Dialog
      PaperProps={{
        style: { minHeight: 'auto' },
      }}
      {...(dialogProps || {})}
      open={dialogOpen}
      title={dialogProps?.title || t('payment.subscription.overdue.pastDue')}
      sx={{ '& .MuiDialogContent-root': { pt: 0 } }}
      onClose={handleClose}>
      {error ? (
        <Alert severity="error">{error.message}</Alert>
      ) : (
        <Stack
          sx={{
            gap: 1,
          }}>
          {summaryList.length === 0 && (
            <>
              <Alert severity="success">{getEmptyStateMessage()}</Alert>
              <Stack
                direction="row"
                sx={{
                  justifyContent: 'flex-end',
                  mt: 2,
                }}>
                <Button variant="outlined" color="primary" onClick={handleClose} sx={{ width: 'fit-content' }}>
                  {/* @ts-ignore */}
                  {t('common.know')}
                </Button>
              </Stack>
            </>
          )}
          {summaryList.length === 1 && (
            <>
              <Typography
                variant="body1"
                sx={{
                  color: 'text.secondary',
                }}>
                {getOverdueTitle()}
                {detailLinkOptions.enabled && (
                  <>
                    <br />
                    {t('payment.subscription.overdue.description')}
                    <a
                      href={detailUrl}
                      target="_blank"
                      onClick={handleViewDetailClick}
                      rel="noreferrer"
                      style={{ color: theme.palette.text.link }}>
                      {getDetailLinkText()}
                    </a>
                  </>
                )}
              </Typography>
              <Stack
                direction="row"
                sx={{
                  justifyContent: 'flex-end',
                  gap: 2,
                  mt: 2,
                }}>
                <Button variant="outlined" color="primary" onClick={handleClose}>
                  {/* @ts-ignore */}
                  {t('common.cancel')}
                </Button>
                {/* @ts-ignore */}
                {renderPayButton(summaryList[0])}
              </Stack>
            </>
          )}
          {summaryList.length > 1 && (
            <>
              <Typography
                variant="body1"
                sx={{
                  color: 'text.secondary',
                }}>
                {getOverdueTitle()}
                {detailLinkOptions.enabled && (
                  <>
                    <br />
                    {t('payment.subscription.overdue.description')}
                    <a
                      href={detailUrl}
                      target="_blank"
                      rel="noreferrer"
                      onClick={handleViewDetailClick}
                      style={{ color: theme.palette.text.link }}>
                      {getDetailLinkText()}
                    </a>
                  </>
                )}
              </Typography>
              <Typography
                variant="body1"
                sx={{
                  color: 'text.secondary',
                }}>
                {t('payment.subscription.overdue.list')}
              </Typography>
              <Stack>
                {summaryList.map((item: any) => (
                  <Stack
                    key={item?.currency?.id}
                    direction="row"
                    sx={{
                      justifyContent: 'space-between',
                      alignItems: 'center',
                      py: 1,
                      px: 0.5,
                      borderBottom: '1px solid',
                      borderColor: 'grey.200',

                      '&:hover': {
                        backgroundColor: () => theme.palette.grey[100],
                      },

                      mt: 0,
                    }}>
                    <Typography>
                      {t('payment.subscription.overdue.total', {
                        total: formatAmount(item?.amount, item?.currency?.decimal),
                        currency: item?.currency?.symbol,
                        method: getMethodText(item?.method),
                      })}
                    </Typography>
                    {renderPayButton(item, false, {
                      variant: 'text',
                      sx: {
                        color: 'text.link',
                      },
                    })}
                  </Stack>
                ))}
              </Stack>
            </>
          )}
        </Stack>
      )}
    </Dialog>
  );
}

export default OverdueInvoicePayment;
