import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
import Toast from '@arcblock/ux/lib/Toast';
import { ConfirmDialog, api, formatBNStr, formatError } from '@blocklet/payment-react';
import type { TSubscriptionExpanded } from '@blocklet/payment-types';
import { useRequest, useSetState } from 'ahooks';
import { FormProvider, useForm, useFormContext } from 'react-hook-form';
import { useNavigate } from 'react-router-dom';
import type { LiteralUnion } from 'type-fest';

import { useMemo } from 'react';
import Actions from '../../actions';
import ClickBoundary from '../../click-boundary';
import SubscriptionCancelForm from './cancel';
import SubscriptionPauseForm from './pause';
import SlashStakeForm from './slash-stake';

type Props = {
  data: TSubscriptionExpanded;
  variant?: LiteralUnion<'compact' | 'normal', string>;
  onChange: (action: string) => void;
};

const fetchStakingData = (id: string, time: string): Promise<{ return_amount: string; slash_amount: string }> => {
  return api.get(`/api/subscriptions/${id}/staking?time=${encodeURIComponent(time)}`).then((res: any) => res.data);
};

function SubscriptionActionsInner({ data, variant = 'compact', onChange }: Props) {
  const { t } = useLocaleContext();
  const navigate = useNavigate();
  const { reset, getValues, setError, handleSubmit } = useFormContext();
  const [state, setState] = useSetState({
    action: '',
    loading: false,
  });

  const {
    data: stakeResult = {
      return_amount: '0',
      total: '0',
      slash_amount: '0',
    },
    runAsync: fetchStakeResultAsync,
  } = useRequest(
    () => {
      if (data.paymentMethod?.type === 'arcblock') {
        return fetchStakingData(data.id, '');
      }
      return Promise.resolve({ return_amount: '0', slash_amount: '0', total: '0' });
    },
    {
      manual: true,
    }
  );

  const stakeValue = useMemo(() => {
    return formatBNStr(stakeResult?.slash_amount, data?.paymentCurrency?.decimal);
  }, [stakeResult]);

  const createHandler = (action: string) => {
    return async () => {
      const values = getValues();
      if (action === 'cancel') {
        if (values.cancel.at === 'custom' && !values.cancel.time) {
          setError('cancel.time', { type: 'required', message: t('admin.subscription.cancel.required') });
          return;
        }
      }
      if (action === 'pause') {
        if (values.pause.type === 'custom' && !values.pause.resumesAt) {
          setError('pause.resumesAt', { type: 'required', message: t('admin.subscription.pause.required') });
          return;
        }
      }

      try {
        setState({ loading: true });
        const key = action === 'slash-stake' ? 'slashStake' : action;
        await api.put(`/api/subscriptions/${data.id}/${action}`, values[key] || {}).then((res: any) => res.data);
        Toast.success(t('common.saved'));
        onChange(state.action);
      } catch (err) {
        console.error(err);
        Toast.error(formatError(err));
      } finally {
        setState({ loading: false, action: '' });
      }
    };
  };

  const handleCancel = () => {
    setState({ action: '' });
    reset();
  };

  const resume = {
    label: t('admin.subscription.resume'),
    handler: () => setState({ action: 'resume' }),
    disabled: data.status === 'canceled',
    color: 'primary',
  };
  const pause = {
    label: t('admin.subscription.pause.title'),
    handler: () => setState({ action: 'pause' }),
    disabled: data.status === 'canceled',
    color: 'primary',
  };

  const showSlashStake = data.status === 'canceled' && Number(stakeValue) > 0;

  const actions = [
    {
      label: t('admin.subscription.update'),
      handler: () => setState({ action: 'update' }),
      color: 'primary',
      disabled: data.status === 'canceled',
    },
    data.status === 'paused' || data.pause_collection ? resume : pause,
    {
      label: t('admin.subscription.cancel.title'),
      handler: () => setState({ action: 'cancel' }),
      disabled: data.status === 'canceled',
      color: 'error',
      divider: !showSlashStake,
    },
    ...(showSlashStake
      ? [
          {
            label: t('admin.subscription.cancel.staking.slashTitle'),
            handler: () => {
              setState({ action: 'slashStake' });
            },
            color: 'error',
            divider: true,
          },
        ]
      : []),
    {
      label: t('admin.customer.view'),
      handler: () => navigate(`/admin/customers/${data.customer_id}`),
      color: 'primary',
    },
  ];

  if (variant === 'compact') {
    actions.push({
      label: t('admin.subscription.view'),
      handler: () => navigate(`/admin/billing/${data.id}`),
      color: 'primary',
    });
  }

  return (
    <ClickBoundary>
      <Actions variant={variant} actions={actions} onOpenCallback={fetchStakeResultAsync} />
      {state.action === 'cancel' && (
        <ConfirmDialog
          onConfirm={handleSubmit(createHandler('cancel'))}
          onCancel={handleCancel}
          title={t('admin.subscription.cancel.title')}
          message={<SubscriptionCancelForm data={data} />}
          loading={state.loading}
        />
      )}
      {state.action === 'pause' && (
        <ConfirmDialog
          onConfirm={createHandler('pause')}
          onCancel={handleCancel}
          title={t('admin.subscription.pause.title')}
          message={<SubscriptionPauseForm />}
          loading={state.loading}
        />
      )}
      {state.action === 'resume' && (
        <ConfirmDialog
          onConfirm={createHandler('resume')}
          onCancel={handleCancel}
          title={t('admin.subscription.resume')}
          message={t('admin.subscription.resumeTip')}
          loading={state.loading}
          color="primary"
        />
      )}
      {state.action === 'slashStake' && (
        <ConfirmDialog
          onConfirm={handleSubmit(createHandler('slash-stake'))}
          onCancel={handleCancel}
          title={t('admin.subscription.cancel.staking.slashTitle')}
          message={<SlashStakeForm data={data} stakeValue={stakeValue} />}
          loading={state.loading}
        />
      )}
    </ClickBoundary>
  );
}

export default function SubscriptionActions(rawProps: Props) {
  const props = Object.assign(
    {
      variant: 'compact',
    },
    rawProps
  );

  const methods = useForm({
    defaultValues: {
      cancel: {
        at: 'now',
        time: '',
        refund: 'none',
        staking: 'none',
        slashReason: '',
        comment: '',
      },
      pause: {
        type: 'never',
        resumesAt: '',
        behavior: 'void',
      },
      slashStake: {
        slashReason: '',
      },
    },
  });

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