/* eslint-disable react/no-unused-prop-types */
/* eslint-disable react/require-default-props */
/* eslint-disable @typescript-eslint/indent */
import Dialog from '@arcblock/ux/lib/Dialog';
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
import type {
  DonationSettings,
  PaymentBeneficiary,
  PaymentDetails,
  TCheckoutSessionExpanded,
  TPaymentCurrency,
  TPaymentLink,
  TPaymentMethod,
  TSetting,
} from '@blocklet/payment-types';
import {
  Avatar,
  AvatarGroup,
  Box,
  Button,
  CircularProgress,
  IconButton,
  Popover,
  Stack,
  Typography,
  Tooltip,
  type ButtonProps as MUIButtonProps,
} from '@mui/material';
import { useRequest, useSetState } from 'ahooks';
import omit from 'lodash/omit';
import uniqBy from 'lodash/uniqBy';
import { useEffect, useRef, useState } from 'react';
import { Settings } from '@mui/icons-material';

import api from '../libs/api';
import {
  formatAmount,
  formatBNStr,
  getCustomerAvatar,
  getTxLink,
  getUserProfileLink,
  lazyLoad,
  openDonationSettings,
} from '../libs/util';
import type { CheckoutProps, PaymentThemeOptions } from '../types';
import CheckoutForm from './form';
import { PaymentThemeProvider } from '../theme';
import { usePaymentContext } from '../contexts/payment';
import Livemode from '../components/livemode';
import { useMobile } from '../hooks/mobile';
import { useDonateContext } from '../contexts/donate';

export type DonateHistory = {
  supporters: TCheckoutSessionExpanded[];
  currency: TPaymentCurrency;
  method: TPaymentMethod;
  total?: number;
  totalAmount: string;
};

export type CheckoutDonateSettings = {
  target: string;
  title: string;
  description: string;
  reference: string;
  beneficiaries: PaymentBeneficiary[];
  amount?: {
    presets?: string[];
    preset?: string;
    minimum?: string;
    maximum?: string;
    custom?: boolean;
  };
  appearance?: {
    button?: {
      text?: any;
      icon?: any;
      size?: string;
      color?: string;
      variant?: string;
    };
    history?: {
      variant?: string;
    };
  };
};
export interface ButtonType extends Omit<MUIButtonProps, 'text' | 'icon'> {
  text?: string | React.ReactNode;
  icon: React.ReactNode;
}

export type DonateProps = Pick<CheckoutProps, 'onPaid' | 'onError'> & {
  settings: CheckoutDonateSettings;
  livemode?: boolean;
  timeout?: number;
  mode?: 'inline' | 'default' | 'custom';
  inlineOptions?: {
    button?: ButtonType;
  };
  theme?: 'default' | 'inherit' | PaymentThemeOptions;
  children?: (
    openDialog: () => void,
    donateTotalAmount: string,
    supporters: DonateHistory,
    loading?: boolean,
    donateSettings?: DonationSettings
  ) => React.ReactNode;
};

const donationCache: { [key: string]: Promise<TPaymentLink> } = {};
const createOrUpdateDonation = (settings: DonationSettings, livemode: boolean = true): Promise<TPaymentLink> => {
  const donationKey = `${settings.target}-${livemode}`;
  if (!donationCache[donationKey]) {
    donationCache[donationKey] = api
      .post(`/api/donations?livemode=${livemode}`, omit(settings, ['appearance']))
      .then((res: any) => res?.data)
      .finally(() => {
        setTimeout(() => {
          delete donationCache[donationKey];
        }, 3000);
      });
  }

  return donationCache[donationKey];
};

const supporterCache: { [key: string]: Promise<DonateHistory> } = {};
const fetchSupporters = (target: string, livemode: boolean = true): Promise<DonateHistory> => {
  if (!supporterCache[target]) {
    supporterCache[target] = api
      .get('/api/donations', { params: { target, livemode } })
      .then((res: any) => res?.data)
      .finally(() => {
        setTimeout(() => {
          delete supporterCache[target];
        }, 3000);
      });
  }

  return supporterCache[target];
};

