/*
 This file is part of GNU Taler
 (C) 2022-2024 Taler Systems S.A.

 GNU Taler is free software; you can redistribute it and/or modify it under the
 terms of the GNU General Public License as published by the Free Software
 Foundation; either version 3, or (at your option) any later version.

 GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

 You should have received a copy of the GNU General Public License along with
 GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
import {
  AmountJson,
  Amounts,
  ConversionRate,
  HttpStatusCode,
  TalerConversionInfoConfig,
  TalerError,
  TalerErrorCode,
  assertUnreachable,
  encodeCrock,
  getRandomBytes,
} from "@gnu-taler/taler-util";
import {
  Attention,
  ButtonBetter,
  ErrorLoading,
  Loading,
  LocalNotificationBanner,
  RouteDefinition,
  ShowInputErrorLabel,
  notifyInfo,
  useBankCoreApiContext,
  useChallengeHandler,
  useLocalNotificationBetter,
  useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
import { useEffect, useState } from "preact/hooks";

import {
  Paytos,
  TalerCorebankApi,
  opFixedSuccess,
} from "@gnu-taler/taler-util";
import { useAccountDetails } from "../../hooks/account.js";
import {
  TransCalc,
  TransferCalculation,
  useCashoutEstimatorByUser,
  useConversionInfo,
  useConversionRateForUser,
} from "../../hooks/regional.js";
import { LoggedIn, useSessionState } from "../../hooks/session.js";
import { TanChannel, undefinedIfEmpty } from "../../utils.js";
import { LoginForm } from "../LoginForm.js";
import {
  InputAmount,
  RenderAmount,
  doAutoFocus,
} from "../PaytoWireTransferForm.js";
import { SolveMFAChallenges } from "../SolveMFA.js";

const TALER_SCREEN_ID = 127;

interface Props {
  account: string;
  focus?: boolean;
  onCashout: () => void;
  routeClose: RouteDefinition;
}

type FormType = {
  isDebit: boolean;
  amount: string;
  subject: string;
  channel: TanChannel;
};
type ErrorFrom<T> = {
  [P in keyof T]+?: string;
};

const RANDOM_STRING = encodeCrock(getRandomBytes(32));
export function CreateCashout({
  account: accountName,
  onCashout,
  focus,
  routeClose,
}: Props): VNode {
  const { i18n } = useTranslationContext();
  const { config } = useBankCoreApiContext();
  if (!config.allow_conversion) {
    return (
      <Fragment>
        <Attention type="warning" title={i18n.str`Unable to create a cashout`}>
          <i18n.Translate>
            The bank configuration does not support cashout operations.
          </i18n.Translate>
        </Attention>
        <div class="mt-5 sm:mt-6">
          <a
            href={routeClose.url({})}
            name="close"
            class="inline-flex w-full justify-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
          >
            <i18n.Translate>Close</i18n.Translate>
          </a>
        </div>
      </Fragment>
    );
  }

  const resultAccount = useAccountDetails(accountName);
  const { state: credentials } = useSessionState();
  const creds = credentials.status !== "loggedIn" ? undefined : credentials;
  const rateResp = useConversionRateForUser(accountName, creds?.token);
  const conversionResp = useConversionInfo();

  if (!resultAccount) {
    return <Loading />;
  } else if (resultAccount instanceof TalerError) {
    return <ErrorLoading error={resultAccount} />;
  } else if (resultAccount.type === "fail") {
    switch (resultAccount.case) {
      case HttpStatusCode.Unauthorized:
        return <LoginForm currentUser={accountName} />;
      case HttpStatusCode.NotFound:
        return <LoginForm currentUser={accountName} />;
      default:
        assertUnreachable(resultAccount);
    }
  }

  if (!conversionResp) {
    return <Loading />;
  } else if (conversionResp instanceof TalerError) {
    return <ErrorLoading error={conversionResp} />;
  } else if (conversionResp.type === "fail") {
    switch (conversionResp.case) {
      case HttpStatusCode.NotImplemented: {
        return (
          <Attention type="danger" title={i18n.str`Cashout is disabled`}>
            <i18n.Translate>
              Cashout should be enabled in the configuration, the conversion
              rate should be initialized with fee(s), rates and a rounding mode.
            </i18n.Translate>
          </Attention>
        );
      }
      default:
        assertUnreachable(conversionResp);
    }
  }

  if (!rateResp) {
    return <Loading />;
  } else if (rateResp instanceof TalerError) {
    return <ErrorLoading error={rateResp} />;
  } else if (rateResp.type === "fail") {
    switch (rateResp.case) {
      case HttpStatusCode.NotImplemented: {
        return (
          <Attention type="danger" title={i18n.str`Cashout is disabled`}>
            <i18n.Translate>
              Cashout should be enabled in the configuration, the conversion
              rate should be initialized with fee(s), rates and a rounding mode.
            </i18n.Translate>
          </Attention>
        );
      }
      default:
        assertUnreachable(rateResp);
    }
  }
  const rate = rateResp.body;
  if (!rate) {
    return (
      <div>conversion enabled but server replied without conversion_rate</div>
    );
  }
  if (!creds) {
    return <div>authentication required</div>;
  }

  return (
    <CreateCashoutInternal
      accountData={resultAccount.body}
      onCashout={onCashout}
      routeClose={routeClose}
      focus={focus}
      convConfig={conversionResp.body}
      rate={rateResp.body}
      session={creds}
    />
  );
}

function CreateCashoutInternal({
  onCashout,
  accountData,
  focus,
  routeClose,
  convConfig: {
    fiat_currency,
    fiat_currency_specification,
    regional_currency,
    regional_currency_specification,
  },
  session,
  rate,
}: Omit<Props, "account"> & {
  convConfig: TalerConversionInfoConfig;
  rate: ConversionRate;
  session: LoggedIn;
  accountData: TalerCorebankApi.AccountData;
}): VNode {
  const {
    estimateByCredit: calculateFromCredit,
    estimateByDebit: calculateFromDebit,
  } = useCashoutEstimatorByUser(accountData.name);
  const [form, setForm] = useState<Partial<FormType>>({ isDebit: true });
  const [notification, safeFunctionHandler] = useLocalNotificationBetter();

  const mfa = useChallengeHandler();
  const { i18n } = useTranslationContext();
  const {
    lib: { bank: api },
  } = useBankCoreApiContext();

  const regionalZero = Amounts.zeroOfCurrency(regional_currency);
  const fiatZero = Amounts.zeroOfCurrency(fiat_currency!);

  const account = {
    balance: Amounts.parseOrThrow(accountData.balance.amount),
    balanceIsDebit: accountData.balance.credit_debit_indicator == "debit",
    debitThreshold: Amounts.parseOrThrow(accountData.debit_threshold),
  };

  const balanceLimit = IntAmounts.toIntAmount(
    account.balance,
    account.balanceIsDebit,
  ).increment(account.debitThreshold);

  const zeroCalc = {
    debit: regionalZero,
    credit: fiatZero,
    beforeFee: fiatZero,
  };
  const [calculationResult, setCalculation] =
    useState<TransferCalculation>(zeroCalc);
  const sellFee = Amounts.parseOrThrow(rate.cashout_fee);
  const sellRate = rate.cashout_ratio;
  /**
   * can be in regional currency or fiat currency
   * depending on the isDebit flag
   */
  const inputAmount = Amounts.parseOrThrow(
    `${form.isDebit ? regional_currency : fiat_currency}:${
      !form.amount ? "0" : form.amount
    }`,
  );

  const higerThanMin = form.isDebit
    ? Amounts.cmp(inputAmount, rate.cashout_min_amount) >= 0
    : true;
  const notZero = Amounts.isNonZero(inputAmount);

  const conversionCalculator = safeFunctionHandler(
    async (isDebit: boolean, input: AmountJson, fee: AmountJson) => {
      if (notZero && higerThanMin) {
        return isDebit
          ? calculateFromDebit(input, fee)
          : calculateFromCredit(input, fee);
      } else {
        return opFixedSuccess(zeroCalc);
      }
    },
    [form.isDebit ?? false, inputAmount, sellFee],
  );
  conversionCalculator.onSuccess = (success) => setCalculation(success);
  conversionCalculator.onFail = (fail) => {
    switch (fail.case) {
      case HttpStatusCode.BadRequest:
        return i18n.str`The server didn't understand the request.`;
      case HttpStatusCode.Conflict:
        return i18n.str`The amount is too small`;
      case HttpStatusCode.NotImplemented:
        return i18n.str`Conversion is not implemented.`;
      case TalerErrorCode.GENERIC_PARAMETER_MISSING:
        return i18n.str`At least debit or credit needs to be provided`;
      case TalerErrorCode.GENERIC_PARAMETER_MALFORMED:
        return i18n.str`The amount is malfored`;
      case TalerErrorCode.GENERIC_CURRENCY_MISMATCH:
        return i18n.str`The currency is not supported`;
    }
  };

  useEffect(() => {
    conversionCalculator.call();
  }, [form.amount, form.isDebit, notZero, higerThanMin, rate.cashout_fee]);

  const calc = !calculationResult ? zeroCalc : calculationResult;

  const balanceAfter = IntAmounts.toIntAmount(
    account.balance,
    account.balanceIsDebit,
  ).deduce(calc.debit).result;

  function updateForm(newForm: typeof form): void {
    setForm(newForm);
  }
  const errors = undefinedIfEmpty<ErrorFrom<typeof form>>({
    subject: !form.subject ? i18n.str`Required` : undefined,
    amount: !form.amount
      ? i18n.str`Required`
      : !inputAmount
        ? i18n.str`Invalid`
        : !calculationResult
          ? i18n.str`Amount needs to be higher`
          : Amounts.isZero(
                balanceLimit
                  .deduce(calculationResult.debit)
                  .getResultZeroIfNegative(),
              )
            ? i18n.str`Balance is not enough`
            : Amounts.cmp(calculationResult.debit, rate.cashout_min_amount) < 0
              ? i18n.str`It is not possible to cashout less than ${
                  Amounts.stringifyValueWithSpec(
                    Amounts.parseOrThrow(rate.cashout_min_amount),
                    regional_currency_specification,
                  ).normal
                }: ${Amounts.stringify(calculationResult.debit)}`
              : Amounts.isZero(calculationResult.credit)
                ? i18n.str`The total transfer to the destination will be zero`
                : undefined,
  });
  const trimmedAmountStr = form.amount?.trim();

  const subject = form.subject;

  const cashout = safeFunctionHandler(
    (calc: TransCalc, subject: string, challengeIds: string[]) =>
      api.createCashout(
        session,
        {
          request_uid: RANDOM_STRING,
          amount_credit: Amounts.stringify(calc.credit),
          amount_debit: Amounts.stringify(calc.debit),
          subject,
        },
        { challengeIds },
      ),
    !!errors || !subject ? undefined : [calc, subject, []],
  );
  cashout.onSuccess = (success) => {
    notifyInfo(i18n.str`Cashout created`);
    onCashout();
  };
  cashout.onFail = (fail) => {
    switch (fail.case) {
      case HttpStatusCode.Accepted: {
        mfa.onChallengeRequired(fail.body);
        return i18n.str`Second factor authentication required.`;
      }
      case HttpStatusCode.NotFound:
        return i18n.str`Account not found`;
      case TalerErrorCode.BANK_TRANSFER_REQUEST_UID_REUSED:
        return i18n.str`Duplicated request detected, check if the operation succeeded or try again.`;
      case TalerErrorCode.BANK_BAD_CONVERSION:
        return i18n.str`The conversion rate was applied incorrectly`;
      case TalerErrorCode.BANK_UNALLOWED_DEBIT:
        return i18n.str`The account does not have sufficient funds`;
      case HttpStatusCode.NotImplemented:
        return i18n.str`Cashout is disabled`;
      case TalerErrorCode.BANK_CONFIRM_INCOMPLETE:
        return i18n.str`Missing cashout URI in the profile`;
      case TalerErrorCode.BANK_CONVERSION_AMOUNT_TO_SMALL:
        return i18n.str`The amount is below the minimum amount permitted.`;
      case TalerErrorCode.BANK_TAN_CHANNEL_SCRIPT_FAILED:
        return i18n.str`Sending the confirmation message failed, retry later or contact the administrator.`;
      case TalerErrorCode.BANK_TAN_CHANNEL_NOT_SUPPORTED: {
        return i18n.str`The server doesn't support the current TAN channel.`;
      }
    }
  };

  const retryCashout = cashout.lambda((ids: string[]) => [
    cashout.args![0],
    cashout.args![1],
    ids,
  ]);

  const cashoutDisabled = !accountData.cashout_payto_uri;

  const cashoutAccount = !accountData.cashout_payto_uri
    ? undefined
    : Paytos.fromString(accountData.cashout_payto_uri);
  const cashoutAccountName =
    !cashoutAccount || cashoutAccount.type === "fail"
      ? undefined
      : cashoutAccount.body.displayName;

  const cashoutLegalName =
    !cashoutAccount || cashoutAccount.type === "fail"
      ? undefined
      : cashoutAccount.body.params["receiver-name"];

  if (mfa.pendingChallenge) {
    return (
      <SolveMFAChallenges
        currentChallenge={mfa.pendingChallenge}
        username={accountData.name}
        description={i18n.str`Create cashout.`}
        onCancel={mfa.doCancelChallenge}
        onCompleted={retryCashout}
      />
    );
  }

  return (
    <div>
      <LocalNotificationBanner notification={notification} />

      <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-6 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
        <section class="mt-4 rounded-sm px-4 py-6 p-8 ">
          <h2 id="summary-heading" class="font-medium text-lg">
            <i18n.Translate>Cashout</i18n.Translate>
          </h2>

          <dl class="mt-4 space-y-4">
            <div class="justify-between items-center flex">
              <dt class="text-sm text-gray-600">
                <i18n.Translate>Conversion rate</i18n.Translate>
              </dt>
              <dd class="text-sm text-gray-900">{sellRate}</dd>
            </div>

            <div class="flex items-center justify-between border-t-2 afu pt-4">
              <dt class="flex items-center text-sm text-gray-600">
                <span>
                  <i18n.Translate>Balance</i18n.Translate>
                </span>
              </dt>
              <dd class="text-sm text-gray-900">
                <RenderAmount
                  value={account.balance}
                  negative={account.balanceIsDebit}
                  withSign
                  spec={regional_currency_specification}
                />
              </dd>
            </div>
            <div class="flex items-center justify-between border-t-2 afu pt-4">
              <dt class="flex items-center text-sm text-gray-600">
                <span>
                  <i18n.Translate>Fee</i18n.Translate>
                </span>
              </dt>
              <dd class="text-sm text-gray-900">
                <RenderAmount
                  value={sellFee}
                  negative
                  withSign
                  spec={fiat_currency_specification!}
                />
              </dd>
            </div>
            {cashoutAccountName && cashoutLegalName ? (
              <Fragment>
                <div class="flex items-center justify-between border-t-2 afu pt-4">
                  <dt class="flex items-center text-sm text-gray-600">
                    <span>
                      <i18n.Translate>To account</i18n.Translate>
                    </span>
                  </dt>
                  <dd class="text-sm text-gray-900">{cashoutAccountName}</dd>
                </div>
                <div class="flex items-center justify-between border-t-2 afu pt-4">
                  <dt class="flex items-center text-sm text-gray-600">
                    <span>
                      <i18n.Translate>Legal name</i18n.Translate>
                    </span>
                  </dt>
                  <dd class="text-sm text-gray-900">{cashoutLegalName}</dd>
                </div>
                <p class="mt-2 text-sm text-gray-500">
                  <i18n.Translate>
                    If this name doesn't match the account holder's name, your
                    transaction may fail.
                  </i18n.Translate>
                </p>
              </Fragment>
            ) : (
              <div class="flex items-center justify-between border-t-2 afu pt-4">
                <Attention type="warning" title={i18n.str`Unable to cashout`}>
                  <i18n.Translate>
                    Before being able to cashout to a bank account, you need to
                    complete your profile
                  </i18n.Translate>
                </Attention>
              </div>
            )}
          </dl>
        </section>
        <form
          class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"
          autoCapitalize="none"
          autoCorrect="off"
          onSubmit={(e) => {
            e.preventDefault();
          }}
        >
          <div class="px-4 py-6 sm:p-8">
            <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
              {/* subject */}

              <div class="sm:col-span-5">
                <label
                  class="block text-sm font-medium leading-6 text-gray-900"
                  for="subject"
                >
                  {i18n.str`Transfer subject`}
                  <b class="text-[red]"> *</b>
                </label>
                <div class="mt-2">
                  <input
                    ref={focus ? doAutoFocus : undefined}
                    type="text"
                    class="block w-full rounded-md disabled:bg-gray-200 border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
                    name="subject"
                    id="subject"
                    disabled={cashoutDisabled}
                    data-error={!!errors?.subject && form.subject !== undefined}
                    value={form.subject ?? ""}
                    onChange={(e) => {
                      form.subject = e.currentTarget.value;
                      updateForm(structuredClone(form));
                    }}
                    autocomplete="off"
                  />
                  <ShowInputErrorLabel
                    message={errors?.subject}
                    isDirty={form.subject !== undefined}
                  />
                </div>
              </div>

              <div class="sm:col-span-5">
                <label
                  class="block text-sm font-medium leading-6 text-gray-900"
                  for="subject"
                >
                  {i18n.str`Currency`}
                </label>

                <div class="mt-2">
                  <button
                    type="button"
                    name="set 50"
                    class="               inline-flex p-4 text-sm items-center rounded-l-md bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
                    onClick={(e) => {
                      e.preventDefault();
                      form.isDebit = true;
                      updateForm(structuredClone(form));
                    }}
                  >
                    {form.isDebit ? (
                      <svg
                        class="self-center flex-none h-5 w-5 text-indigo-600"
                        viewBox="0 0 20 20"
                        fill="currentColor"
                        aria-hidden="true"
                      >
                        <path
                          fill-rule="evenodd"
                          d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
                          clip-rule="evenodd"
                        />
                      </svg>
                    ) : (
                      <svg
                        fill="none"
                        viewBox="0 0 24 24"
                        stroke-width="1.5"
                        stroke="currentColor"
                        class="w-5 h-5"
                      >
                        <path d="M15 12H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
                      </svg>
                    )}

                    <i18n.Translate>Send {regional_currency}</i18n.Translate>
                  </button>
                  <button
                    type="button"
                    name="set 25"
                    class=" -ml-px -mr-px inline-flex p-4 text-sm items-center rounded-r-md              bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
                    onClick={(e) => {
                      e.preventDefault();
                      form.isDebit = false;
                      updateForm(structuredClone(form));
                    }}
                  >
                    {!form.isDebit ? (
                      <svg
                        class="self-center flex-none h-5 w-5 text-indigo-600"
                        viewBox="0 0 20 20"
                        fill="currentColor"
                        aria-hidden="true"
                      >
                        <path
                          fill-rule="evenodd"
                          d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"
                          clip-rule="evenodd"
                        />
                      </svg>
                    ) : (
                      <svg
                        fill="none"
                        viewBox="0 0 24 24"
                        stroke-width="1.5"
                        stroke="currentColor"
                        class="w-5 h-5"
                      >
                        <path d="M15 12H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
                      </svg>
                    )}

                    <i18n.Translate>Receive {fiat_currency}</i18n.Translate>
                  </button>
                </div>
              </div>

              {/* amount */}
              <div class="sm:col-span-5">
                <div class="flex justify-between">
                  <label
                    class="block text-sm font-medium leading-6 text-gray-900"
                    for="amount"
                  >
                    {i18n.str`Amount`}
                    <b class="text-[red]"> *</b>
                  </label>
                </div>
                <div class="mt-2">
                  <InputAmount
                    name="amount"
                    left
                    currency={form.isDebit ? regional_currency : fiat_currency!}
                    value={trimmedAmountStr}
                    onChange={
                      cashoutDisabled
                        ? undefined
                        : (value) => {
                            form.amount = value;
                            updateForm(structuredClone(form));
                          }
                    }
                  />
                  <ShowInputErrorLabel
                    message={errors?.amount}
                    isDirty={form.amount !== undefined}
                  />
                </div>
              </div>

              {Amounts.isZero(calc.credit) ? undefined : (
                <div class="sm:col-span-5">
                  <dl class="mt-4 space-y-4">
                    <div class="justify-between items-center flex ">
                      <dt class="text-sm text-gray-600">
                        <i18n.Translate>Total cost</i18n.Translate>
                      </dt>
                      <dd class="text-sm text-gray-900">
                        <RenderAmount
                          value={calc.debit}
                          negative
                          withColor
                          spec={regional_currency_specification}
                        />
                      </dd>
                    </div>

                    <div class="flex items-center justify-between border-t-2 afu pt-4">
                      <dt class="flex items-center text-sm text-gray-600">
                        <span>
                          <i18n.Translate>Balance left</i18n.Translate>
                        </span>
                      </dt>
                      <dd class="text-sm text-gray-900">
                        <RenderAmount
                          value={balanceAfter}
                          negative={balanceAfter.negative}
                          withSign
                          spec={regional_currency_specification}
                        />
                      </dd>
                    </div>
                    {Amounts.isZero(sellFee) ||
                    Amounts.isZero(calc.beforeFee) ? undefined : (
                      <div class="flex items-center justify-between border-t-2 afu pt-4">
                        <dt class="flex items-center text-sm text-gray-600">
                          <span>
                            <i18n.Translate>Before fee</i18n.Translate>
                          </span>
                        </dt>
                        <dd class="text-sm text-gray-900">
                          <RenderAmount
                            value={calc.beforeFee}
                            spec={fiat_currency_specification!}
                          />
                        </dd>
                      </div>
                    )}
                    <div class="flex justify-between items-center border-t-2 afu pt-4">
                      <dt class="text-lg text-gray-900 font-medium">
                        <i18n.Translate>Total cashout transfer</i18n.Translate>
                      </dt>
                      <dd class="text-lg text-gray-900 font-medium">
                        <RenderAmount
                          value={calc.credit}
                          withColor
                          spec={fiat_currency_specification!}
                        />
                      </dd>
                    </div>
                  </dl>
                </div>
              )}
            </div>
          </div>

          <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
            <a
              href={routeClose.url({})}
              name="cancel"
              type="button"
              class="text-sm font-semibold leading-6 text-gray-900"
            >
              <i18n.Translate>Cancel</i18n.Translate>
            </a>
            <ButtonBetter
              type="submit"
              name="cashout"
              class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
              onClick={cashout}
            >
              <i18n.Translate>Cashout</i18n.Translate>
            </ButtonBetter>
          </div>
        </form>
      </div>
    </div>
  );
}
export interface IntAmountJson extends AmountJson {
  negative: boolean;
  saturated: boolean;
}

