/*
 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 {
  HttpStatusCode,
  Logger,
  WithdrawUriResult,
} from "@gnu-taler/taler-util";
import {
  RequestError,
  useTranslationContext,
} from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact";
import { useMemo, useState } from "preact/hooks";
import {
  ObservedStateType,
  PageStateType,
  notifyError,
} from "../context/pageState.js";
import { useAccessAnonAPI } from "../hooks/access.js";
import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
import { ShowInputErrorLabel } from "./ShowInputErrorLabel.js";

const logger = new Logger("WithdrawalConfirmationQuestion");

interface Props {
  onConfirmed: () => void;
  onAborted: () => void;
  withdrawUri: WithdrawUriResult;
}
/**
 * Additional authentication required to complete the operation.
 * Not providing a back button, only abort.
 */
export function WithdrawalConfirmationQuestion({
  onConfirmed,
  onAborted,
  withdrawUri,
}: Props): VNode {
  const { i18n } = useTranslationContext();

  const captchaNumbers = useMemo(() => {
    return {
      a: Math.floor(Math.random() * 10),
      b: Math.floor(Math.random() * 10),
    };
  }, []);

  const { confirmWithdrawal, abortWithdrawal } = useAccessAnonAPI();
  const [captchaAnswer, setCaptchaAnswer] = useState<string | undefined>();
  const answer = parseInt(captchaAnswer ?? "", 10);
  const errors = undefinedIfEmpty({
    answer: !captchaAnswer
      ? i18n.str`Answer the question before continue`
      : Number.isNaN(answer)
      ? i18n.str`The answer should be a number`
      : answer !== captchaNumbers.a + captchaNumbers.b
      ? i18n.str`The answer "${answer}" to "${captchaNumbers.a} + ${captchaNumbers.b}" is wrong.`
      : undefined,
  });
  return (
    <Fragment>
      <h1 class="nav">{i18n.str`Confirm Withdrawal`}</h1>
      <article>
        <div class="challenge-div">
          <form
            class="challenge-form"
            noValidate
            onSubmit={(e) => {
              e.preventDefault();
            }}
            autoCapitalize="none"
            autoCorrect="off"
          >
            <div class="pure-form" id="captcha" name="capcha-form">
              <h2>{i18n.str`Authorize withdrawal by solving challenge`}</h2>
              <p>
                <label for="answer">
                  {i18n.str`What is`}&nbsp;
                  <em>
                    {captchaNumbers.a}&nbsp;+&nbsp;{captchaNumbers.b}
                  </em>
                  ?&nbsp;
                </label>
                &nbsp;
                <input
                  name="answer"
                  id="answer"
                  value={captchaAnswer ?? ""}
                  type="text"
                  autoFocus
                  required
                  onInput={(e): void => {
                    setCaptchaAnswer(e.currentTarget.value);
                  }}
                />
                <ShowInputErrorLabel
                  message={errors?.answer}
                  isDirty={captchaAnswer !== undefined}
                />
              </p>
              <p>
                <button
                  type="submit"
                  class="pure-button pure-button-primary btn-confirm"
                  disabled={!!errors}
                  onClick={async (e) => {
                    e.preventDefault();
                    try {
                      await confirmWithdrawal(
                        withdrawUri.withdrawalOperationId,
                      );
                      onConfirmed();
                    } catch (error) {
                      if (error instanceof RequestError) {
                        notifyError(
                          buildRequestErrorMessage(i18n, error.cause, {
                            onClientError: (status) =>
                              status === HttpStatusCode.Conflict
                                ? i18n.str`The withdrawal has been aborted previously and can't be confirmed`
                                : status === HttpStatusCode.UnprocessableEntity
                                ? i18n.str`The withdraw operation cannot be confirmed because no exchange and reserve public key selection happened before`
                                : undefined,
                          }),
                        );
                      } else {
                        notifyError({
                          title: i18n.str`Operation failed, please report`,
                          description:
                            error instanceof Error
                              ? error.message
                              : JSON.stringify(error),
                        });
                      }
                    }
                  }}
                >
                  {i18n.str`Confirm`}
                </button>
                &nbsp;
                <button
                  class="pure-button pure-button-secondary btn-cancel"
                  onClick={async (e) => {
                    e.preventDefault();
                    try {
                      await abortWithdrawal(withdrawUri.withdrawalOperationId);
                      onAborted();
                    } catch (error) {
                      if (error instanceof RequestError) {
                        notifyError(
                          buildRequestErrorMessage(i18n, error.cause, {
                            onClientError: (status) =>
                              status === HttpStatusCode.Conflict
                                ? i18n.str`The reserve operation has been confirmed previously and can't be aborted`
                                : undefined,
                          }),
                        );
                      } else {
                        notifyError({
                          title: i18n.str`Operation failed, please report`,
                          description:
                            error instanceof Error
                              ? error.message
                              : JSON.stringify(error),
                        });
                      }
                    }
                  }}
                >
                  {i18n.str`Cancel`}
                </button>
              </p>
            </div>
          </form>
          <div class="hint">
            <p>
              <i18n.Translate>
                A this point, a <b>real</b> bank would ask for an additional
                authentication proof (PIN/TAN, one time password, ..), instead
                of a simple calculation.
              </i18n.Translate>
            </p>
          </div>
        </div>
      </article>
    </Fragment>
  );
}
