/*
 This file is part of GNU Taler
 (C) 2022 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 {
  Amounts,
  TalerError,
  TranslatedString
} from "@gnu-taler/taler-util";
import {
  useLocalNotification,
  useTranslationContext
} from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
import { useEffect, useState } from "preact/hooks";
import { mutate } from "swr";
import { Attention } from "@gnu-taler/web-util/browser";
import { ErrorLoading } from "@gnu-taler/web-util/browser";
import { Loading } from "@gnu-taler/web-util/browser";
import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser";
import { useBankCoreApiContext } from "../../context/config.js";
import { useAccountDetails } from "../../hooks/access.js";
import { useBackendState } from "../../hooks/backend.js";
import {
  useEstimator
} from "../../hooks/circuit.js";
import {
  TanChannel,
  undefinedIfEmpty,
  withRuntimeErrorHandling
} from "../../utils.js";
import { LoginForm } from "../LoginForm.js";
import { InputAmount } from "../PaytoWireTransferForm.js";
import { assertUnreachable } from "../WithdrawalOperationPage.js";
import { ShowLocalNotification } from "@gnu-taler/web-util/browser";

interface Props {
  account: string;
  onComplete: (id: string) => void;
  onCancel: () => void;
}

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


export function CreateCashout({
  account: accountName,
  onComplete,
  onCancel,
}: Props): VNode {
  const { i18n } = useTranslationContext();
  const resultAccount = useAccountDetails(accountName);
  const {
    estimateByCredit: calculateFromCredit,
    estimateByDebit: calculateFromDebit,
  } = useEstimator();
  const { state } = useBackendState()
  const creds = state.status !== "loggedIn" ? undefined : state
  const { api, config } = useBankCoreApiContext()
  const [form, setForm] = useState<Partial<FormType>>({ isDebit: true });
  const [notification, notify, handleError] = useLocalNotification()

  if (!config.have_cashout) {
    return <Attention type="warning" title={i18n.str`Unable to create a cashout`} onClose={onCancel}>
      <i18n.Translate>The bank configuration does not support cashout operations.</i18n.Translate>
    </Attention>
  }
  if (!config.fiat_currency) {
    return <Attention type="warning" title={i18n.str`Unable to create a cashout`} onClose={onCancel}>
      <i18n.Translate>The bank configuration support cashout operations but there is no fiat currency.</i18n.Translate>
    </Attention>
  }

  if (!resultAccount) {
    return <Loading />
  }
  if (resultAccount instanceof TalerError) {
    return <ErrorLoading error={resultAccount} />
  }
  if (resultAccount.type === "fail") {
    switch (resultAccount.case) {
      case "unauthorized": return <LoginForm reason="forbidden" />
      case "not-found": return <LoginForm reason="not-found" />
      default: assertUnreachable(resultAccount)
    }
  }

  // if (resultRatios.type === "fail") {
  //   switch (resultRatios.case) {
  //     case "not-supported": return <div>cashout operations are not supported</div>
  //     default: assertUnreachable(resultRatios.case)
  //   }
  // }

  // const ratio = resultRatios.body

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

  const zero = Amounts.zeroOfCurrency(account.balance.currency);
  const limit = account.balanceIsDebit
    ? Amounts.sub(account.debitThreshold, account.balance).amount
    : Amounts.add(account.balance, account.debitThreshold).amount;

  const zeroCalc = { debit: zero, credit: zero, beforeFee: zero };
  const [calc, setCalc] = useState(zeroCalc);

  const sellRate = config.conversion_info?.sell_at_ratio;
  const sellFee = !config.conversion_info?.sell_out_fee
    ? zero
    : Amounts.parseOrThrow(
      `${account.balance.currency}:${config.conversion_info.sell_out_fee}`,
    );

  if (sellRate === undefined || sellRate < 0) return <div>error rate</div>;

  const safeSellRate = sellRate

  const amount = Amounts.parseOrThrow(
    `${!form.isDebit ? config.fiat_currency.name : account.balance.currency}:${!form.amount ? "0" : form.amount
    }`,
  );

  useEffect(() => {
    async function doAsync() {
      await handleError(async () => {
        const resp = await (form.isDebit ?
          calculateFromDebit(amount, sellFee, safeSellRate) :
          calculateFromCredit(amount, sellFee, safeSellRate));
        setCalc(resp)
      })
    }
    doAsync()
  }, [form.amount, form.isDebit]);

  const balanceAfter = Amounts.sub(account.balance, calc.debit).amount;

  function updateForm(newForm: typeof form): void {
    setForm(newForm);
  }
  const errors = undefinedIfEmpty<ErrorFrom<typeof form>>({
    amount: !form.amount
      ? i18n.str`required`
      : !amount
        ? i18n.str`could not be parsed`
        : Amounts.cmp(limit, calc.debit) === -1
          ? i18n.str`balance is not enough`
          : Amounts.cmp(calc.beforeFee, sellFee) === -1
            ? i18n.str`the total amount to transfer does not cover the fees`
            : Amounts.isZero(calc.credit)
              ? i18n.str`the total transfer at destination will be zero`
              : undefined,
    channel: !form.channel ? i18n.str`required` : undefined,
  });

  return (
    <div>
      <ShowLocalNotification notification={notification} />
      <h1>New cashout</h1>
      <form class="pure-form">
        <fieldset>
          <label>{i18n.str`Subject`}</label>
          <input
            value={form.subject ?? ""}
            onChange={(e) => {
              form.subject = e.currentTarget.value;
              updateForm(structuredClone(form));
            }}
          />
          <ShowInputErrorLabel
            message={errors?.subject}
            isDirty={form.subject !== undefined}
          />
        </fieldset>
        <fieldset>
          <label for="amount">
            {form.isDebit
              ? i18n.str`Amount to send`
              : i18n.str`Amount to receive`}

          </label>
          <div style={{ display: "flex" }}>
            <InputAmount
              name="amount"
              currency={amount.currency}
              value={form.amount}
              onChange={(v) => {
                form.amount = v;
                updateForm(structuredClone(form));
              }}
              error={errors?.amount}
            />
            <label class="toggle" style={{ marginLeft: 4, marginTop: 0 }}>
              <input
                class="toggle-checkbox"
                type="checkbox"
                name="asd"
                onChange={(e): void => {
                  form.isDebit = !form.isDebit;
                  updateForm(structuredClone(form));
                }}
              />
              <div class="toggle-switch"></div>
            </label>
          </div>
        </fieldset>
        <fieldset>
          <label>{i18n.str`Conversion rate`}</label>
          <input value={sellRate} disabled />
        </fieldset>
        <fieldset>
          <label for="balance-now">{i18n.str`Balance now`}</label>
          <InputAmount
            name="banace-now"
            currency={account.balance.currency}
            value={Amounts.stringifyValue(account.balance)}
          />
        </fieldset>
        <fieldset>
          <label for="total-cost"
            style={{ fontWeight: "bold", color: "red" }}
          >{i18n.str`Total cost`}</label>
          <InputAmount
            name="total-cost"
            currency={account.balance.currency}
            value={Amounts.stringifyValue(calc.debit)}
          />
        </fieldset>
        <fieldset>
          <label for="balance-after">{i18n.str`Balance after`}</label>
          <InputAmount
            name="balance-after"
            currency={account.balance.currency}
            value={balanceAfter ? Amounts.stringifyValue(balanceAfter) : ""}
          />
        </fieldset>{" "}
        {Amounts.isZero(sellFee) ? undefined : (
          <Fragment>
            <fieldset>
              <label for="amount-conversiojn">{i18n.str`Amount after conversion`}</label>
              <InputAmount
                name="amount-conversion"
                currency={config.fiat_currency.name}
                value={Amounts.stringifyValue(calc.beforeFee)}
              />
            </fieldset>

            <fieldset>
              <label form="cashout-fee">{i18n.str`Cashout fee`}</label>
              <InputAmount
                name="cashout-fee"
                currency={config.fiat_currency.name}
                value={Amounts.stringifyValue(sellFee)}
              />
            </fieldset>
          </Fragment>
        )}
        <fieldset>
          <label for="total"
            style={{ fontWeight: "bold", color: "green" }}
          >{i18n.str`Total cashout transfer`}</label>
          <InputAmount
            name="total"
            currency={config.fiat_currency.name}
            value={Amounts.stringifyValue(calc.credit)}
          />
        </fieldset>
        <fieldset>
          <label>{i18n.str`Confirmation channel`}</label>

          <div class="channel">
            <input
              class={
                "pure-button content " +
                (form.channel === TanChannel.EMAIL
                  ? "pure-button-primary"
                  : "pure-button-secondary")
              }
              type="submit"
              value={i18n.str`Email`}
              onClick={async (e) => {
                e.preventDefault();
                form.channel = TanChannel.EMAIL;
                updateForm(structuredClone(form));
              }}
            />
            <input
              class={
                "pure-button content " +
                (form.channel === TanChannel.SMS
                  ? "pure-button-primary"
                  : "pure-button-secondary")
              }
              type="submit"
              value={i18n.str`SMS`}
              onClick={async (e) => {
                e.preventDefault();
                form.channel = TanChannel.SMS;
                updateForm(structuredClone(form));
              }}
            />
            <input
              class={
                "pure-button content " +
                (form.channel === TanChannel.FILE
                  ? "pure-button-primary"
                  : "pure-button-secondary")
              }
              type="submit"
              value={i18n.str`FILE`}
              onClick={async (e) => {
                e.preventDefault();
                form.channel = TanChannel.FILE;
                updateForm(structuredClone(form));
              }}
            />
          </div>
          <ShowInputErrorLabel
            message={errors?.channel}
            isDirty={form.channel !== undefined}
          />
        </fieldset>
        <br />
        <div style={{ display: "flex", justifyContent: "space-between" }}>
          <button
            class="pure-button pure-button-secondary btn-cancel"
            onClick={(e) => {
              e.preventDefault();
              onCancel();
            }}
          >
            {i18n.str`Cancel`}
          </button>

          <button
            class="pure-button pure-button-primary btn-register"
            type="submit"
            disabled={!!errors}
            onClick={async (e) => {
              e.preventDefault();

              if (errors || !creds) return;
              await handleError(async () => {
                const resp = await api.createCashout(creds, {
                  amount_credit: Amounts.stringify(calc.credit),
                  amount_debit: Amounts.stringify(calc.debit),
                  subject: form.subject,
                  tan_channel: form.channel,
                });
                if (resp.type === "ok") {
                  mutate(() => true)// clean cashout list
                  onComplete(resp.body.cashout_id);
                } else {
                  switch (resp.case) {
                    case "incorrect-exchange-rate": return notify({
                      type: "error",
                      title: i18n.str`The exchange rate was incorrectly applied`,
                      description: resp.detail.hint as TranslatedString,
                      debug: resp.detail,
                    });
                    case "no-contact-info": return notify({
                      type: "error",
                      title: i18n.str`Need a contact data where to send the TAN`,
                      description: resp.detail.hint as TranslatedString,
                      debug: resp.detail,
                    });
                    case "no-enough-balance": return notify({
                      type: "error",
                      title: i18n.str`The account does not have sufficient funds`,
                      description: resp.detail.hint as TranslatedString,
                      debug: resp.detail,
                    });
                    case "account-not-found": return notify({
                      type: "error",
                      title: i18n.str`Account not found`,
                      description: resp.detail.hint as TranslatedString,
                      debug: resp.detail,
                    });
                    case "cashout-not-supported": return notify({
                      type: "error",
                      title: i18n.str`The bank does not support cashout`,
                      description: resp.detail.hint as TranslatedString,
                      debug: resp.detail,
                    });
                    // case "": return notify({
                    //   type: "error",
                    //   title: i18n.str`This user is not allowed to make a cashout`,
                    //   description: resp.detail.hint as TranslatedString,
                    //   debug: resp.detail,
                    // });
                    default: assertUnreachable(resp)
                  }
                }
              })
            }}
          >
            {i18n.str`Create`}
          </button>
        </div>
      </form>
    </div>
  );
}
