/* eslint-disable react/no-unstable-nested-components */
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
import Toast from '@arcblock/ux/lib/Toast';
import {
  ConfirmDialog,
  api,
  formatError,
  getPrefix,
  getSubscriptionAction,
  usePaymentContext,
  OverdueInvoicePayment,
  formatBNStr,
  ResumeSubscription,
} from '@blocklet/payment-react';
import type { TSubscriptionExpanded } from '@blocklet/payment-types';
import { Button, Link, Stack, Tooltip, Typography, Box, Alert } from '@mui/material';
import { useRequest, useSetState } from 'ahooks';
import isEmpty from 'lodash/isEmpty';
import { useEffect, useState, ReactNode } from 'react';
import { FormProvider, useForm, useFormContext } from 'react-hook-form';
import { useNavigate } from 'react-router-dom';
import { joinURL } from 'ufo';
import { BN } from '@ocap/util';
import DID from '@arcblock/ux/lib/DID';
import { AddOutlined } from '@mui/icons-material';
import CustomerCancelForm from './cancel';
import OverdraftProtectionDialog from '../../customer/overdraft-protection';
import Actions from '../../actions';
import { useUnpaidInvoicesCheckForSubscription } from '../../../hooks/subscription';
import { isActive, isWillCanceled } from '../../../libs/util';

interface ActionConfig {
  key: string;
  show: boolean;
  label: string | ReactNode | (() => ReactNode);
  labelText?: string;
  tooltip?: string;
  onClick: (e?: React.MouseEvent) => void;
  variant?: 'text' | 'outlined' | 'contained';
  color?: 'inherit' | 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error';
  sx?: any;
  primary?: boolean;
  component?: any;
  href?: string;
  target?: string;
  divider?: boolean;
}

type ActionProps = {
  [key: string]: {
    color?: 'inherit' | 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error';
    variant?: 'text' | 'outlined' | 'contained';
    sx?: {
      [key: string]: any;
    };
    text?: string;
  };
};
export interface ProtectionInitValues {
  enabled?: boolean;
  return_stake?: boolean;
}
export interface ActionMethods {
  openOverdraftProtection: (initValues?: ProtectionInitValues) => void;
}

export type ActionDisplayMode = 'all-buttons' | 'primary-buttons' | 'menu-only';

type Props = {
  subscription: TSubscriptionExpanded;
  showExtra?: boolean;
  showRecharge?: boolean;
  showBalanceInfo?: boolean;
  showOverdraftProtection?:
    | boolean
    | {
        show: boolean;
        onChange: () => any | Promise<any>;
      };
  showDelegation?: boolean;
  showUnsubscribe?: boolean;
  onChange?: (action?: string) => any | Promise<any>;
  actionProps?: ActionProps;
  mode?: ActionDisplayMode;
  setUp?: (methods: ActionMethods) => void;
  includeActions?: string[] | null;
  excludeActions?: string[] | null;
  forceShowDetailAction?: boolean;
  buttonSize?: 'small' | 'medium' | 'large';
};

const fetchChangePlan = async (id: string): Promise<boolean> => {
  try {
    const res = await api.get(`/api/subscriptions/${id}/change-plan`);
    return !!res.data;
  } catch {
    return false;
  }
};

const fetchBatchPay = async (id: string): Promise<string> => {
  try {
    const res = await api.get(`/api/subscriptions/${id}/summary`);
    if (!isEmpty(res.data) && Object.keys(res.data).length === 1) {
      return Object.keys(res.data)[0] as string;
    }
    return '';
  } catch {
    return '';
  }
};

const supportRecharge = (subscription: TSubscriptionExpanded) => {
  return (
    ['active', 'trialing', 'past_due'].includes(subscription?.status) &&
    ['arcblock', 'ethereum', 'base'].includes(subscription?.paymentMethod?.type) &&
    subscription.paymentCurrency?.type !== 'credit'
  );
};

