/*
 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 {
  AbsoluteTime,
  AmountJson,
  Amounts,
  HttpStatusCode,
  TalerBankConversionApi,
  TalerError,
  TranslatedString,
  assertUnreachable,
} from "@gnu-taler/taler-util";
import {
  Attention,
  ButtonBetter,
  ErrorLoading,
  InternationalizationAPI,
  Loading,
  LocalNotificationBanner,
  RouteDefinition,
  ShowInputErrorLabel,
  useBankCoreApiContext,
  useLocalNotificationBetter,
  useTranslationContext,
  utils,
} from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
import { useEffect, useState } from "preact/hooks";
import {
  FormErrors,
  FormStatus,
  FormValues,
  RecursivePartial,
  UIField,
  useFormState,
} from "../../hooks/form.js";
import {
  TransferCalculation,
  useCashinEstimator,
  useCashoutEstimator,
  useConversionInfo,
} from "../../hooks/regional.js";
import { useSessionState } from "../../hooks/session.js";
import { undefinedIfEmpty } from "../../utils.js";
import { InputAmount, RenderAmount } from "../PaytoWireTransferForm.js";
import { ProfileNavigation } from "../ProfileNavigation.js";

import { DescribeConversion } from "../admin/ConversionClassList.js";
import { opEmptySuccess } from "@gnu-taler/taler-util";
import { opFixedSuccess } from "@gnu-taler/taler-util";
import { TalerErrorCode } from "@gnu-taler/taler-util";

const TALER_SCREEN_ID = 126;

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

type FormType = {
  amount: AmountJson;
  conv: TalerBankConversionApi.ConversionRate;
};

function useComponentState({
  routeCancel,
  routeConversionConfig,
  routeMyAccountCashout,
  routeMyAccountDelete,
  routeMyAccountDetails,
  routeMyAccountPassword,
}: Props): utils.RecursiveState<VNode> {
  const { i18n } = useTranslationContext();

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

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

  const resp = useConversionInfo();
  if (!resp) {
    return <Loading />;
  }
  if (resp instanceof TalerError) {
    return <ErrorLoading error={resp} />;
  }

  if (resp.type !== "ok") {
    switch (resp.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(resp);
    }
  }
  const info = resp.body;

  return function afterComponentLoads() {
    const {
      lib: { conversion },
    } = useBankCoreApiContext();

    const [notification, safeFunctionHandler] = useLocalNotificationBetter();

    const initalState: FormValues<FormType> = {
      amount: "100",
      conv: {
        cashin_min_amount: info.conversion_rate.cashin_min_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,
        cashin_tiny_amount:
          info.conversion_rate.cashin_tiny_amount.split(":")[1],
        cashout_min_amount:
          info.conversion_rate.cashout_min_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,
        cashout_tiny_amount:
          info.conversion_rate.cashout_tiny_amount.split(":")[1],
      },
    };

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

    const { estimateByDebit: calculateCashoutFromDebit } =
      useCashoutEstimator();

    const { estimateByDebit: calculateCashinFromDebit } = useCashinEstimator();

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

    const in_amount = !form.amount
      ? undefined
      : Amounts.parseOrThrow(`${info.fiat_currency}:${form.amount.value}`);

    const in_fee = Amounts.parseOrThrow(info.conversion_rate.cashin_fee);
    const out_fee = Amounts.parseOrThrow(info.conversion_rate.cashout_fee);

    const calculate = safeFunctionHandler(
      async (amount: AmountJson) => {
        const respCashin = await calculateCashinFromDebit(amount, in_fee);
        if (respCashin.type === "fail") {
          return respCashin;
        }
        const cashin = respCashin.body;
        const respCashout = await calculateCashoutFromDebit(
          cashin.credit,
          out_fee,
        );
        if (respCashout.type === "fail") {
          return respCashout;
        }
        const cashout = respCashout.body;
        return opFixedSuccess({ cashin, cashout });
      },
      !in_amount || status.status === "fail" ? undefined : [in_amount],
    );
    calculate.onSuccess = (resp) => setCalc(resp);
    calculate.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(() => {
      calculate.call();
    }, [
      form.amount?.value,
      form.conv?.cashin_fee?.value,
      form.conv?.cashout_fee?.value,
    ]);

    const [section, setSection] = useState<"detail" | "cashout" | "cashin">(
      "detail",
    );
    const cashinCalc = calculationResult?.cashin;
    const cashoutCalc = calculationResult?.cashout;

    const update = safeFunctionHandler(
      conversion.updateConversionRate.bind(conversion),
      !creds || status.status === "fail"
        ? undefined
        : [creds.token, status.result.conv],
    );

    update.onSuccess = () => {
      setSection("detail");
    };
    update.onFail = (fail) => {
      switch (fail.case) {
        case HttpStatusCode.Unauthorized:
          return i18n.str`Wrong credentials`;
        case HttpStatusCode.NotImplemented:
          return i18n.str`Conversion is disabled`;
      }
    };

    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-6 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</i18n.Translate>
                    </dt>
                    <dd class="text-sm text-gray-900">
                      <DescribeConversion
                        ratio={info.conversion_rate.cashin_ratio}
                        fee={info.conversion_rate.cashin_fee}
                        min={info.conversion_rate.cashin_min_amount}
                        rounding={info.conversion_rate.cashin_rounding_mode}
                        minSpec={info.fiat_currency_specification}
                        feeSpec={info.regional_currency_specification}
                      />
                    </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</i18n.Translate>
                    </dt>
                    <dd class="text-sm text-gray-900">
                      <DescribeConversion
                        ratio={info.conversion_rate.cashout_ratio}
                        fee={info.conversion_rate.cashout_fee}
                        min={info.conversion_rate.cashout_min_amount}
                        rounding={info.conversion_rate.cashout_rounding_mode}
                        minSpec={info.regional_currency_specification}
                        feeSpec={info.fiat_currency_specification}
                      />
                    </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>

                {!cashoutCalc || !cashinCalc ? 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={cashinCalc.debit}
                              negative
                              withColor
                              spec={info.fiat_currency_specification}
                            />
                          </dd>
                        </div>

                        {Amounts.isZero(cashinCalc.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={cashinCalc.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>Cashin after fee</i18n.Translate>
                          </dt>
                          <dd class="text-lg text-gray-900 font-medium">
                            <RenderAmount
                              value={cashinCalc.credit}
                              withColor
                              spec={info.regional_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={cashoutCalc.debit}
                              negative
                              withColor
                              spec={info.regional_currency_specification}
                            />
                          </dd>
                        </div>

                        {Amounts.isZero(cashoutCalc.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={cashoutCalc.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>Cashout after fee</i18n.Translate>
                          </dt>
                          <dd class="text-lg text-gray-900 font-medium">
                            <RenderAmount
                              value={cashoutCalc.credit}
                              withColor
                              spec={info.fiat_currency_specification}
                            />
                          </dd>
                        </div>
                      </dl>
                    </div>

                    {cashoutCalc &&
                    status.status === "ok" &&
                    Amounts.cmp(status.result.amount, cashoutCalc.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" ? (
                <ButtonBetter
                  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={update}
                >
                  <i18n.Translate>Update</i18n.Translate>
                </ButtonBetter>
              ) : (
                <div />
              )}
            </div>
          </form>
        </div>
      </div>
    );
  };
}

export const ConversionConfig = utils.recursive(useComponentState);

/**
 *
 * @param i18n
 * @param regional
 * @param fiat
 * @returns form validator
 */
