import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
import Toast from '@arcblock/ux/lib/Toast';
import { ConfirmDialog, api, formatAmountPrecisionLimit, formatError } from '@blocklet/payment-react';
import type { TPaymentIntentExpanded } from '@blocklet/payment-types';
import { fromUnitToToken } from '@ocap/util';
import { useRequest, useSetState } from 'ahooks';
import { useNavigate } from 'react-router-dom';
import type { LiteralUnion } from 'type-fest';
import {
  FormControl,
  FormControlLabel,
  FormHelperText,
  InputAdornment,
  Radio,
  RadioGroup,
  Stack,
  TextField,
} from '@mui/material';
import { Controller, FormProvider, useForm, useFormContext } from 'react-hook-form';

import { useState } from 'react';
import Actions from '../actions';
import ClickBoundary from '../click-boundary';

/**
 * Format the refundable amount the server returned (raw BN string) into a human-readable
 * token string by **flooring** to `precision` decimal places — never rounding up.
 *
 * `formatBNStr` rounds (via toLocaleString / toFixed), which can produce a value whose
 * `fromTokenToUnit` round-trip exceeds the original raw amount by sub-cent dust and gets
 * rejected by the server with "refund amount exceeds the available amount". Flooring keeps
 * the round-trip ≤ original. The trailing-zero trim mirrors `formatBNStr` so the input
 * field looks identical for amounts that don't get truncated.
 */
function formatRefundableAmount(raw: string | undefined, decimals: number, precision: number = 6): string {
  if (!raw) return '0';
  const tokenAmount = fromUnitToToken(raw, decimals);
  const [whole = '0', frac = ''] = tokenAmount.split('.');
  if (precision <= 0) return whole;
  const truncated = frac.substring(0, precision).replace(/0+$/, '');
  return truncated ? `${whole}.${truncated}` : whole;
}

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

const fetchRefundData = (id: string) => {
  return api.get(`/api/payment-intents/${id}/refundable-amount`).then((res: any) => res.data);
};

function RefundForm({ data, refundMaxAmount }: { data: TPaymentIntentExpanded; refundMaxAmount: string }) {
  const { t, locale } = useLocaleContext();
  const {
    control,
    setValue,
    formState: { errors },
  } = useFormContext();
  const validateAmount = (v: number) => {
    if (Number(v) <= 0 || Number(v) > Number(refundMaxAmount)) {
      return t('admin.paymentIntent.refundForm.amountRange', {
        min: 0,
        max: refundMaxAmount,
        symbol: data.paymentCurrency.symbol,
      });
    }
    const validPrecision = formatAmountPrecisionLimit(
      v.toString(),
      locale,
      data.paymentCurrency.maximum_precision || 6
    );
    if (validPrecision) {
      return validPrecision;
    }
    return true;
  };
  return (
    <Stack
      direction="column"
      spacing={1}
      sx={{
        alignItems: 'flex-start',
        width: 400,
      }}>
      <Controller
        name="refund.reason"
        control={control}
        render={({ field }) => (
          <RadioGroup {...field} sx={{ ml: '5px !important' }}>
            <FormControlLabel
              value="duplicate"
              control={<Radio checked={field.value === 'duplicate'} />}
              label={t('admin.paymentIntent.refundForm.duplicate')}
              onClick={() => setValue('refund.reason', 'duplicate')}
            />
            <FormControlLabel
              value="requested_by_customer"
              control={<Radio checked={field.value === 'requested_by_customer'} />}
              label={t('admin.paymentIntent.refundForm.requestedByCustomer')}
              onClick={() => setValue('refund.reason', 'requested_by_customer')}
            />
            <FormControlLabel
              value="requested_by_admin"
              control={<Radio checked={field.value === 'requested_by_admin'} />}
              label={t('admin.paymentIntent.refundForm.requestedByAdmin')}
              onClick={() => setValue('refund.reason', 'requested_by_admin')}
            />
            <FormControlLabel
              value="fraudulent"
              control={<Radio checked={field.value === 'fraudulent'} />}
              label={t('admin.paymentIntent.refundForm.fraudulent')}
              onClick={() => setValue('refund.reason', 'fraudulent')}
            />
            <FormControlLabel
              value="expired_uncaptured_charge"
              control={<Radio checked={field.value === 'expired_uncaptured_charge'} />}
              label={t('admin.paymentIntent.refundForm.expiredUncapturedCharge')}
              onClick={() => setValue('refund.reason', 'expired_uncaptured_charge')}
            />
          </RadioGroup>
        )}
      />
      <FormControl fullWidth component="fieldset" variant="outlined">
        <Controller
          name="refund.amount"
          control={control}
          rules={{
            validate: validateAmount,
          }}
          render={({ field }) => (
            <TextField
              {...field}
              variant="outlined"
              size="small"
              type="number"
              fullWidth
              placeholder={t('admin.paymentIntent.refundForm.amount')}
              error={!!(errors as any)?.refund?.amount}
              helperText={(errors as any)?.refund?.amount?.message}
              slotProps={{
                input: {
                  endAdornment: <InputAdornment position="end">{data.paymentCurrency.symbol}</InputAdornment>,
                },
              }}
            />
          )}
        />
        <FormHelperText>
          {t('admin.paymentIntent.refundForm.amountHelper', {
            min: 0,
            max: refundMaxAmount,
            symbol: data.paymentCurrency.symbol,
          })}
        </FormHelperText>
      </FormControl>
      <Controller
        name="refund.description"
        control={control}
        rules={{
          required: t('common.required'),
          validate: (value) => value.trim() !== '' || t('common.required'),
        }}
        render={({ field }) => (
          <TextField
            {...field}
            variant="outlined"
            size="small"
            fullWidth
            multiline
            minRows={2}
            maxRows={4}
            placeholder={t('admin.paymentIntent.refundForm.description')}
            error={!!(errors as any)?.refund?.description}
            helperText={(errors as any)?.refund?.description?.message}
            slotProps={{
              htmlInput: {
                maxLength: 200,
              },
            }}
          />
        )}
      />
    </Stack>
  );
}