const emojiFont = {
  fontFamily:
    'Avenir, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"',
};

export function DonateDetails({ supporters = [], currency, method }: DonateHistory) {
  const { locale } = useLocaleContext();
  return (
    <Stack
      className="cko-donate-details"
      sx={{
        width: '100%',
        minWidth: '256px',
        maxWidth: 'calc(100vw - 32px)',
        maxHeight: '300px',
        overflowX: 'hidden',
        overflowY: 'auto',
        margin: '0 auto',
      }}>
      {supporters.map((x) => (
        <Box
          key={x.id}
          sx={{
            padding: '6px',
            '&:hover': {
              backgroundColor: (theme) => theme.palette.grey[100],
              transition: 'background-color 200ms linear',
              cursor: 'pointer',
            },
            borderBottom: '1px solid',
            borderColor: 'grey.100',
            display: 'flex',
            justifyContent: 'space-between',
            alignItems: 'center',
          }}
          onClick={() => {
            const { link, text } = getTxLink(method, x.payment_details as PaymentDetails);
            if (link && text) {
              window.open(link, '_blank');
            }
          }}>
          <Stack
            direction="row"
            spacing={0.5}
            sx={{
              alignItems: 'center',
              flex: 3,
              overflow: 'hidden',
            }}>
            <Avatar
              key={x.id}
              src={getCustomerAvatar(x.customer?.did, x?.updated_at ? new Date(x.updated_at).toISOString() : '', 20)}
              alt={x.customer?.metadata?.anonymous ? '' : x.customer?.name}
              variant="circular"
              sx={{ width: 20, height: 20 }}
              onClick={(e) => {
                e.stopPropagation();
                if (x.customer?.metadata?.anonymous) {
                  return;
                }
                if (x.customer?.did) {
                  window.open(getUserProfileLink(x.customer?.did, locale), '_blank');
                }
              }}
            />
            <Typography
              sx={{
                ml: '8px !important',
                fontSize: '0.875rem',
                fontWeight: '500',
                overflow: 'hidden',
                whiteSpace: 'nowrap',
                textOverflow: 'ellipsis',
              }}
              onClick={(e) => {
                if (x.customer?.metadata?.anonymous) {
                  return;
                }
                e.stopPropagation();
                if (x.customer?.did) {
                  window.open(getUserProfileLink(x.customer?.did, locale), '_blank');
                }
              }}>
              {x.customer?.name}
            </Typography>
          </Stack>
          <Stack
            direction="row"
            spacing={0.5}
            sx={{
              alignItems: 'center',
              justifyContent: 'flex-end',
              flex: 1,
            }}>
            <Avatar
              key={x.id}
              src={currency?.logo}
              alt={currency?.symbol}
              variant="circular"
              sx={{ width: 16, height: 16 }}
            />
            <Typography sx={{ color: 'text.secondary' }}>{formatBNStr(x.amount_total, currency.decimal)}</Typography>
            <Typography sx={{ color: 'text.secondary' }}>{currency.symbol}</Typography>
          </Stack>
        </Box>
      ))}
    </Stack>
  );
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
function SupporterAvatar({
  supporters = [],
  totalAmount = '0',
  currency,
  method,
  showDonateDetails = false,
}: DonateHistory & { showDonateDetails?: boolean }) {
  const [open, setOpen] = useState(false);
  const customers = uniqBy(supporters, 'customer_id');
  const customersNum = customers.length;
  if (customersNum === 0) return null;
  return (
    <Stack
      sx={{
        flexDirection: 'row',
        justifyContent: 'center',
        alignItems: 'center',
      }}>
      <AvatarGroup
        max={5}
        sx={{
          '& .MuiAvatar-root': {
            backgroundColor: 'background.paper',
            '&.MuiAvatar-colorDefault': {
              backgroundColor: '#bdbdbd',
            },
          },
        }}>
        {customers.slice(0, 5).map((supporter) => (
          <Avatar
            src={getCustomerAvatar(
              supporter.customer?.did,
              supporter?.updated_at ? new Date(supporter.updated_at).toISOString() : '',
              24
            )}
            alt={supporter.customer?.metadata?.anonymous ? '' : supporter.customer?.name}
            key={supporter.customer?.id}
            sx={{
              width: '24px',
              height: '24px',
            }}
          />
        ))}
      </AvatarGroup>
      <Box
        sx={{
          fontSize: '14px',
          color: 'text.secondary',
          pl: 1.5,
          pr: 1,
          ml: -1,
          borderRadius: '8px',
          backgroundColor: 'grey.100',
          height: '24px',
          ...(showDonateDetails
            ? {
                cursor: 'pointer',
                '&:hover': {
                  backgroundColor: 'grey.200',
                },
              }
            : {}),
        }}
        onClick={() => showDonateDetails && setOpen(true)}>
        {`${customersNum} supporter${customersNum > 1 ? 's' : ''} (${formatAmount(totalAmount || '0', currency?.decimal)} ${currency.symbol})`}
      </Box>
      <Dialog
        open={open}
        onClose={() => setOpen(false)}
        sx={{
          '.MuiDialogContent-root': {
            width: {
              xs: '100%',
              md: '450px',
            },
            padding: '8px',
          },
          '.cko-donate-details': {
            maxHeight: {
              xs: '100%',
              md: '300px',
            },
          },
        }}
        title={`${customersNum} supporter${customersNum > 1 ? 's' : ''}`}>
        <DonateDetails supporters={supporters} currency={currency} method={method} totalAmount={totalAmount} />
      </Dialog>
    </Stack>
  );
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
function SupporterTable({ supporters = [], totalAmount = '0', currency, method }: DonateHistory) {
  const customers = uniqBy(supporters, 'customer_id');
  const customersNum = customers.length;
  if (customersNum === 0) return null;
  return (
    <Box
      sx={{
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        gap: { xs: 0.5, sm: 1 },
      }}>
      <SupporterAvatar supporters={supporters} totalAmount={totalAmount} currency={currency} method={method} />
      <DonateDetails supporters={supporters} totalAmount={totalAmount} currency={currency} method={method} />
    </Box>
  );
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
function SupporterSimple({ supporters = [], totalAmount = '0', currency, method }: DonateHistory) {
  const { t } = useLocaleContext();
  const customers = uniqBy(supporters, 'customer_id');
  const customersNum = customers.length;

  return (
    <Box
      sx={{
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',

        gap: {
          xs: 0.5,
          sm: 1,
        },
      }}>
      <Typography
        component="p"
        sx={{
          color: 'text.secondary',
        }}>
        {customersNum === 0 ? (
          <span style={emojiFont}>{t('payment.checkout.donation.empty')}</span>
        ) : (
          t('payment.checkout.donation.summary', {
            total: customersNum,
            symbol: currency.symbol,
            totalAmount: formatAmount(totalAmount || '0', currency.decimal),
          })
        )}
      </Typography>
      <AvatarGroup
        total={customersNum}
        max={10}
        spacing={4}
        sx={{
          '& .MuiAvatar-root': {
            width: 24,
            height: 24,
            fontSize: '0.6rem',
          },
        }}>
        {customers.map((x) => (
          <Avatar
            key={x.id}
            title={x.customer?.name}
            alt={x.customer?.metadata?.anonymous ? '' : x.customer?.name}
            src={getCustomerAvatar(x.customer?.did, x?.updated_at ? new Date(x.updated_at).toISOString() : '', 48)}
            variant="circular"
          />
        ))}
      </AvatarGroup>
    </Box>
  );
}

const defaultDonateAmount = {
  presets: ['1', '5', '10'],
  preset: '1',
  minimum: '0.01',
  maximum: '100',
  custom: true,
};
function useDonation(settings: CheckoutDonateSettings, livemode: boolean, mode = 'default') {
  const [state, setState] = useSetState({
    open: false,
    supporterLoaded: false,
    exist: false,
  });
  const donateContext = useDonateContext();
  const { isMobile } = useMobile();
  const { settings: donateConfig = {} as TSetting } = donateContext || {};
  const donateSettings = {
    ...settings,
    amount: settings.amount || donateConfig?.settings?.amount || defaultDonateAmount,
    appearance: {
      button: {
        ...(settings?.appearance?.button || {}),
        text: settings?.appearance?.button?.text || donateConfig?.settings?.btnText || 'Donate',
        icon: settings?.appearance?.button?.icon || donateConfig?.settings?.icon || null,
      },
      history: {
        variant: settings?.appearance?.history?.variant || donateConfig?.settings?.historyType || 'avatar',
      },
    },
  };

  const hasRequestedRef = useRef(false);
  const containerRef = useRef<HTMLDivElement>(null);
  const donation = useRequest(() => createOrUpdateDonation(donateSettings, livemode), {
    manual: true,
    loadingDelay: 300,
  });
  const supporters = useRequest(
    () => (donation.data ? fetchSupporters(donation.data.id, livemode) : Promise.resolve({})),
    {
      manual: true,
      loadingDelay: 300,
    }
  );
  const rootMargin = isMobile
    ? '50px' // 移动端
    : `${Math.min(window.innerHeight / 2, 300)}px`;

  useEffect(() => {
    if (mode === 'inline') return;
    const element = containerRef.current;
    if (!element) return;
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting && !hasRequestedRef.current) {
          hasRequestedRef.current = true;
          lazyLoad(() => {
            donation.run();
            supporters.run();
          });
        }
      },
      { threshold: 0, rootMargin }
    );

    observer.observe(element);
    // eslint-disable-next-line consistent-return
    return () => observer.unobserve(element);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [mode]);

  useEffect(() => {
    if (donation.data && state.supporterLoaded === false) {
      setState({ supporterLoaded: true });
      supporters.runAsync().catch(console.error);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [donation.data]);

  return {
    containerRef,
    donation,
    supporters,
    state,
    setState,
    donateSettings,
    supportUpdateSettings: !!donateContext.settings,
  };
}

function CheckoutDonateInner({
  settings,
  livemode = true,
  timeout,
  onPaid,
  onError,
  mode,
  inlineOptions = {},
  theme,
  children,
}: DonateProps) {
  // eslint-disable-line
  const { containerRef, state, setState, donation, supporters, donateSettings, supportUpdateSettings } = useDonation(
    settings,
    livemode,
    mode
  );
  const customers = uniqBy((supporters?.data as DonateHistory)?.supporters || [], 'customer_did');
  const { t } = useLocaleContext();
  const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
  const [popoverOpen, setPopoverOpen] = useState<boolean>(false);
  const { isMobile } = useMobile();
  const { connect, session } = usePaymentContext();

  const handlePaid = (...args: any[]) => {
    if (onPaid) {
      // @ts-ignore
      onPaid(...args);
    }

    supporters.runAsync().catch(console.error);

    setTimeout(() => {
      setState({ open: false });
    }, timeout);
  };

  if (donation.error) {
    return null;
  }

  const handlePopoverOpen = (event: any) => {
    donation.run();
    supporters.run();
    setAnchorEl(event.currentTarget);
    setPopoverOpen(true);
  };

  const handlePopoverClose = () => {
    setPopoverOpen(false);
  };

  const startDonate = () => {
    setState({ open: true });
  };

  const inlineText = inlineOptions?.button?.text || donateSettings.appearance.button.text;

  const inlineRender = (
    <>
      <Button
        size={(donateSettings.appearance?.button?.size || 'medium') as any}
        color={(donateSettings.appearance?.button?.color || 'primary') as any}
        variant={(donateSettings.appearance?.button?.variant || 'contained') as any}
        {...donateSettings.appearance?.button}
        onClick={handlePopoverOpen}>
        <Stack
          direction="row"
          spacing={0.5}
          sx={{
            alignItems: 'center',
          }}>
          {donateSettings.appearance.button.icon}
          {typeof donateSettings.appearance.button.text === 'string' ? (
            <Typography sx={{ whiteSpace: 'nowrap' }}>{donateSettings.appearance.button.text}</Typography>
          ) : (
            donateSettings.appearance.button.text
          )}
        </Stack>
      </Button>
      <Popover
        id="mouse-over-popper"
        open={popoverOpen}
        anchorEl={anchorEl}
        onClose={handlePopoverClose}
        anchorOrigin={{
          vertical: 'top',
          horizontal: 'center',
        }}
        transformOrigin={{
          vertical: 'bottom',
          horizontal: 'center',
        }}>
        <Box
          sx={{
            minWidth: 320,
            padding: '20px',
          }}>
          {supporters.loading && (
            <div
              style={{
                position: 'absolute',
                top: 0,
                left: 0,
                right: 0,
                bottom: 0,
                display: 'flex',
                justifyContent: 'center',
                alignItems: 'center',
                backgroundColor: 'background.paper',
                opacity: 0.8,
              }}>
              <CircularProgress />
            </div>
          )}
          <Box
            sx={{
              display: 'flex',
              alignItems: 'center',
              flexDirection: 'column',
              gap: 2,
            }}>
            <Button {...inlineOptions.button} onClick={() => startDonate()}>
              <Stack
                direction="row"
                spacing={0.5}
                sx={{
                  alignItems: 'center',
                }}>
                {inlineOptions?.button?.icon}
                {typeof inlineText === 'string' ? (
                  <Typography sx={{ whiteSpace: 'nowrap' }}>{inlineText}</Typography>
                ) : (
                  inlineText
                )}
              </Stack>
            </Button>
            <SupporterSimple {...(supporters.data as DonateHistory)} />
          </Box>
        </Box>
      </Popover>
    </>
  );
  const defaultRender = (
    <Box
      sx={{
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        gap: { xs: 1, sm: 2 },
        width: '100%',
        minWidth: 300,
        maxWidth: 720,
      }}>
      <Button
        size={(donateSettings.appearance?.button?.size || 'medium') as any}
        color={(donateSettings.appearance?.button?.color || 'primary') as any}
        variant={(donateSettings.appearance?.button?.variant || 'outlined') as any}
        sx={{
          ...(!donateSettings.appearance?.button?.variant
            ? {
                color: 'primary.main',
                borderColor: 'grey.200',
              }
            : {}),
          // @ts-ignore
          ...(donateSettings.appearance?.button?.sx || {}),
        }}
        {...donateSettings.appearance?.button}
        onClick={() => startDonate()}>
        <Stack
          direction="row"
          spacing={0.5}
          sx={{
            alignItems: 'center',
          }}>
          {donateSettings.appearance.button.icon && (
            <Typography sx={{ mr: 0.5, display: 'inline-flex', alignItems: 'center' }}>
              {donateSettings.appearance.button.icon}
            </Typography>
          )}
          {typeof donateSettings.appearance.button.text === 'string' ? (
            <Typography>{donateSettings.appearance.button.text}</Typography>
          ) : (
            donateSettings.appearance.button.text
          )}
        </Stack>
      </Button>
      {supporters.data && donateSettings.appearance.history.variant === 'avatar' && (
        <SupporterAvatar {...(supporters.data as DonateHistory)} showDonateDetails />
      )}
      {supporters.data && donateSettings.appearance.history.variant === 'table' && (
        <SupporterTable {...(supporters.data as DonateHistory)} />
      )}
    </Box>
  );
  const renderInnerView = () => {
    if (mode === 'inline') {
      return inlineRender;
    }
    if (mode === 'custom') {
      return children && typeof children === 'function' ? (
        <>
          {children(
            startDonate,
            `${formatAmount(
              (supporters.data as DonateHistory)?.totalAmount || '0',
              (supporters.data as DonateHistory)?.currency?.decimal
            )} ${(supporters.data as DonateHistory)?.currency?.symbol || ''}`,
            (supporters.data as DonateHistory) || {},
            !!supporters.loading,
            donateSettings
          )}
        </>
      ) : (
        <Typography>
          Please provide a valid render function{' '}
          <pre>{'(openDonate, donateTotalAmount, supporters, loading, donateSettings) => ReactNode'}</pre>
        </Typography>
      );
    }
    return defaultRender;
  };

  const isAdmin = ['owner', 'admin'].includes(session?.user?.role);

  return (
    <div ref={containerRef}>
      {renderInnerView()}
      {donation.data && (
        <Dialog
          open={state.open}
          title={
            <Box
              sx={{
                display: 'flex',
                alignItems: 'center',
                gap: 0.5,
              }}>
              <Typography variant="h3" sx={{ maxWidth: 320, textOverflow: 'ellipsis', overflow: 'hidden' }}>
                {donateSettings.title}
              </Typography>
              {supportUpdateSettings && isAdmin && (
                <Tooltip title={t('payment.checkout.donation.configTip')} placement="bottom">
                  <IconButton
                    size="small"
                    onClick={(e) => {
                      e.stopPropagation();
                      openDonationSettings(true);
                    }}>
                    <Settings fontSize="small" sx={{ ml: -0.5 }} />
                  </IconButton>
                </Tooltip>
              )}
              {!donation.data.livemode && <Livemode sx={{ width: 'fit-content', ml: 0.5 }} />}
            </Box>
          }
          maxWidth="md"
          toolbar={
            isMobile ? null : (
              <Box
                sx={{
                  display: 'flex',
                  alignItems: 'center',
                  gap: 1,
                  color: 'text.secondary',
                }}>
                <AvatarGroup
                  total={customers?.length}
                  max={5}
                  spacing={4}
                  sx={{
                    '& .MuiAvatar-root': {
                      width: 18,
                      height: 18,
                      fontSize: '0.6rem',
                    },
                  }}>
                  {customers.map((x: any) => (
                    <Avatar
                      key={x.id}
                      title={x.customer?.name}
                      src={getCustomerAvatar(
                        x.customer?.did,
                        x?.updated_at ? new Date(x.updated_at).toISOString() : '',
                        24
                      )}
                      variant="circular"
                    />
                  ))}
                </AvatarGroup>
                {customers?.length > 0 && (
                  <Typography variant="body2">
                    {t('payment.checkout.donation.gaveTips', { count: customers?.length })}
                  </Typography>
                )}
              </Box>
            )
          }
          showCloseButton={false}
          disableEscapeKeyDown
          sx={{
            '.MuiDialogContent-root': {
              padding: '16px 24px',
              borderTop: '1px solid',
              borderColor: 'grey.100',
              width: '100%',
            },
            '.ux-dialog_header': {
              gap: 5,
            },
          }}
          PaperProps={{
            style: { minHeight: 'auto', width: 680 },
          }}
          onClose={(e: any, reason: string) => setState({ open: reason === 'backdropClick' })}>
          <Box sx={{ height: '100%', width: '100%' }}>
            <CheckoutForm
              id={donation.data?.id}
              onPaid={handlePaid}
              onError={onError}
              action={donateSettings.appearance?.button?.text}
              mode="inline"
              theme={theme}
              formType="donation"
              extraParams={{
                livemode,
              }}
              formRender={{
                cancel: (
                  <Button
                    variant="outlined"
                    size="large"
                    onClick={() => {
                      connect.close();
                      setState({ open: false });
                    }}>
                    {t('common.cancel')}
                  </Button>
                ),
                onCancel: () => {
                  connect.close();
                  setState({ open: false });
                },
              }}
            />
          </Box>
        </Dialog>
      )}
    </div>
  );
}

export default function CheckoutDonate(rawProps: DonateProps) {
  const props = Object.assign(
    {
      theme: 'default',
      livemode: undefined,
      inlineOptions: {
        button: {
          text: 'Tip',
        },
      },
      timeout: 5000,
      mode: 'default',
    },
    rawProps
  );

  const { livemode } = usePaymentContext();
  const content = (
    // eslint-disable-next-line react/prop-types
    <CheckoutDonateInner {...props} livemode={props.livemode === undefined ? livemode : props.livemode} />
  );
  // eslint-disable-next-line react/prop-types
  if (props.theme === 'inherit') {
    return content;
  }
  // eslint-disable-next-line react/prop-types
  if (props.theme && typeof props.theme === 'object') {
    // eslint-disable-next-line react/prop-types
    return <PaymentThemeProvider theme={props.theme}>{content}</PaymentThemeProvider>;
  }
  return <PaymentThemeProvider>{content}</PaymentThemeProvider>;
}