function createFormValidator(
  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_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_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`Rnvalid`
            : undefined,

        cashin_tiny_amount: !state.conv.cashin_tiny_amount
          ? i18n.str`Required`
          : !cashin_tiny_amount
            ? i18n.str`Invalid`
            : +state.conv.cashin_tiny_amount == 0
              ? i18n.str`Must be > 0`
              : undefined,
        cashout_tiny_amount: !state.conv.cashout_tiny_amount
          ? i18n.str`Required`
          : !cashout_tiny_amount
            ? i18n.str`Invalid`
            : +state.conv.cashout_tiny_amount == 0
              ? i18n.str`Must be > 0`
              : 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_tiny_amount: !errors?.conv?.cashin_tiny_amount
          ? Amounts.stringify(cashin_tiny_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,
        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_tiny_amount: !errors?.conv?.cashout_tiny_amount
          ? Amounts.stringify(cashout_tiny_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,
      },
    };
    return errors === undefined
      ? { status: "ok", result: result as FormType, errors }
      : { status: "fail", result, errors };
  };
}

export function ConversionForm({
  id,
  inputCurrency,
  outputCurrency,
  fee,
  minimum,
  ratio,
  rounding,
  tiny,
  fallback_fee,
  fallback_minimum,
  fallback_ratio,
  fallback_rounding,
  fallback_tiny,
}: {
  inputCurrency: string;
  outputCurrency: string;
  minimum: UIField | undefined;
  fallback_minimum?: string;
  fee: UIField | undefined;
  fallback_fee?: string;
  rounding: UIField | undefined;
  fallback_rounding?: string;
  tiny: UIField | undefined;
  fallback_tiny?: string;
  ratio: UIField | undefined;
  fallback_ratio?: string;
  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={`${id}_min_amount`}
              class="block text-sm font-medium leading-6 text-gray-900"
            >{i18n.str`Minimum amount`}</label>
            <InputAmount
              name={`${id}_min_amount`}
              left
              currency={inputCurrency}
              value={minimum?.value ?? ""}
              onChange={minimum?.onUpdate}
              placeholder={fallback_minimum}
            />
            <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>
              &nbsp;
            </p>
          </div>
        </div>
      </div>

      <div class="px-6 pt-6">
        <label
          class="block text-sm font-medium leading-6 text-gray-900"
          for={`${id}_ratio`}
        >
          {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={`${id}_ratio`}
            data-error={!!ratio?.error && ratio?.value !== undefined}
            value={ratio?.value ?? ""}
            onChange={(e) => {
              ratio?.onUpdate(e.currentTarget.value);
            }}
            autocomplete="off"
            placeholder={fallback_ratio ?? "1.0"}
          />
          <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 ?? fallback_ratio} {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
              class="block text-sm font-medium leading-6 text-gray-900"
              for={`${id}_tiny_amount`}
            >
              {i18n.str`Tiny amount`}
            </label>
            <InputAmount
              name={`${id}_tiny_amount`}
              left
              currency={inputCurrency}
              value={tiny?.value ?? ""}
              onChange={tiny?.onUpdate}
              placeholder={fallback_tiny ?? "0.01"}
            />
            <ShowInputErrorLabel
              message={tiny?.error}
              isDirty={tiny?.value !== undefined}
            />
          </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={`${id}_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 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 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 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>
              {!fallback_rounding ? undefined : (
                <p class="mt-2 text-sm text-gray-500">
                  <i18n.Translate>
                    If none specified the fallback value is "{fallback_rounding}
                    ".
                  </i18n.Translate>
                </p>
              )}
            </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  ">
                <i18n.Translate>
                  Rounding an amount of 1.24 with rounding value 0.1
                </i18n.Translate>
                <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">
                <i18n.Translate>
                  Given the rounding value of 0.1 the possible values closest to
                  1.24 are: 1.1, 1.2, 1.3, 1.4.
                </i18n.Translate>
              </p>
              <p class="text-gray-900 my-4">
                <i18n.Translate>
                  With the "zero" mode the value will be rounded to 1.2
                </i18n.Translate>
              </p>
              <p class="text-gray-900 my-4">
                <i18n.Translate>
                  With the "nearest" mode the value will be rounded to 1.2
                </i18n.Translate>
              </p>
              <p class="text-gray-900 mt-4">
                <i18n.Translate>
                  With the "up" mode the value will be rounded to 1.3
                </i18n.Translate>
              </p>
            </details>
            <details class="group ">
              <summary class="flex cursor-pointer flex-row items-center justify-between  ">
                <i18n.Translate>
                  Rounding an amount of 1.26 with rounding value 0.1
                </i18n.Translate>
                <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">
                <i18n.Translate>
                  Given the rounding value of 0.1 the possible values closest to
                  1.24 are: 1.1, 1.2, 1.3, 1.4.
                </i18n.Translate>
              </p>
              <p class="text-gray-900 my-4">
                <i18n.Translate>
                  With the "zero" mode the value will be rounded to 1.2
                </i18n.Translate>
              </p>
              <p class="text-gray-900 my-4">
                <i18n.Translate>
                  With the "nearest" mode the value will be rounded to 1.3
                </i18n.Translate>
              </p>
              <p class="text-gray-900 my-4">
                <i18n.Translate>
                  With the "up" mode the value will be rounded to 1.3
                </i18n.Translate>
              </p>
            </details>
            <details class="group ">
              <summary class="flex cursor-pointer flex-row items-center justify-between  ">
                <i18n.Translate>
                  Rounding an amount of 1.24 with rounding value 0.3
                </i18n.Translate>
                <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">
                <i18n.Translate>
                  Given the rounding value of 0.3 the possible values closest to
                  1.24 are: 0.9, 1.2, 1.5, 1.8.
                </i18n.Translate>
              </p>
              <p class="text-gray-900 my-4">
                <i18n.Translate>
                  With the "zero" mode the value will be rounded to 1.2
                </i18n.Translate>
              </p>
              <p class="text-gray-900 my-4">
                <i18n.Translate>
                  With the "nearest" mode the value will be rounded to 1.2
                </i18n.Translate>
              </p>
              <p class="text-gray-900 my-4">
                <i18n.Translate>
                  With the "up" mode the value will be rounded to 1.5
                </i18n.Translate>
              </p>
            </details>
            <details class="group ">
              <summary class="flex cursor-pointer flex-row items-center justify-between  ">
                <i18n.Translate>
                  Rounding an amount of 1.26 with rounding value 0.3
                </i18n.Translate>
                <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">
                <i18n.Translate>
                  Given the rounding value of 0.3 the possible values closest to
                  1.24 are: 0.9, 1.2, 1.5, 1.8.
                </i18n.Translate>
              </p>
              <p class="text-gray-900 my-4">
                <i18n.Translate>
                  With the "zero" mode the value will be rounded to 1.2
                </i18n.Translate>
              </p>
              <p class="text-gray-900 my-4">
                <i18n.Translate>
                  With the "nearest" mode the value will be rounded to 1.3
                </i18n.Translate>
              </p>
              <p class="text-gray-900 my-4">
                <i18n.Translate>
                  With the "up" mode the value will be rounded to 1.3
                </i18n.Translate>
                .0
              </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={`${id}_fee`}
              class="block text-sm font-medium leading-6 text-gray-900"
            >{i18n.str`Fee`}</label>
            <InputAmount
              name={`${id}_fee`}
              left
              currency={outputCurrency}
              value={fee?.value ?? ""}
              onChange={fee?.onUpdate}
              placeholder={fallback_fee}
            />
            <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>
  );
}