export function PaymentIntentActionsInner({ data, variant = 'compact', onChange }: Props) {
  const { t } = useLocaleContext();
  const navigate = useNavigate();
  const { reset, getValues, setValue, handleSubmit } = useFormContext();
  const [state, setState] = useSetState({
    action: '',
    loading: false,
  });
  const [refundMaxAmount, setRefundMaxAmount] = useState('0');
  const isSlash = data.payment_details?.arcblock?.type === 'slash' && data.paymentMethod?.type === 'arcblock';
  const { runAsync: runRefundAmountAsync } = useRequest<any, any[]>(
    () => {
      if (isSlash) {
        return Promise.resolve({ amount: '0' });
      }
      return fetchRefundData(data.id);
    },
    {
      onSuccess: (res: any) => {
        const amount = formatRefundableAmount(res?.amount, data.paymentCurrency.decimal);
        setRefundMaxAmount(amount);
      },
      manual: true,
    }
  );

  const onRefund = async () => {
    const { refund } = getValues();
    if (!refund.amount || !refund.reason || !refund.description) {
      Toast.warning(t('admin.paymentIntent.refundForm.required'));
      return;
    }
    try {
      setState({ loading: true });
      await api.put(`/api/payment-intents/${data.id}/refund`, refund).then((res: any) => res.data);
      Toast.success(t('admin.paymentIntent.refundSuccess'));
      onChange('refund');
      runRefundAmountAsync();
    } catch (err) {
      console.error(err);
      Toast.error(formatError(err));
    } finally {
      setState({ loading: false, action: '' });
    }
  };

  const actions = [
    {
      label: t('admin.paymentIntent.refund'),
      handler: () => {
        runRefundAmountAsync().then((res) => {
          reset();
          const curAmount = formatRefundableAmount(res?.amount, data.paymentCurrency.decimal);
          if (Number(curAmount) <= 0) {
            Toast.info(t('admin.paymentIntent.refundForm.empty'));
            return;
          }
          setValue('refund.amount', curAmount);
          setState({ action: 'refund' });
        });
      },
      color: 'primary',
      disabled: Number(refundMaxAmount) <= 0,
      divider: true,
    },
    {
      label: t('admin.customer.view'),
      handler: () => navigate(`/admin/customers/${data.customer_id}`),
      color: 'primary',
      disabled: false,
    },
  ];
  if (variant === 'compact') {
    actions.push({
      label: t('admin.paymentIntent.view'),
      handler: () => navigate(`/admin/payments/${data.id}`),
      color: 'primary',
      disabled: false,
    });
  }

  return (
    <ClickBoundary>
      <Actions variant={variant} actions={actions} onOpenCallback={runRefundAmountAsync} />
      {state.action === 'refund' && (
        <ConfirmDialog
          onConfirm={handleSubmit(onRefund)}
          onCancel={() => setState({ action: '' })}
          title={t('admin.paymentIntent.refund')}
          message={<RefundForm data={data} refundMaxAmount={refundMaxAmount} />}
          loading={state.loading}
        />
      )}
    </ClickBoundary>
  );
}

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

  const methods = useForm({
    mode: 'onChange',
    defaultValues: {
      refund: {
        reason: 'requested_by_admin',
        description: '',
      },
    },
  });
  return (
    <FormProvider {...methods}>
      <PaymentIntentActionsInner {...props} />
    </FormProvider>
  );
}
