import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
import { ConfirmDialog, Switch, api, formatError, usePaymentContext } from '@blocklet/payment-react';
import type { ChainType, TPaymentCurrency, TPaymentMethodExpanded } from '@blocklet/payment-types';
import {
  AddOutlined,
  DeleteOutlined,
  EditOutlined,
  InfoOutlined,
  QrCodeOutlined,
  CheckCircleOutline,
  ErrorOutline,
  OpenInNewOutlined,
} from '@mui/icons-material';
import {
  Alert,
  Avatar,
  Box,
  CircularProgress,
  Divider,
  Grid,
  IconButton,
  List,
  ListItem,
  ListItemAvatar,
  ListItemText,
  Skeleton,
  Stack,
  Tooltip,
  Typography,
  useTheme,
} from '@mui/material';
import { useRequest, useSessionStorageState, useSetState } from 'ahooks';
import useBus from 'use-bus';

import Toast from '@arcblock/ux/lib/Toast';
import { DIDDialog } from '@arcblock/ux/lib/DID';
import { fromUnitToToken } from '@ocap/util';
import { joinURL } from 'ufo';
import IconCollapse from '../../../../components/collapse';
import InfoCard from '../../../../components/info-card';
import InfoRow from '../../../../components/info-row';
import PaymentCurrencyAdd from '../../../../components/payment-currency/add';
import PaymentCurrencyEdit from '../../../../components/payment-currency/edit';
import EditInLine from '../../../../components/edit-in-line';
import { useRpcStatus } from '../../../../hooks/rpc-status';
import PaymentMethodEdit from './edit';

const getMethods = (
  params: Record<string, any> = {}
): Promise<{
  list: TPaymentMethodExpanded[];
  addresses: { arcblock: string; ethereum: string };
  balances: { [currencyId: string]: string };
}> => {
  const search = new URLSearchParams();
  Object.keys(params).forEach((key) => {
    search.set(key, String(params[key]));
  });
  return api.get(`/api/payment-methods?${search.toString()}`).then((res: any) => res.data);
};

const updateApiHost = (id: string, params: Record<string, any>) => {
  return api.put(`/api/payment-methods/${id}/settings`, { arcblock: params }).then((res: any) => res.data);
};

function EditApiHost({ method, refresh }: { method: TPaymentMethodExpanded; refresh: () => void }) {
  const { t } = useLocaleContext();

  const handleSave = async (newValue: string) => {
    await updateApiHost(method.id, { api_host: newValue });
    refresh();
  };

  return (
    <InfoRow
      label={t('admin.paymentMethod.arcblock.api_host.label')}
      value={
        <EditInLine
          value={method.settings?.arcblock?.api_host || ''}
          onSave={handleSave}
          placeholder={t('admin.paymentMethod.arcblock.api_host.tip')}
          required
        />
      }
    />
  );
}

function EditApiExplorerHost({ method, refresh }: { method: TPaymentMethodExpanded; refresh: () => void }) {
  const { t } = useLocaleContext();

  const handleSave = async (newValue: string) => {
    await updateApiHost(method.id, { explorer_host: newValue });
    refresh();
  };

  return (
    <InfoRow
      label={t('admin.paymentMethod.arcblock.explorer_host.label')}
      value={
        <EditInLine
          value={method.settings?.arcblock?.explorer_host || ''}
          onSave={handleSave}
          placeholder={t('admin.paymentMethod.arcblock.explorer_host.tip')}
          required
        />
      }
    />
  );
}

const groupByType = (methods: TPaymentMethodExpanded[]) => {
  const groups: Record<string, TPaymentMethodExpanded[]> = {};
  methods.forEach((x) => {
    groups[x.type] = groups[x.type] || [];
    (groups[x.type] as any).push(x);
  });

  return groups;
};

