/*
 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,
  HttpStatusCode,
  TalerBankConversionApi,
  TalerError,
  TranslatedString,
  assertUnreachable
} from "@gnu-taler/taler-util";
import {
  Attention,
  InternationalizationAPI,
  LocalNotificationBanner,
  ShowInputErrorLabel,
  useLocalNotification,
  useTranslationContext,
  utils
} from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
import { useEffect, useState } from "preact/hooks";
import { useBankCoreApiContext } from "../context/config.js";
import { useBackendState } from "../hooks/backend.js";
import { TransferCalculation, useCashinEstimator, useCashoutEstimator, useConversionInfo } from "../hooks/circuit.js";
import { RouteDefinition } from "../route.js";
import { undefinedIfEmpty } from "../utils.js";
import { InputAmount, RenderAmount } from "./PaytoWireTransferForm.js";
import { ProfileNavigation } from "./ProfileNavigation.js";

interface Props {
  routeMyAccountDetails: RouteDefinition;
  routeMyAccountDelete: RouteDefinition;
  routeMyAccountPassword: RouteDefinition;
  routeMyAccountCashout: RouteDefinition;
  routeConversionConfig: RouteDefinition;
  routeCancel: RouteDefinition;
  onUpdateSuccess: () => void;
}

type UIField = {
  value: string | undefined;
  onUpdate: (s: string) => void;
  error: TranslatedString | undefined;
}

type FormHandler<T> = {
  [k in keyof T]?:
  T[k] extends string ? UIField :
  T[k] extends AmountJson ? UIField :
  FormHandler<T[k]>;
}

type FormValues<T> = {
  [k in keyof T]:
  T[k] extends string ? (string | undefined) :
  T[k] extends AmountJson ? (string | undefined) :
  FormValues<T[k]>;
}

type RecursivePartial<T> = {
  [k in keyof T]?:
  T[k] extends string ? (string) :
  T[k] extends AmountJson ? (AmountJson) :
  RecursivePartial<T[k]>;
}

type FormErrors<T> = {
  [k in keyof T]?:
  T[k] extends string ? (TranslatedString) :
  T[k] extends AmountJson ? (TranslatedString) :
  FormErrors<T[k]>;
}

type FormStatus<T> = {
  status: "ok",
  result: T,
  errors: undefined,
} | {
  status: "fail",
  result: RecursivePartial<T>,
  errors: FormErrors<T>,
}
type FormType = { amount: AmountJson, conv: TalerBankConversionApi.ConversionRate }

function constructFormHandler<T>(form: FormValues<T>, updateForm: (d: FormValues<T>) => void, errors: FormErrors<T> | undefined): FormHandler<T> {
  const keys = (Object.keys(form) as Array<keyof T>)

  const handler = keys.reduce((prev, fieldName) => {
    const currentValue: any = form[fieldName];
    const currentError: any = errors ? errors[fieldName] : undefined;
    function updater(newValue: any) {
      updateForm({ ...form, [fieldName]: newValue })
    }
    if (typeof currentValue === "object") {
      const group = constructFormHandler(currentValue, updater, currentError)
      // @ts-expect-error asdasd
      prev[fieldName] = group
      return prev;
    }
    const field: UIField = {
      error: currentError,
      value: currentValue,
      onUpdate: updater
    }
    // @ts-expect-error asdasd
    prev[fieldName] = field
    return prev
  }, {} as FormHandler<T>)

  return handler;
}

function useFormState<T>(defaultValue: FormValues<T>, check: (f: FormValues<T>) => FormStatus<T>): [FormHandler<T>, FormStatus<T>] {
  const [form, updateForm] = useState<FormValues<T>>(defaultValue)

  const status = check(form)
  const handler = constructFormHandler(form, updateForm, status.errors)

  return [handler, status]
}

function useComponentState({
  onUpdateSuccess,
  routeCancel,
  routeConversionConfig,
  routeMyAccountCashout,
  routeMyAccountDelete,
  routeMyAccountDetails,
  routeMyAccountPassword,
}: Props): utils.RecursiveState<VNode> {

  const result = useConversionInfo()
  const info = result && !(result instanceof TalerError) && result.type === "ok" ?
    result.body : undefined;

  const { state: credentials } = useBackendState();
  const creds =
    credentials.status !== "loggedIn" || !credentials.isUserAdministrator
      ? undefined
      : credentials;

  if (!info) {
    return <div>waiting...</div>
  }

  if (!creds) {
    return <div>only admin can setup conversion</div>;
  }

  return () => {
    const { i18n } = useTranslationContext();

    const { api, config } = useBankCoreApiContext();

    const [notification, notify, handleError] = useLocalNotification();

    const initalState: FormValues<FormType> = {
      amount: "100",
      conv: {
        cashin_min_amount: info.conversion_rate.cashin_min_amount.split(":")[1],
        cashin_tiny_amount: info.conversion_rate.cashin_tiny_amount.split(":")[1],
        cashin_fee: info.conversion_rate.cashin_fee.split(":")[1],
        cashin_ratio: info.conversion_rate.cashin_ratio,
        cashin_rounding_mode: info.conversion_rate.cashin_rounding_mode,
        cashout_min_amount: info.conversion_rate.cashout_min_amount.split(":")[1],
        cashout_tiny_amount: info.conversion_rate.cashout_tiny_amount.split(":")[1],
        cashout_fee: info.conversion_rate.cashout_fee.split(":")[1],
        cashout_ratio: info.conversion_rate.cashout_ratio,
        cashout_rounding_mode: info.conversion_rate.cashout_rounding_mode,
      }
    }

    const [form, status] = useFormState<FormType>(
      initalState,
      checkConversionForm(i18n, info.regional_currency, info.fiat_currency)
    )

    const {
      estimateByDebit: calculateCashoutFromDebit,
    } = useCashoutEstimator();

    const {
      estimateByDebit: calculateCashinFromDebit,
    } = useCashinEstimator();

    const [calc, setCalc] = useState<{ cashin: TransferCalculation, cashout: TransferCalculation }>()

    useEffect(() => {
      async function doAsync() {
        await handleError(async () => {
          if (!info) return;
          if (!form.amount?.value || form.amount.error) return;
          const in_amount = Amounts.parseOrThrow(`${info.fiat_currency}:${form.amount.value}`)
          const in_fee = Amounts.parseOrThrow(info.conversion_rate.cashin_fee)
          const cashin = await calculateCashinFromDebit(in_amount, in_fee);


          // const out_amount = Amounts.parseOrThrow(`${info.regional_currency}:${form.amount.value}`)
          const out_fee = Amounts.parseOrThrow(info.conversion_rate.cashout_fee)
          const cashout = await calculateCashoutFromDebit(cashin.credit, out_fee);

          setCalc({ cashin, cashout });
        });
      }
      doAsync();
    }, [form.amount?.value, form.conv?.cashin_fee?.value, form.conv?.cashout_fee?.value]);

    const [section, setSection] = useState<"detail" | "cashout" | "cashin">("detail")

    async function doUpdate() {
      if (!creds) return
      await handleError(async () => {
        if (status.status === "fail") return;
        const resp = await api
          .getConversionInfoAPI()
          .updateConversionRate(creds.token, status.result.conv)
        if (resp.type === "ok") {
          setSection("detail")
        } else {
          switch (resp.case) {
            case HttpStatusCode.Unauthorized: {
              return notify({
                type: "error",
                title: i18n.str`Wrong credentials`,
                description: resp.detail.hint as TranslatedString,
                debug: resp.detail,
              });
            }
            case HttpStatusCode.NotImplemented: {
              return notify({
                type: "error",
                title: i18n.str`Conversion is disabled`,
                description: resp.detail.hint as TranslatedString,
                debug: resp.detail,
              });
            }
            default:
              assertUnreachable(resp);
          }
        }
      });
    }

    const in_ratio = Number.parseFloat(info.conversion_rate.cashin_ratio)
    const out_ratio = Number.parseFloat(info.conversion_rate.cashout_ratio)

    const both_high = in_ratio > 1 && out_ratio > 1;
    const both_low = in_ratio < 1 && out_ratio < 1;


    return (
      <div>
        <ProfileNavigation current="conversion"
          routeMyAccountCashout={routeMyAccountCashout}
          routeMyAccountDelete={routeMyAccountDelete}
          routeMyAccountDetails={routeMyAccountDetails}
          routeMyAccountPassword={routeMyAccountPassword}
          routeConversionConfig={routeConversionConfig}
        />

        <LocalNotificationBanner notification={notification} />
        <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">

          <div class="px-4 sm:px-0">
            <h2 class="text-base font-semibold leading-7 text-gray-900">
              <i18n.Translate>Conversion</i18n.Translate>
            </h2>
            <div class="px-2 mt-2 grid grid-cols-1 gap-y-4 sm:gap-x-4">
              <label
                data-enabled={section === "detail"}
                class="relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none border-gray-300 data-[enabled=true]:border-indigo-600 data-[enabled=true]:ring-2 data-[enabled=true]:ring-indigo-600"
              >
                <input
                  type="radio"
                  name="project-type"
                  value="Newsletter"
                  class="sr-only"
                  aria-labelledby="project-type-0-label"
                  aria-describedby="project-type-0-description-0 project-type-0-description-1"
                  onChange={() => {
                    setSection("detail")
                  }}
                />
                <span class="flex flex-1">
                  <span class="flex flex-col">
                    <span class="block text-sm  font-medium text-gray-900">
                      <i18n.Translate>Details</i18n.Translate>
                    </span>
                  </span>
                </span>
              </label>

              <label
                data-enabled={section === "cashout"}
                class="relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none border-gray-300 -- data-[enabled=true]:border-indigo-600 data-[enabled=true]:ring-2 data-[enabled=true]:ring-indigo-600"
              >
                <input
                  type="radio"
                  name="project-type"
                  value="Existing Customers"
                  class="sr-only"
                  aria-labelledby="project-type-1-label"
                  aria-describedby="project-type-1-description-0 project-type-1-description-1"
                  onChange={() => {
                    setSection("cashout")
                  }}
                />
                <span class="flex flex-1">
                  <span class="flex flex-col">
                    <span class="block text-sm font-medium text-gray-900">
                      <i18n.Translate>Config cashout</i18n.Translate>
                    </span>
                  </span>
                </span>
              </label>
              <label
                data-enabled={section === "cashin"}
                class="relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none border-gray-300 -- data-[enabled=true]:border-indigo-600 data-[enabled=true]:ring-2 data-[enabled=true]:ring-indigo-600"
              >
                <input
                  type="radio"
                  name="project-type"
                  value="Existing Customers"
                  class="sr-only"
                  aria-labelledby="project-type-1-label"
                  aria-describedby="project-type-1-description-0 project-type-1-description-1"
                  onChange={() => {
                    setSection("cashin")
                  }}
                />
                <span class="flex flex-1">
                  <span class="flex flex-col">
                    <span class="block text-sm font-medium text-gray-900">
                      <i18n.Translate>Config cashin</i18n.Translate>
                    </span>
                  </span>
                </span>
              </label>
            </div>

          </div>

          <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();
            }}
          >
            {section == "cashin" &&
              <ConversionForm id="cashin"
                inputCurrency={info.fiat_currency}
                outputCurrency={info.regional_currency}
                fee={form?.conv?.cashin_fee}
                minimum={form?.conv?.cashin_min_amount}
                ratio={form?.conv?.cashin_ratio}
                rounding={form?.conv?.cashin_rounding_mode}
                tiny={form?.conv?.cashin_tiny_amount}
              />}



            {section == "cashout" && <Fragment>
              <ConversionForm id="cashout"
                inputCurrency={info.regional_currency}
                outputCurrency={info.fiat_currency}
                fee={form?.conv?.cashout_fee}
                minimum={form?.conv?.cashout_min_amount}
                ratio={form?.conv?.cashout_ratio}
                rounding={form?.conv?.cashout_rounding_mode}
                tiny={form?.conv?.cashout_tiny_amount}
              />
            </Fragment>}




            {section == "detail" && <Fragment>
              <div class="px-6 pt-6">
                <div class="justify-between items-center flex ">
                  <dt class="text-sm text-gray-600">
                    <i18n.Translate>Cashin ratio</i18n.Translate>
                  </dt>
                  <dd class="text-sm text-gray-900">
                    {info.conversion_rate.cashin_ratio}
                  </dd>
                </div>
              </div>

              <div class="px-6 pt-6">
                <div class="justify-between items-center flex ">
                  <dt class="text-sm text-gray-600">
                    <i18n.Translate>Cashout ratio</i18n.Translate>
                  </dt>
                  <dd class="text-sm text-gray-900">
                    {info.conversion_rate.cashout_ratio}
                  </dd>
                </div>
              </div>

              {both_low || both_high ? <div class="p-4">
                <Attention title={i18n.str`Bad ratios`} type="warning">
                  <i18n.Translate>
                    One of the ratios should be higher or equal than 1 an the other should be lower or equal than 1.
                  </i18n.Translate>
                </Attention>
              </div> : undefined}

              <div class="px-6 pt-6">
                <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
                  <div class="sm:col-span-5">
                    <label
                      for="amount"
                      class="block text-sm font-medium leading-6 text-gray-900"
                    >{i18n.str`Initial amount`}</label>
                    <InputAmount
                      name="amount"
                      left
                      currency={info.fiat_currency}
                      value={form.amount?.value ?? ""}
                      onChange={form.amount?.onUpdate}
                    />
                    <ShowInputErrorLabel
                      message={form.amount?.error}
                      isDirty={form.amount?.value !== undefined}
                    />
                    <p class="mt-2 text-sm text-gray-500">
                      <i18n.Translate>Use it to test how the conversion will affect the amount.</i18n.Translate>
                    </p>
                  </div>
                </div>
              </div>

              {!calc ? undefined : (
                <div class="px-6 pt-6">
                  <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>Sending to this bank</i18n.Translate>
                        </dt>
                        <dd class="text-sm text-gray-900">
                          <RenderAmount
                            value={calc.cashin.debit}
                            negative
                            withColor
                            spec={info.regional_currency_specification}
                          />
                        </dd>
                      </div>

                      {Amounts.isZero(calc.cashin.beforeFee) ? undefined : (
                        <div class="flex items-center justify-between afu ">
                          <dt class="flex items-center text-sm text-gray-600">
                            <span>
                              <i18n.Translate>Converted</i18n.Translate>
                            </span>
                          </dt>
                          <dd class="text-sm text-gray-900">
                            <RenderAmount
                              value={calc.cashin.beforeFee}
                              spec={info.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>Cashin after fee</i18n.Translate>
                        </dt>
                        <dd class="text-lg text-gray-900 font-medium">
                          <RenderAmount
                            value={calc.cashin.credit}
                            withColor
                            spec={info.fiat_currency_specification}
                          />
                        </dd>
                      </div>
                    </dl>
                  </div>

                  <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>Sending from this bank</i18n.Translate>
                        </dt>
                        <dd class="text-sm text-gray-900">
                          <RenderAmount
                            value={calc.cashout.debit}
                            negative
                            withColor
                            spec={info.fiat_currency_specification}
                          />
                        </dd>
                      </div>

                      {Amounts.isZero(calc.cashout.beforeFee) ? undefined : (
                        <div class="flex items-center justify-between afu">
                          <dt class="flex items-center text-sm text-gray-600">
                            <span>
                              <i18n.Translate>Converted</i18n.Translate>
                            </span>
                          </dt>
                          <dd class="text-sm text-gray-900">
                            <RenderAmount
                              value={calc.cashout.beforeFee}
                              spec={info.regional_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>Cashout after fee</i18n.Translate>
                        </dt>
                        <dd class="text-lg text-gray-900 font-medium">
                          <RenderAmount
                            value={calc.cashout.credit}
                            withColor
                            spec={info.regional_currency_specification}
                          />
                        </dd>
                      </div>
                    </dl>
                  </div>

                  {calc && status.status === "ok" && Amounts.cmp(status.result.amount, calc.cashout.credit) < 0 ? <div class="p-4">
                    <Attention title={i18n.str`Bad configuration`} type="warning">
                      <i18n.Translate>
                        This configuration allows users to cash out more of what has been cashed in.
                      </i18n.Translate>
                    </Attention>
                  </div> : undefined}
                </div>
              )}
            </Fragment>}


            <div class="flex items-center justify-between mt-4 gap-x-6 border-t border-gray-900/10 px-4 py-4">
              <a name="cancel"
                href={routeCancel.url({})}
                class="text-sm font-semibold leading-6 text-gray-900"
              >
                <i18n.Translate>Cancel</i18n.Translate>
              </a>
              {section == "cashin" || section == "cashout" ? <Fragment>
                <button
                  type="submit"
                  name="update conversion"
                  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={async () => {
                    doUpdate()
                  }}
                >
                  <i18n.Translate>Update</i18n.Translate>
                </button>
              </Fragment> : <div />}
            </div>


          </form>
        </div>
      </div>
    );

  }
}

/**
 * Show histories of public accounts.
 */