/**
 * Helper to manipulate amounts that can be negative.
 *
 */
export class IntAmounts {
  readonly result: IntAmountJson;
  private constructor(
    value: AmountJson,
    negative: boolean = false,
    saturated: boolean = false,
  ) {
    this.result = {
      ...value,
      negative,
      saturated,
    };
  }

  static from(value: IntAmountJson): IntAmounts {
    return new IntAmounts(value, value.negative, value.saturated);
  }

  static toIntAmount(value: AmountJson, negative: boolean = false): IntAmounts {
    return new IntAmounts(value, negative);
  }

  getResultZeroIfNegative(): IntAmountJson {
    return this.result.negative
      ? IntAmounts.toIntAmount(Amounts.zeroOfCurrency(this.result.currency))
          .result
      : this.result;
  }
  /**
   * Sum this value to a positive or negative amount.
   *
   * @param d
   * @returns
   */
  merge(d: IntAmountJson): IntAmounts {
    if (d.negative) {
      return this.deduce(d);
    } else {
      return this.increment(d);
    }
  }
  /**
   * Deduce the absolute value by the amount
   *
   * @param am
   * @returns
   */
  deduce(am: AmountJson): IntAmounts {
    if (this.result.negative) {
      const { amount, saturated } = Amounts.add(this.result, am);
      return IntAmounts.from({
        ...amount,
        saturated,
        negative: true,
      });
    } else {
      const negative = Amounts.cmp(this.result, am) < 0;
      const { amount, saturated } = negative
        ? Amounts.sub(am, this.result)
        : Amounts.sub(this.result, am);
      return IntAmounts.from({
        ...amount,
        negative,
        saturated,
      });
    }
  }
  /**
   * Increment the value by the amount
   *
   * @param am
   * @returns
   */
  increment(am: AmountJson): IntAmounts {
    if (this.result.negative) {
      const negative = Amounts.cmp(this.result, am) > 0;
      const { amount, saturated } = negative
        ? Amounts.sub(this.result, am)
        : Amounts.sub(am, this.result);
      return IntAmounts.from({
        ...amount,
        negative,
        saturated,
      });
    } else {
      const { amount, saturated } = Amounts.add(this.result, am);
      return IntAmounts.from({
        ...amount,
        saturated,
        negative: false,
      });
    }
  }
}