function RpcStatus({ method }: { method: TPaymentMethodExpanded }) {
  const { t } = useLocaleContext();
  // @ts-ignore
  const apiHost = method.settings?.[method.type]?.api_host;
  const { status } = useRpcStatus(apiHost, { debounce: 0, pollInterval: 2000 });

  const renderStatus = () => {
    if (status.loading) {
      return (
        <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
          <CircularProgress size={14} />
          <Typography
            sx={{
              color: 'text.secondary',
            }}>
            {t('admin.paymentMethod.evm.checking')}
          </Typography>
        </Box>
      );
    }
    if (status.connected) {
      return (
        <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
          <CheckCircleOutline sx={{ fontSize: 14 }} color="success" />
          <Typography
            sx={{
              color: 'text.secondary',
            }}>
            {t('admin.paymentMethod.evm.connected')}
          </Typography>
        </Box>
      );
    }
    return (
      <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
        <ErrorOutline sx={{ fontSize: 14 }} color="error" />
        <Typography color="error">{t('admin.paymentMethod.evm.connectionError')}</Typography>
      </Box>
    );
  };
  return (
    <>
      <InfoRow label={t(`admin.paymentMethod.${method.type}.api_host.label`)} value={apiHost} />
      <InfoRow label={t('admin.paymentMethod.evm.rpcStatus')} value={renderStatus()} />
      <InfoRow label={t('admin.paymentMethod.evm.blockHeight')} value={status.blockNumber?.toLocaleString()} />
    </>
  );
}

function Balance({
  method,
  balances,
  addresses,
  setDidDialog,
  refresh,
}: {
  method: TPaymentMethodExpanded;
  balances: { [currencyId: string]: string };
  addresses: { arcblock: string; ethereum: string };
  setDidDialog: (value: any) => void;
  refresh: () => void;
}) {
  const { t } = useLocaleContext();
  const defaultCurrency = (method.payment_currencies || [])?.find(
    (x) => x.id === method.default_currency_id
  ) as TPaymentCurrency;
  const balance = fromUnitToToken(balances?.[defaultCurrency?.id] || '0', defaultCurrency?.decimal);
  const explorerHost = (method?.settings?.[method?.type as ChainType] as any)?.explorer_host || '';
  const getLink = () => {
    if (method.type === 'arcblock' && addresses?.arcblock) {
      return joinURL(explorerHost, 'accounts', addresses?.arcblock, 'tokens');
    }
    if (['ethereum', 'base'].includes(method.type) && addresses?.ethereum) {
      return joinURL(explorerHost, 'address', addresses?.ethereum);
    }
    return '';
  };
  const insufficientBalance =
    ['ethereum', 'base'].includes(method.type) &&
    balances?.[defaultCurrency?.id] &&
    balances?.[defaultCurrency?.id] === '0';
  const getAddress = (type: string) => {
    if (['ethereum', 'base'].includes(type)) {
      return addresses?.ethereum || '';
    }
    return addresses?.arcblock || '';
  };
  return (
    <>
      {method.type === 'arcblock' ? (
        <EditApiExplorerHost method={method} refresh={refresh} />
      ) : (
        <InfoRow label={t('admin.paymentMethod.props.explorer_host')} value={explorerHost} />
      )}
      <InfoRow
        label={
          <Box
            sx={{
              display: 'flex',
              alignItems: 'center',
              gap: 0.5,
            }}>
            {t('admin.paymentMethod.props.balance')}
            {['ethereum', 'base'].includes(method.type) && (
              <Tooltip
                title={t('admin.paymentMethod.gasTip', {
                  method: method.type,
                  chain: method.name,
                })}>
                <InfoOutlined
                  sx={{ fontSize: 16, cursor: 'pointer', color: insufficientBalance ? 'error.main' : 'text.secondary' }}
                />
              </Tooltip>
            )}
          </Box>
        }
        value={
          <Stack
            direction="row"
            sx={{
              alignItems: 'center',
              display: 'inline-flex',
              borderRadius: 1,
            }}>
            <Typography
              component="a"
              href={getLink()}
              target="_blank"
              rel="noreferrer"
              sx={{
                display: 'flex',
                alignItems: 'center',
                gap: 0.5,
                '&.MuiTypography-root': {
                  color: insufficientBalance ? 'error.main' : 'text.link',
                },
              }}>
              {balance} {defaultCurrency?.symbol}
              <OpenInNewOutlined sx={{ fontSize: 14 }} />
            </Typography>
            <Divider orientation="vertical" flexItem sx={{ mx: 1 }} />
            <Tooltip title={t('admin.paymentMethod.recharge')}>
              <IconButton
                size="small"
                onClick={() =>
                  setDidDialog({
                    open: true,
                    chainId: (method.settings?.[method.type as ChainType] as any)?.chain_id,
                    did: getAddress(method.type),
                  })
                }
                sx={{ p: 0.5 }}>
                <QrCodeOutlined sx={{ fontSize: 16 }} />
              </IconButton>
            </Tooltip>
          </Stack>
        }
      />
    </>
  );
}