export const ConversionConfig = utils.recursive(useComponentState);

function checkConversionForm(i18n: InternationalizationAPI, regional: string, fiat: string) {
  return function check(state: FormValues<FormType>): FormStatus<FormType> {

    const cashin_min_amount = Amounts.parse(`${fiat}:${state.conv.cashin_min_amount}`)
    const cashin_tiny_amount = Amounts.parse(`${regional}:${state.conv.cashin_tiny_amount}`)
    const cashin_fee = Amounts.parse(`${regional}:${state.conv.cashin_fee}`)

    const cashout_min_amount = Amounts.parse(`${regional}:${state.conv.cashout_min_amount}`)
    const cashout_tiny_amount = Amounts.parse(`${fiat}:${state.conv.cashout_tiny_amount}`)
    const cashout_fee = Amounts.parse(`${fiat}:${state.conv.cashout_fee}`)

    const am = Amounts.parse(`${fiat}:${state.amount}`)

    const cashin_ratio = Number.parseFloat(state.conv.cashin_ratio ?? "")
    const cashout_ratio = Number.parseFloat(state.conv.cashout_ratio ?? "")

    const errors = undefinedIfEmpty<FormErrors<FormType>>({
      conv: undefinedIfEmpty<FormErrors<FormType["conv"]>>({
        cashin_min_amount: !state.conv.cashin_min_amount ? i18n.str`required` :
          !cashin_min_amount ? i18n.str`invalid` :
            undefined,
        cashin_tiny_amount: !state.conv.cashin_tiny_amount ? i18n.str`required` :
          !cashin_tiny_amount ? i18n.str`invalid` :
            undefined,
        cashin_fee: !state.conv.cashin_fee ? i18n.str`required` :
          !cashin_fee ? i18n.str`invalid` :
            undefined,

        cashout_min_amount: !state.conv.cashout_min_amount ? i18n.str`required` :
          !cashout_min_amount ? i18n.str`invalid` :
            undefined,
        cashout_tiny_amount: !state.conv.cashin_tiny_amount ? i18n.str`required` :
          !cashout_tiny_amount ? i18n.str`invalid` :
            undefined,
        cashout_fee: !state.conv.cashin_fee ? i18n.str`required` :
          !cashout_fee ? i18n.str`invalid` :
            undefined,

        cashin_rounding_mode: !state.conv.cashin_rounding_mode ? i18n.str`required` : undefined,
        cashout_rounding_mode: !state.conv.cashout_rounding_mode ? i18n.str`required` : undefined,

        cashin_ratio: !state.conv.cashin_ratio ? i18n.str`required` : Number.isNaN(cashin_ratio) ? i18n.str`invalid` : undefined,
        cashout_ratio: !state.conv.cashout_ratio ? i18n.str`required` : Number.isNaN(cashout_ratio) ? i18n.str`invalid` : undefined,
      }),

      amount: !state.amount ? i18n.str`required` :
        !am ? i18n.str`invalid` :
          undefined,
    })

    const result: RecursivePartial<FormType> = {
      amount: am,
      conv: {
        cashin_fee: !errors?.conv?.cashin_fee ? Amounts.stringify(cashin_fee!) : undefined,
        cashin_min_amount: !errors?.conv?.cashin_min_amount ? Amounts.stringify(cashin_min_amount!) : undefined,
        cashin_ratio: !errors?.conv?.cashin_ratio ? String(cashin_ratio!) : undefined,
        cashin_rounding_mode: !errors?.conv?.cashin_rounding_mode ? (state.conv.cashin_rounding_mode!) : undefined,
        cashin_tiny_amount: !errors?.conv?.cashin_tiny_amount ? Amounts.stringify(cashin_tiny_amount!) : undefined,
        cashout_fee: !errors?.conv?.cashout_fee ? Amounts.stringify(cashout_fee!) : undefined,
        cashout_min_amount: !errors?.conv?.cashout_min_amount ? Amounts.stringify(cashout_min_amount!) : undefined,
        cashout_ratio: !errors?.conv?.cashout_ratio ? String(cashout_ratio!) : undefined,
        cashout_rounding_mode: !errors?.conv?.cashout_rounding_mode ? (state.conv.cashout_rounding_mode!) : undefined,
        cashout_tiny_amount: !errors?.conv?.cashout_tiny_amount ? Amounts.stringify(cashout_tiny_amount!) : undefined,
      }

    }
    return errors === undefined ?
      { status: "ok", result: result as FormType, errors } :
      { status: "fail", result, errors }
  }
}


