/*
 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 {
  AmountJson,
  Amounts,
  HttpStatusCode,
  Logger,
  parseWithdrawUri,
} from "@gnu-taler/taler-util";
import {
  RequestError,
  useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { Ref, VNode, h } from "preact";
import { useEffect, useRef, useState } from "preact/hooks";
import { useAccessAPI } from "../hooks/access.js";
import { notifyError } from "../hooks/notification.js";
import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
import { forwardRef } from "preact/compat";

const logger = new Logger("WalletWithdrawForm");
const RefAmount = forwardRef(Amount);

export function WalletWithdrawForm({
  focus,
  limit,
  onSuccess,
}: {
  limit: AmountJson;
  focus?: boolean;
  onSuccess: (operationId: string) => void;
}): VNode {
  const { i18n } = useTranslationContext();
  const { createWithdrawal } = useAccessAPI();

  const [amountStr, setAmountStr] = useState<string | undefined>("5.00");
  const ref = useRef<HTMLInputElement>(null);
  useEffect(() => {
    if (focus) ref.current?.focus();
  }, [focus]);

  const trimmedAmountStr = amountStr?.trim();

  const parsedAmount = trimmedAmountStr
    ? Amounts.parse(`${limit.currency}:${trimmedAmountStr}`)
    : undefined;

  const errors = undefinedIfEmpty({
    amount:
      trimmedAmountStr == null
        ? i18n.str`required`
        : !parsedAmount
          ? i18n.str`invalid`
          : Amounts.cmp(limit, parsedAmount) === -1
            ? i18n.str`balance is not enough`
            : undefined,
  });

  return (
    <form
      id="reserve-form"
      class="pure-form"
      name="tform"
      onSubmit={(e) => {
        e.preventDefault();
      }}
      autoCapitalize="none"
      autoCorrect="off"
    >
      <p>
        <label for="withdraw-amount">{i18n.str`Amount to withdraw:`}</label>
        &nbsp;
        <RefAmount
          currency={limit.currency}
          value={amountStr}
          onChange={(v) => {
            setAmountStr(v);
          }}
          error={errors?.amount}
          ref={ref}
        />
      </p>
      <p>
        <div>
          <input
            id="select-exchange"
            class="pure-button pure-button-primary"
            type="submit"
            disabled={!!errors}
            value={i18n.str`Withdraw`}
            onClick={async (e) => {
              e.preventDefault();
              if (!parsedAmount) return;
              try {
                const result = await createWithdrawal({
                  amount: Amounts.stringify(parsedAmount),
                });
                const uri = parseWithdrawUri(result.data.taler_withdraw_uri);
                if (!uri) {
                  return notifyError({
                    title: i18n.str`Server responded with an invalid  withdraw URI`,
                    description: i18n.str`Withdraw URI: ${result.data.taler_withdraw_uri}`,
                  });
                } else {
                  onSuccess(uri.withdrawalOperationId);
                }
              } catch (error) {
                if (error instanceof RequestError) {
                  notifyError(
                    buildRequestErrorMessage(i18n, error.cause, {
                      onClientError: (status) =>
                        status === HttpStatusCode.Forbidden
                          ? i18n.str`The operation was rejected due to insufficient funds`
                          : undefined,
                    }),
                  );
                } else {
                  notifyError({
                    title: i18n.str`Operation failed, please report`,
                    description:
                      error instanceof Error
                        ? error.message
                        : JSON.stringify(error),
                  });
                }
              }
            }}
          />
        </div>
      </p>
    </form>
  );
}

export function Amount(
  {
    currency,
    value,
    error,
    onChange,
  }: {
    error?: string;
    currency: string;
    value: string | undefined;
    onChange?: (s: string) => void;
  },
  ref: Ref<HTMLInputElement>,
): VNode {
  return (
    <div style={{ width: "max-content" }}>
      <div>
        <input
          type="text"
          readonly
          class="currency-indicator"
          size={currency.length}
          maxLength={currency.length}
          tabIndex={-1}
          style={{
            borderTopRightRadius: 0,
            borderBottomRightRadius: 0,
            borderRight: 0,
          }}
          value={currency}
        />
        <input
          type="number"
          ref={ref}
          name="amount"
          id="amount"
          placeholder="0"
          style={{
            borderTopLeftRadius: 0,
            borderBottomLeftRadius: 0,
            borderLeft: 0,
            width: 150,
            color: "black",
          }}
          value={value ?? ""}
          disabled={!onChange}
          onInput={(e): void => {
            if (onChange) {
              onChange(e.currentTarget.value);
            }
          }}
        />
      </div>
      <ShowInputErrorLabel message={error} isDirty={value !== undefined} />
    </div>
  );
}