export function SubscriptionActionsInner({
  subscription,
  showExtra = false,
  showRecharge = false,
  showOverdraftProtection = false,
  showDelegation = false,
  showUnsubscribe = true,
  onChange = undefined,
  actionProps = {},
  mode = 'all-buttons',
  setUp = undefined,
  showBalanceInfo = false,
  includeActions = null,
  excludeActions = null,
  forceShowDetailAction = false,
  buttonSize = 'small',
}: Props) {
  const { t, locale } = useLocaleContext();
  const { reset, getValues } = useFormContext();
  const navigate = useNavigate();
  const { connect, session } = usePaymentContext();
  const [subs, setSubscription] = useState(subscription);
  const { checkUnpaidInvoices } = useUnpaidInvoicesCheckForSubscription(subscription.id, true);
  const action = getSubscriptionAction(subs, actionProps ?? {});

  const showAction = (actionName: string) => {
    if (excludeActions && excludeActions.length > 0) {
      return !excludeActions.includes(actionName);
    }
    if (includeActions && includeActions.length > 0) {
      return includeActions.includes(actionName);
    }
    return true;
  };

  const isScheduledCreditPrice = (price: any) => {
    const schedule = price?.metadata?.credit_config?.schedule;
    return schedule?.enabled && schedule?.delivery_mode === 'schedule';
  };

  const hasScheduledCredit = (subscription.items || []).some((item: any) => isScheduledCreditPrice(item.price));

  const { data: changePlanAvailable = false } = useRequest(() => fetchChangePlan(subscription.id), {
    ready: !!showExtra && isActive(subscription) && !hasScheduledCredit,
  });

  const { data: batchPayAvailable = '' } = useRequest(() => fetchBatchPay(subscription.id), {
    ready: !!showExtra,
  });

  const { data: upcoming = {} } = useRequest<any, any[]>(
    () => api.get(`/api/subscriptions/${subscription.id}/upcoming`).then((res: any) => res.data),
    {
      ready: showAction('recharge') && !!(showRecharge && supportRecharge(subscription)) && showBalanceInfo,
    }
  );

  const { data: payerValue = {} } = useRequest<any, any[]>(
    () => api.get(`/api/subscriptions/${subscription.id}/payer-token`).then((res: any) => res.data),
    {
      ready: showAction('recharge') && !!(showRecharge && supportRecharge(subscription)) && showBalanceInfo,
    }
  );

  const isInsufficientBalance =
    upcoming?.amount && payerValue?.token && new BN(payerValue.token || '0').lt(new BN(upcoming.amount || '0'));

  const [state, setState] = useSetState({
    action: '',
    subscription: '',
    loading: false,
    openProtection: false,
    protectionLoading: false,
    protectionInitValues: null,
    batchPay: false,
  });

  const shouldFetchDelegation = showDelegation && ['active', 'trialing', 'past_due'].includes(subscription?.status);
  const shouldFetchOverdraftProtection =
    ((typeof showOverdraftProtection === 'boolean' && showOverdraftProtection) ||
      (typeof showOverdraftProtection === 'object' && showOverdraftProtection.show)) &&
    subscription?.paymentMethod?.type === 'arcblock' &&
    ['active', 'trialing', 'past_due'].includes(subscription?.status) &&
    showAction('protection');

  const { data: delegation = { sufficient: true }, refresh: refreshDelegation } = useRequest<any, any[]>(
    () => api.get(`/api/subscriptions/${subscription.id}/delegation`).then((res: any) => res.data),
    {
      ready: showAction('delegation') && shouldFetchDelegation,
      refreshDeps: [subscription.id, shouldFetchDelegation],
    }
  );
  const noDelegation = delegation && typeof delegation === 'object' && !delegation.sufficient;

  const { data: overdraftProtection = { enabled: false, remaining: '0' }, refresh: refreshOverdraftProtection } =
    useRequest<any, any[]>(
      () => api.get(`/api/subscriptions/${subscription.id}/overdraft-protection`).then((res: any) => res.data),
      {
        ready: shouldFetchOverdraftProtection,
        refreshDeps: [subscription.id, shouldFetchOverdraftProtection],
      }
    );

  useEffect(() => {
    if (setUp) {
      setUp({
        openOverdraftProtection: (initValues?: ProtectionInitValues) =>
          setState({
            openProtection: true,
            // @ts-ignore
            protectionInitValues: initValues,
          }),
      });
    }
  }, [setUp]);

  const handleCancel = async () => {
    try {
      const result = await checkUnpaidInvoices();
      if (result) {
        setState({ action: '' });
        return;
      }
      setState({ loading: true });
      const sub = await api
        .put(`/api/subscriptions/${state.subscription}/cancel`, {
          at: 'current_period_end',
          ...getValues().cancel,
          cancel_from: 'customer',
        })
        .then((res: any) => res.data);
      setSubscription(sub);
      Toast.success(t('common.saved'));
      if (onChange) onChange(state.action);
    } catch (err) {
      console.error(err);
      Toast.error(formatError(err));
    } finally {
      setState({ loading: false, action: '', subscription: '' });
      reset();
    }
  };

  const handleDelegate = () => {
    connect.open({
      containerEl: undefined as unknown as Element,
      saveConnect: false,
      action: 'delegation',
      locale: locale as 'en' | 'zh',
      prefix: joinURL(getPrefix(), '/api/did'),
      extraParams: { subscriptionId: subscription.id, sessionUserDid: session.user.did },
      messages: {
        scan: t('common.connect.defaultScan'),
        title: t('customer.delegation.title'),
        confirm: t('common.connect.confirm'),
      } as any,
      onSuccess: () => {
        connect.close();
        Toast.success(t('customer.delegation.success'));
        refreshDelegation();
      },
      onClose: () => {
        connect.close();
      },
      onError: (err: any) => {
        Toast.error(formatError(err));
      },
    });
  };

  const handleOverdraftProtection = async (formData: any) => {
    try {
      if (!formData.enabled) {
        const result = await checkUnpaidInvoices();
        if (result) {
          setState({ openProtection: false });
          return;
        }
      }
      setState({ protectionLoading: true });
      const result = await api
        .post(`/api/subscriptions/${subscription.id}/overdraft-protection`, formData)
        .then((res: any) => res.data);

      const { open, amount } = result;

      if (formData.return_stake && overdraftProtection.remaining !== '0') {
        Toast.success(t('customer.overdraftProtection.applyRemainingSuccess'));
        setState({ openProtection: false });
        refreshOverdraftProtection();
        // @ts-ignore
        showOverdraftProtection?.onChange?.();
        onChange?.();
        return;
      }

      if (open) {
        connect.open({
          containerEl: undefined as unknown as Element,
          saveConnect: false,
          locale: locale as 'en' | 'zh',
          action: 'overdraft-protection',
          prefix: joinURL(getPrefix(), '/api/did'),
          extraParams: { subscriptionId: subscription.id, amount, sessionUserDid: session.user.did },
          messages: {
            scan: t('common.connect.defaultScan'),
            title: t('customer.overdraftProtection.title'),
            confirm: t('common.connect.confirm'),
          } as any,
          onSuccess: () => {
            connect.close();
            if (!subscription.overdraft_protection?.enabled) {
              Toast.success(t('customer.overdraftProtection.openSuccess'));
            } else {
              Toast.success(t('customer.overdraftProtection.settingSuccess'));
            }
            setState({ openProtection: false });
            refreshOverdraftProtection();
            // @ts-ignore
            showOverdraftProtection?.onChange?.();
            onChange?.();
          },
          onClose: () => {
            connect.close();
            setState({ protectionLoading: false });
          },
        });
      } else {
        Toast.success(t('customer.overdraftProtection.closeSuccess'));
        setState({ openProtection: false });
        refreshOverdraftProtection();
        // @ts-ignore
        showOverdraftProtection?.onChange?.();
        onChange?.();
      }
    } catch (err) {
      Toast.error(formatError(err) || t('customer.overdraftProtection.settingError'));
      throw err;
    } finally {
      setState({ protectionLoading: false });
    }
  };

  const renderActions = () => {
    const supportUnsubscribe = action?.action === 'cancel' && showUnsubscribe;
    const supportAction = action && (action?.action !== 'cancel' || supportUnsubscribe);
    const supportResume = isWillCanceled(subscription) && action?.action === 'recover';
    const serviceActions = subscription.service_actions?.filter((x: any) => x?.type !== 'notification') || [];
    const actionConfigs: ActionConfig[] = [
      {
        key: 'delegation',
        show: showDelegation && noDelegation,
        label: t('customer.delegation.btn'),
        tooltip: t('customer.delegation.title'),
        onClick: handleDelegate,
        variant: actionProps?.delegation?.variant || 'outlined',
        color: actionProps?.delegation?.color || 'primary',
        primary: true,
      },
      {
        key: 'protection',
        show: shouldFetchOverdraftProtection,
        label: t('customer.overdraftProtection.setting'),
        onClick: () => setState({ openProtection: true, protectionInitValues: null }),
        variant: actionProps?.protection?.variant || 'outlined',
        color: actionProps?.protection?.color || 'primary',
      },
      {
        key: 'recharge',
        show: !!(showRecharge && supportRecharge(subscription)),
        label: () => {
          const balanceDisplay = (
            <Stack
              direction="row"
              spacing={0.5}
              sx={{
                alignItems: 'center',
              }}>
              <AddOutlined fontSize="small" />
              {t('customer.recharge.title')}
            </Stack>
          );

          if (showBalanceInfo && subscription.paymentCurrency) {
            const formattedBalance = formatBNStr(payerValue?.token || '0', subscription.paymentCurrency.decimal);
            const formattedUpcoming = formatBNStr(upcoming?.amount || '0', subscription.paymentCurrency.decimal);

            return (
              <Tooltip
                title={
                  <Box>
                    {isInsufficientBalance && (
                      <Alert severity="error" sx={{ py: 0, mb: 1 }}>
                        {t('admin.subscription.insufficientBalance')}
                      </Alert>
                    )}
                    <Stack spacing={0.5}>
                      <Box
                        sx={{
                          display: 'flex',
                          justifyContent: 'space-between',
                        }}>
                        <Typography sx={{ color: 'text.secondary' }} variant="body2">
                          {t('admin.subscription.currentBalance')}
                        </Typography>
                        <Typography variant="body2">
                          {formattedBalance} {subscription.paymentCurrency.symbol}
                        </Typography>
                      </Box>
                      <Box
                        sx={{
                          display: 'flex',
                          justifyContent: 'space-between',
                        }}>
                        <Typography sx={{ color: 'text.secondary' }} variant="body2">
                          {t('admin.subscription.nextInvoiceAmount')}
                        </Typography>
                        <Typography variant="body2">
                          {formattedUpcoming} {subscription.paymentCurrency.symbol}
                        </Typography>
                      </Box>
                      <Box
                        sx={{
                          display: 'flex',
                          justifyContent: 'space-between',
                        }}>
                        <Typography sx={{ color: 'text.secondary' }} variant="body2">
                          {t('admin.subscription.paymentAddress')}
                        </Typography>
                        <DID did={payerValue?.paymentAddress} responsive={false} compact showAvatar={false} />
                      </Box>
                    </Stack>
                  </Box>
                }
                placement="top"
                slotProps={{
                  tooltip: {
                    sx: {
                      bgcolor: 'background.paper',
                      color: 'text.primary',
                      boxShadow: 2,
                      padding: '10px 16px',
                      maxWidth: 480,
                      minWidth: 350,
                      wordBreak: 'break-word',
                    },
                  },
                }}>
                {balanceDisplay}
              </Tooltip>
            );
          }

          return balanceDisplay;
        },
        labelText: t('customer.recharge.title'),
        onClick: (e) => {
          e?.stopPropagation();
          navigate(`/customer/subscription/${subscription.id}/recharge`);
        },
        variant: actionProps?.recharge?.variant || 'outlined',
        color: actionProps?.recharge?.color || 'primary',
        primary: !isWillCanceled(subscription),
        sx:
          isInsufficientBalance || payerValue.token === '0'
            ? {
                '&::before': {
                  content: '""',
                  backgroundColor: 'error.main',
                  borderRadius: '50%',
                  position: 'absolute',
                  top: '3px',
                  right: '-3px',
                  width: '6px',
                  height: '6px',
                  zIndex: 1,
                },
                ...(actionProps?.recharge?.sx || {}),
              }
            : actionProps?.recharge?.sx || {},
      },
      {
        key: 'changePlan',
        show: changePlanAvailable && !hasScheduledCredit,
        label: action?.text || t('payment.customer.changePlan.button'),
        onClick: (e) => {
          e?.stopPropagation();
          navigate(`/customer/subscription/${subscription.id}/change-plan`);
        },
        variant: actionProps?.changePlan?.variant || 'contained',
        color: actionProps?.changePlan?.color || 'primary',
        sx: actionProps?.changePlan?.sx,
      },
      {
        key: 'batchPay',
        show: !!batchPayAvailable,
        label: action?.text || t('admin.subscription.batchPay.button'),
        onClick: (e) => {
          e?.stopPropagation();
          setState({
            batchPay: true,
          });
        },
        variant: actionProps?.batchPay?.variant || 'outlined',
        color: actionProps?.batchPay?.color || 'error',
        sx: actionProps?.batchPay?.sx,
        primary: true,
      },
      {
        key: 'mainAction',
        show: !!(!batchPayAvailable && supportAction),
        label: action?.text || t(`payment.customer.${action?.action}.button`),
        onClick: (e) => {
          e?.stopPropagation();
          if (action?.action === 'pastDue') {
            navigate(`/customer/invoice/past-due?subscription=${subscription.id}`);
          } else if (action?.action === 'recover' && !(subscription as any).paymentMethodDetails) {
            Toast.error(t('admin.subscription.bindPaymentMethodFirst'));
          } else {
            // @ts-ignore
            setState({ action: action?.action, subscription: subscription.id });
          }
        },
        // @ts-ignore
        variant: action?.variant || 'outlined',
        // @ts-ignore
        color: action?.color || 'primary',
        sx: action?.sx,
        divider: serviceActions.length > 0,
        primary: supportResume,
      },
      // @ts-ignore
      ...serviceActions.map((x) => ({
        key: x.name,
        show: true,
        label: x.text[locale] || x.text.en || x.name,
        onClick: () => window.open(x.link, '_blank'),
        variant: x?.variant || 'contained',
        color: x?.color || 'primary',
        component: Link,
        href: x.link,
        target: '_blank',
        sx: { textDecoration: 'none !important' },
      })),
    ];

    // 过滤出要显示的操作
    const visibleActions = actionConfigs.filter((a) => a.show && showAction(a.key));

    // 转换为菜单项
    const toMenuItem = (item: any) => ({
      label: item.labelText || (typeof item.label === 'function' ? item.label() : item.label),
      handler: item.onClick,
      color: item.color,
      divider: item.divider,
    });
    const detailAction = forceShowDetailAction && (
      <Button
        className="action-button"
        key="detail"
        sx={actionProps?.detail?.sx || {}}
        variant={actionProps?.detail?.variant || 'outlined'}
        color={actionProps?.detail?.color || 'primary'}
        onClick={() => navigate(`/customer/subscription/${subscription.id}`)}>
        {t('customer.subscription.manage')}
      </Button>
    );

    const toButton = (item: ActionConfig) => {
      const labelContent = typeof item.label === 'function' ? item.label() : item.label;

      return (
        <Button
          key={item.key}
          variant={item.variant}
          color={item.color}
          onClick={item.onClick}
          component={item.component}
          href={item.href}
          target={item.target}
          sx={item.sx}
          className="action-button"
          size={buttonSize}>
          {item.tooltip ? (
            <Tooltip title={item.tooltip}>
              <span>{labelContent}</span>
            </Tooltip>
          ) : (
            labelContent
          )}
        </Button>
      );
    };
    if (mode === 'menu-only') {
      return (
        <>
          {detailAction}
          {visibleActions.length > 0 && (
            <Actions actions={visibleActions.map(toMenuItem)} variant="outlined" sx={actionProps?.menu?.sx || {}} />
          )}
        </>
      );
    }

    if (mode === 'primary-buttons') {
      const primaryButtons = visibleActions.filter((a) => a.primary);
      const menuItems = visibleActions.filter((a) => !a.primary);
      return (
        <>
          {detailAction}
          {primaryButtons.map(toButton)}
          {menuItems.length > 0 && (
            <Actions actions={menuItems.map(toMenuItem)} variant="outlined" sx={actionProps?.menu?.sx || {}} />
          )}
        </>
      );
    }

    return (
      <>
        {detailAction}
        {visibleActions.map(toButton)}
      </>
    );
  };

  return (
    <Stack
      direction="row"
      sx={{
        alignItems: 'center',
        gap: 1,
        flexWrap: 'wrap',
      }}>
      {renderActions()}
      {state.action === 'cancel' && state.subscription && (
        <ConfirmDialog
          onConfirm={handleCancel}
          onCancel={(e) => {
            e.stopPropagation();
            setState({ action: '', subscription: '' });
          }}
          title={t('payment.customer.cancel.title')}
          message={<CustomerCancelForm data={subscription} />}
          loading={state.loading}
        />
      )}
      {state.action === 'recover' && state.subscription && (
        <ResumeSubscription
          subscriptionId={state.subscription}
          onResumed={(updatedSubscription) => {
            setState({ action: '', subscription: '' });
            onChange?.(state.action);
            setSubscription(updatedSubscription as TSubscriptionExpanded);
          }}
          dialogProps={{
            open: true,
            onClose: () => setState({ action: '', subscription: '' }),
          }}
        />
      )}
      {state.openProtection && (
        <OverdraftProtectionDialog
          value={overdraftProtection}
          onSave={handleOverdraftProtection}
          open={state.openProtection}
          payerAddress={subscription.overdraft_protection?.payment_details?.arcblock?.payer}
          stakingAddress={subscription.overdraft_protection?.payment_details?.arcblock?.staking?.address}
          currency={subscription.paymentCurrency}
          subscription={subscription}
          loading={state.protectionLoading}
          onCancel={() => setState({ openProtection: false })}
          initValues={state.protectionInitValues}
        />
      )}
      {state.batchPay && (
        <OverdueInvoicePayment
          subscriptionId={subscription.id}
          onPaid={() => {
            setState({ batchPay: false });
            onChange?.('batch-pay');
          }}
          detailLinkOptions={{ enabled: false }}
          dialogProps={{
            open: state.batchPay,
            onClose: () => setState({ batchPay: false }),
          }}
        />
      )}
    </Stack>
  );
}

export default function SubscriptionActions(rawProps: Props) {
  const props: Props = Object.assign(
    {
      showExtra: false,
      showRecharge: false,
      showBalanceInfo: false,
      showOverdraftProtection: false,
      showDelegation: false,
      onChange: null,
      actionProps: {},
      mode: 'all-buttons',
      setUp: null,
      showUnsubscribe: true,
      includeActions: null,
      excludeActions: null,
      forceShowDetailAction: false,
      buttonSize: 'small',
    },
    rawProps
  );
  const methods = useForm({
    defaultValues: {
      cancel: {
        at: 'current_period_end',
        feedback: 'unused',
        comment: '',
      },
    },
  });

  return (
    <FormProvider {...methods}>
      <SubscriptionActionsInner {...props} />
    </FormProvider>
  );
}