function ConversionForm({ id, inputCurrency, outputCurrency, fee, minimum, ratio, rounding, tiny }: {
  inputCurrency: string,
  outputCurrency: string,
  minimum: UIField | undefined,
  tiny: UIField | undefined,
  fee: UIField | undefined,
  rounding: UIField | undefined,
  ratio: UIField | undefined,
  id: string,
}): VNode {
  const { i18n } = useTranslationContext();
  return <Fragment>
    <div class="px-6 pt-6">
      <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
        <div class="sm:col-span-5">
          <label
            for="cashin_min_amount"
            class="block text-sm font-medium leading-6 text-gray-900"
          >{i18n.str`Minimum amount`}</label>
          <InputAmount
            name="cashin_min_amount"
            left
            currency={inputCurrency}
            value={minimum?.value ?? ""}
            onChange={minimum?.onUpdate}
          />
          <ShowInputErrorLabel
            message={minimum?.error}
            isDirty={minimum?.value !== undefined}
          />
          <p class="mt-2 text-sm text-gray-500">
            <i18n.Translate>Only cashout operation above this threshold will be allowed</i18n.Translate>
          </p>
        </div>
      </div>
    </div>

    <div class="px-6 pt-6">
      <label
        class="block text-sm font-medium leading-6 text-gray-900"
        for="password"
      >
        {i18n.str`Ratio`}
      </label>
      <div class="mt-2">
        <input
          type="number"
          class="block rounded-md 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="current"
          id="cashin_ratio"
          data-error={!!ratio?.error && ratio?.value !== undefined}
          value={ratio?.value ?? ""}
          onChange={(e) => {
            ratio?.onUpdate(e.currentTarget.value);
          }}
          autocomplete="off"
        />
        <ShowInputErrorLabel
          message={ratio?.error}
          isDirty={ratio?.value !== undefined}
        />
      </div>
      <p class="mt-2 text-sm text-gray-500">
        <i18n.Translate>
          Conversion ratio between currencies
        </i18n.Translate>
      </p>
    </div>

    <div class="px-6 pt-4">
      <Attention title={i18n.str`Example conversion`}>
        <i18n.Translate>1 {inputCurrency} will be converted into {ratio?.value} {outputCurrency}</i18n.Translate>
      </Attention>
    </div>

    <div class="px-6 pt-6">
      <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
        <div class="sm:col-span-5">
          <label
            for="cashin_tiny_amount"
            class="block text-sm font-medium leading-6 text-gray-900"
          >{i18n.str`Rounding value`}</label>
          <InputAmount
            name="cashin_tiny_amount"
            left
            currency={outputCurrency}
            value={tiny?.value ?? ""}
            onChange={tiny?.onUpdate}
          />
          <ShowInputErrorLabel
            message={tiny?.error}
            isDirty={tiny?.value !== undefined}
          />
          <p class="mt-2 text-sm text-gray-500">
            <i18n.Translate>Smallest difference between two amounts after the ratio is applied.</i18n.Translate>
          </p>
        </div>
      </div>
    </div>

    <div class="px-6 pt-6">
      <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
        <div class="sm:col-span-5">
          <label
            class="block text-sm font-medium leading-6 text-gray-900"
            for="channel"
          >
            {i18n.str`Rounding mode`}
          </label>
          <div class="mt-2 max-w-xl text-sm text-gray-500">
            <div class="px-4 mt-4 grid grid-cols-1 gap-y-6">
              <label
                onClick={(e) => {
                  e.preventDefault();
                  rounding?.onUpdate("zero")
                }}
                data-selected={rounding?.value === "zero"}
                class="relative flex data-[disabled=false]:cursor-pointer rounded-lg border bg-white data-[disabled=true]:bg-gray-200 p-4 shadow-sm focus:outline-none border-gray-300 data-[selected=true]:ring-2 data-[selected=true]:ring-indigo-600"
              >
                <input
                  type="radio"
                  name="channel"
                  value="Newsletter"
                  class="sr-only"
                />
                <span class="flex flex-1">
                  <span class="flex flex-col">
                    <span
                      id="project-type-0-label"
                      class="block text-sm font-medium text-gray-900 "
                    >
                      <i18n.Translate>Zero</i18n.Translate>
                    </span>
                    <i18n.Translate>Amount will be round below to the largest possible value smaller than the input.</i18n.Translate>
                  </span>
                </span>
                <svg
                  data-selected={rounding?.value === "zero"}
                  class="h-5 w-5 text-indigo-600 data-[selected=false]:hidden"
                  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>
              </label>

              <label
                onClick={(e) => {
                  e.preventDefault();
                  rounding?.onUpdate("up")
                }}
                data-selected={rounding?.value === "up"}
                class="relative flex data-[disabled=false]:cursor-pointer rounded-lg border data-[disabled=true]:bg-gray-200 p-4 shadow-sm focus:outline-none border-gray-300 data-[selected=true]:ring-2 data-[selected=true]:ring-indigo-600"
              >
                <input
                  type="radio"
                  name="channel"
                  value="Existing Customers"
                  class="sr-only"
                />
                <span class="flex flex-1">
                  <span class="flex flex-col">
                    <span
                      id="project-type-0-label"
                      class="block text-sm font-medium text-gray-900 "
                    >
                      <i18n.Translate>Up</i18n.Translate>
                    </span>
                    <i18n.Translate>Amount will be round up to the smallest possible value larger than the input.</i18n.Translate>
                  </span>
                </span>
                <svg
                  data-selected={rounding?.value === "up"}
                  class="h-5 w-5 text-indigo-600 data-[selected=false]:hidden"
                  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>
              </label>
              <label
                onClick={(e) => {
                  e.preventDefault();
                  rounding?.onUpdate("nearest")
                }}
                data-selected={rounding?.value === "nearest"}
                class="relative flex data-[disabled=false]:cursor-pointer rounded-lg border data-[disabled=true]:bg-gray-200 p-4 shadow-sm focus:outline-none border-gray-300 data-[selected=true]:ring-2 data-[selected=true]:ring-indigo-600"
              >
                <input
                  type="radio"
                  name="channel"
                  value="Existing Customers"
                  class="sr-only"
                />
                <span class="flex flex-1">
                  <span class="flex flex-col">
                    <span
                      id="project-type-0-label"
                      class="block text-sm font-medium text-gray-900 "
                    >
                      <i18n.Translate>Nearest</i18n.Translate>
                    </span>
                    <i18n.Translate>Amount will be round to the closest possible value.</i18n.Translate>
                  </span>
                </span>
                <svg
                  data-selected={rounding?.value === "nearest"}
                  class="h-5 w-5 text-indigo-600 data-[selected=false]:hidden"
                  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>
              </label>
            </div>
          </div>
        </div>
      </div>
    </div>

    <div class="px-6 pt-4">
      <Attention title={i18n.str`Examples`}>
        <section class="grid grid-cols-1 gap-y-3  text-gray-600">
          <details class="group  text-sm">
            <summary class="flex cursor-pointer flex-row items-center justify-between  ">
              Rounding an amount of 1.24 with rounding value 0.1
              <svg class="h-6 w-6 rotate-0 transform  group-open:rotate-180" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true">
                <path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"></path>
              </svg>
            </summary>
            <p class="text-gray-900 my-4">
              Given the rounding value of 0.1 the possible values closest to 1.24 are: 1.1, 1.2, 1.3, 1.4.
            </p>
            <p class="text-gray-900 my-4">
              With the "zero" mode the value will be rounded to 1.2
            </p>
            <p class="text-gray-900 my-4">
              With the "nearest" mode the value will be rounded to 1.2
            </p>
            <p class="text-gray-900 mt-4">
              With the "up" mode the value will be rounded to 1.3
            </p>
          </details>
          <details class="group ">
            <summary class="flex cursor-pointer flex-row items-center justify-between  ">
              Rounding an amount of 1.26 with rounding value 0.1
              <svg class="h-6 w-6 rotate-0 transform  group-open:rotate-180" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true">
                <path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"></path>
              </svg>
            </summary>
            <p class="text-gray-900 my-4">
              Given the rounding value of 0.1 the possible values closest to 1.24 are: 1.1, 1.2, 1.3, 1.4.
            </p>
            <p class="text-gray-900 my-4">
              With the "zero" mode the value will be rounded to 1.2
            </p>
            <p class="text-gray-900 my-4">
              With the "nearest" mode the value will be rounded to 1.3
            </p>
            <p class="text-gray-900 my-4">
              With the "up" mode the value will be rounded to 1.3
            </p>
          </details>
          <details class="group ">
            <summary class="flex cursor-pointer flex-row items-center justify-between  ">
              Rounding an amount of 1.24 with rounding value 0.3
              <svg class="h-6 w-6 rotate-0 transform  group-open:rotate-180" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true">
                <path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"></path>
              </svg>
            </summary>
            <p class="text-gray-900 my-4">
              Given the rounding value of 0.3 the possible values closest to 1.24 are: 0.9, 1.2, 1.5, 1.8.
            </p>
            <p class="text-gray-900 my-4">
              With the "zero" mode the value will be rounded to 1.2
            </p>
            <p class="text-gray-900 my-4">
              With the "nearest" mode the value will be rounded to 1.2
            </p>
            <p class="text-gray-900 my-4">
              With the "up" mode the value will be rounded to 1.5
            </p>
          </details>
          <details class="group ">
            <summary class="flex cursor-pointer flex-row items-center justify-between  ">
              Rounding an amount of 1.26 with rounding value 0.3
              <svg class="h-6 w-6 rotate-0 transform  group-open:rotate-180" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true">
                <path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"></path>
              </svg>
            </summary>
            <p class="text-gray-900 my-4">
              Given the rounding value of 0.3 the possible values closest to 1.24 are: 0.9, 1.2, 1.5, 1.8.
            </p>
            <p class="text-gray-900 my-4">
              With the "zero" mode the value will be rounded to 1.2
            </p>
            <p class="text-gray-900 my-4">
              With the "nearest" mode the value will be rounded to 1.3
            </p>
            <p class="text-gray-900 my-4">
              With the "up" mode the value will be rounded to 1.3
            </p>
          </details>
        </section>
      </Attention>
    </div>



    <div class="px-6 pt-6">
      <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
        <div class="sm:col-span-5">
          <label
            for="cashin_fee"
            class="block text-sm font-medium leading-6 text-gray-900"
          >{i18n.str`Fee`}</label>
          <InputAmount
            name="cashin_fee"
            left
            currency={outputCurrency}
            value={fee?.value ?? ""}
            onChange={fee?.onUpdate}
          />
          <ShowInputErrorLabel
            message={fee?.error}
            isDirty={fee?.value !== undefined}
          />
          <p class="mt-2 text-sm text-gray-500">
            <i18n.Translate>Amount to be deducted before amount is credited.</i18n.Translate>
          </p>
        </div>
      </div>
    </div>

  </Fragment>
}