function PaymentMethodSkeleton() {
  return (
    <>
      {[1].map((group) => (
        <Box
          key={group}
          sx={{
            mt: 3,
          }}>
          <Stack
            direction="row"
            sx={{
              alignItems: 'center',
              mb: 1,
              flexWrap: 'wrap',
              gap: 1,
            }}>
            <Skeleton variant="text" width={120} height={32} />
          </Stack>
          <Box
            sx={{
              py: 1,
              borderTop: '1px solid',
              borderBottom: '1px solid',
              borderColor: 'divider',
              mb: 1,
            }}>
            <Stack
              direction="row"
              sx={{
                justifyContent: 'space-between',
                alignItems: 'center',
              }}>
              <Stack
                direction="row"
                spacing={2}
                sx={{
                  alignItems: 'center',
                  flex: 1,
                }}>
                <Skeleton variant="rectangular" width={40} height={40} />
                <Box sx={{ flex: 1 }}>
                  <Skeleton variant="text" width="20%" height={24} />
                  <Skeleton variant="text" width="40%" height={20} />
                </Box>
              </Stack>
            </Stack>
          </Box>
        </Box>
      ))}
    </>
  );
}

export default function PaymentMethods() {
  const { t } = useLocaleContext();
  const { palette } = useTheme();
  const [expandedId, setExpandedId] = useSessionStorageState('payment-method-expanded-id', {
    defaultValue: '',
  });
  const {
    loading,
    error,
    data = { list: [], addresses: {}, balances: {} } as any,
    runAsync,
  } = useRequest(() =>
    getMethods({
      addresses: true,
    })
  );
  const handleExpand = (methodId: string, expanded: boolean) => {
    setExpandedId(expanded ? methodId : '');
  };
  const [didDialog, setDidDialog] = useSetState({ open: false, chainId: '', did: '' });
  const [methodDialog, setMethodDialog] = useSetState({ open: false, value: null });
  const { refresh } = usePaymentContext();
  const [currencyDialog, setCurrencyDialog] = useSetState({ action: '', value: null, method: '' });

  const { list: methods, addresses, balances } = data;
  useBus(
    'paymentMethod.created',
    () => {
      runAsync();
      refresh(true);
    },
    []
  );
  useBus(
    'paymentMethod.updated',
    () => {
      runAsync();
      refresh(true);
    },
    []
  );
  useBus(
    'paymentCurrency.added',
    () => {
      runAsync().then(() => {
        setCurrencyDialog({ action: '', method: '' });
      });
      refresh(true);
    },
    []
  );
  useBus(
    'paymentCurrency.updated',
    () => {
      runAsync().then(() => {
        setCurrencyDialog({ action: '', method: '' });
      });
      refresh(true);
    },
    []
  );

  if (error) {
    return <Alert severity="error">{error.message}</Alert>;
  }

  if (loading || !data || methods?.length === 0) {
    return <PaymentMethodSkeleton />;
  }

  const groups = groupByType(methods);

  const handleDeleteCurrency = async (currency: TPaymentCurrency) => {
    try {
      await api.delete(`/api/payment-currencies/${currency.id}`);
      runAsync();
      refresh(true);
      Toast.success(t('admin.paymentCurrency.deleted'));
    } catch (err) {
      Toast.error(formatError(err));
    } finally {
      // @ts-ignore
      setCurrencyDialog({ action: '', method: '' });
    }
  };

  const handleRefresh = () => {
    runAsync();
    refresh(true);
  };

  return (
    <>
      {Object.keys(groups).map((x) => (
        <Box
          key={x}
          sx={{
            mt: 3,
          }}>
          <Stack
            direction="row"
            sx={{
              alignItems: 'center',
              mb: 1,
              flexWrap: 'wrap',
              gap: 1,
            }}>
            <Typography variant="subtitle2" sx={{ textTransform: 'uppercase' }}>
              {x}
            </Typography>
          </Stack>
          {(groups[x] as TPaymentMethodExpanded[]).map((method) => (
            <IconCollapse
              key={method.id}
              trigger={<InfoCard {...method} />}
              addons={
                <>
                  <Switch checked={method.active} disabled sx={{ cursor: 'default' }} />
                  {method.type !== 'arcblock' && (
                    <IconButton
                      onClick={(e) => {
                        e.stopPropagation();
                        setMethodDialog({ open: true, value: method as any });
                      }}>
                      <EditOutlined />
                    </IconButton>
                  )}
                </>
              }
              style={{
                py: 1,
                borderTop: `1px solid ${palette.divider}`,
                borderBottom: `1px solid ${palette.divider}`,
                '& :hover': { color: 'text.primary' },
              }}
              value={method.id}
              expanded={expandedId === method.id}
              onChange={(value, expanded) => handleExpand(value, expanded)}
              lazy={false}>
              <Grid
                container
                spacing={2}
                sx={{
                  mt: 1,
                }}>
                <Grid
                  size={{
                    xs: 12,
                    md: 6,
                  }}>
                  <InfoRow label={t('admin.paymentMethod.props.type')} value={method.type} />
                  {method.type === 'arcblock' && <EditApiHost method={method} refresh={handleRefresh} />}
                  {['ethereum', 'base'].includes(method.type) && <RpcStatus method={method} />}
                  {['arcblock', 'ethereum', 'base'].includes(method.type) && (
                    <Balance
                      method={method}
                      balances={balances}
                      addresses={addresses}
                      setDidDialog={setDidDialog}
                      refresh={handleRefresh}
                    />
                  )}

                  <InfoRow
                    label={t('admin.paymentMethod.props.confirmation')}
                    value={
                      <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
                        <Typography variant="body2">{method.confirmation.type}</Typography>
                        {method.type === 'stripe' && method.confirmation.type === 'callback' && (
                          <Tooltip
                            title={
                              <Typography variant="body2" component="span">
                                {t('admin.paymentMethod.stripe.webhookHint')}{' '}
                                <Typography
                                  variant="body2"
                                  component="a"
                                  href="/.well-known/service/admin/overview/components"
                                  target="_blank"
                                  rel="noopener noreferrer"
                                  sx={{
                                    color: 'primary.light',
                                    textDecoration: 'underline',
                                    '&:hover': {
                                      color: 'primary.main',
                                    },
                                  }}>
                                  {t('admin.paymentMethod.stripe.configureEnv')}
                                </Typography>
                              </Typography>
                            }
                            placement="right"
                            arrow>
                            <InfoOutlined
                              sx={{
                                fontSize: 16,
                                color: 'info.main',
                                cursor: 'help',
                                transition: 'color 0.2s',
                                '&:hover': {
                                  color: 'info.dark',
                                },
                              }}
                            />
                          </Tooltip>
                        )}
                      </Box>
                    }
                  />
                  <InfoRow
                    label={t('admin.paymentMethod.props.recurring')}
                    value={method.features.recurring ? t('common.yes') : t('common.no')}
                  />
                  <InfoRow
                    label={t('admin.paymentMethod.props.refund')}
                    value={method.features.refund ? t('common.yes') : t('common.no')}
                  />
                  <InfoRow
                    label={t('admin.paymentMethod.props.dispute')}
                    value={method.features.dispute ? t('common.yes') : t('common.no')}
                  />
                  <InfoRow
                    alignItems="flex-start"
                    label={t('admin.paymentMethod.props.currencies')}
                    value={
                      <List>
                        {method.payment_currencies.map((currency) => {
                          if (!currency) {
                            return null;
                          }
                          if (currency.type === 'credit') {
                            return null;
                          }
                          return (
                            <ListItem
                              key={currency.id}
                              disablePadding
                              secondaryAction={
                                currency.locked || method.default_currency_id === currency.id ? null : (
                                  <>
                                    <IconButton
                                      edge="end"
                                      onClick={() => {
                                        setCurrencyDialog({
                                          action: 'edit',
                                          // @ts-ignore
                                          value: currency,
                                          method: method.id,
                                        });
                                      }}>
                                      <EditOutlined />
                                    </IconButton>
                                    <IconButton
                                      edge="end"
                                      onClick={() => {
                                        // @ts-ignore
                                        setCurrencyDialog({ action: 'delete', value: currency, method: method.id });
                                      }}>
                                      <DeleteOutlined />
                                    </IconButton>
                                  </>
                                )
                              }>
                              <ListItemAvatar>
                                <Avatar src={currency.logo} alt={currency.symbol} />
                              </ListItemAvatar>
                              <ListItemText primary={currency.name} secondary={currency.description} />
                            </ListItem>
                          );
                        })}
                        {method.type !== 'stripe' && (
                          <ListItem
                            key="add-currency"
                            disablePadding
                            sx={{ cursor: 'pointer' }}
                            onClick={() => {
                              setCurrencyDialog({ action: 'create', method: method.id });
                            }}>
                            <ListItemAvatar>
                              <Avatar>
                                <AddOutlined fontSize="small" />
                              </Avatar>
                            </ListItemAvatar>
                            <ListItemText primary={t('admin.paymentCurrency.add')} />
                          </ListItem>
                        )}
                        {currencyDialog.method === method.id && currencyDialog.action === 'create' && (
                          <PaymentCurrencyAdd
                            method={method}
                            onClose={() => setCurrencyDialog({ action: '', method: '' })}
                          />
                        )}
                        {currencyDialog.method === method.id && currencyDialog.action === 'edit' && (
                          <PaymentCurrencyEdit
                            method={method}
                            onClose={() => setCurrencyDialog({ action: '', method: '' })}
                            value={currencyDialog.value!}
                          />
                        )}
                        {currencyDialog.method === method.id && currencyDialog.action === 'delete' && (
                          <ConfirmDialog
                            onConfirm={() => handleDeleteCurrency(currencyDialog.value!)}
                            onCancel={() => setCurrencyDialog({ action: '', method: '' })}
                            title={t('admin.paymentCurrency.delete')}
                            message={t('admin.paymentCurrency.deleteConfirm')}
                          />
                        )}
                      </List>
                    }
                  />
                </Grid>
              </Grid>
            </IconCollapse>
          ))}
        </Box>
      ))}
      {didDialog.open && didDialog.did && (
        <DIDDialog
          open={didDialog.open}
          onClose={() => setDidDialog({ open: false, chainId: '', did: '' })}
          did={didDialog.did}
          chainId={didDialog.chainId}
        />
      )}
      {methodDialog.open && methodDialog.value && (
        <PaymentMethodEdit value={methodDialog.value} onClose={() => setMethodDialog({ open: false, value: null })} />
      )}
    </>
  );
}
