/*
 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 } from "@gnu-taler/taler-util";
import {
  ErrorType,
  useTranslationContext,
} from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact";
import { useEffect, useRef, useState } from "preact/hooks";
import { useBackendContext } from "../context/backend.js";
import { ErrorMessage } from "../context/pageState.js";
import { useCredentialsChecker } from "../hooks/backend.js";
import { bankUiSettings } from "../settings.js";
import { undefinedIfEmpty } from "../utils.js";
import { ErrorBannerFloat } from "./BankFrame.js";
import { USERNAME_REGEX } from "./RegistrationPage.js";
import { ShowInputErrorLabel } from "./ShowInputErrorLabel.js";

/**
 * Collect and submit login data.
 */
export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
  const backend = useBackendContext();
  const [username, setUsername] = useState<string | undefined>();
  const [password, setPassword] = useState<string | undefined>();
  const { i18n } = useTranslationContext();
  const testLogin = useCredentialsChecker();
  const [error, saveError] = useState<ErrorMessage | undefined>();
  const ref = useRef<HTMLInputElement>(null);
  useEffect(function focusInput() {
    ref.current?.focus();
  }, []);

  const errors = undefinedIfEmpty({
    username: !username
      ? i18n.str`Missing username`
      : !USERNAME_REGEX.test(username)
      ? i18n.str`Use letters and numbers only, and start with a lowercase letter`
      : undefined,
    password: !password ? i18n.str`Missing password` : undefined,
  });

  return (
    <Fragment>
      <h1 class="nav">{i18n.str`Welcome to ${bankUiSettings.bankName}!`}</h1>
      {error && (
        <ErrorBannerFloat error={error} onClear={() => saveError(undefined)} />
      )}
      <div class="login-div">
        <form
          class="login-form"
          noValidate
          onSubmit={(e) => {
            e.preventDefault();
          }}
          autoCapitalize="none"
          autoCorrect="off"
        >
          <div class="pure-form">
            <h2>{i18n.str`Please login!`}</h2>
            <p class="unameFieldLabel loginFieldLabel formFieldLabel">
              <label for="username">{i18n.str`Username:`}</label>
            </p>
            <input
              ref={ref}
              autoFocus
              type="text"
              name="username"
              id="username"
              value={username ?? ""}
              placeholder="Username"
              autocomplete="username"
              required
              onInput={(e): void => {
                setUsername(e.currentTarget.value);
              }}
            />
            <ShowInputErrorLabel
              message={errors?.username}
              isDirty={username !== undefined}
            />
            <p class="passFieldLabel loginFieldLabel formFieldLabel">
              <label for="password">{i18n.str`Password:`}</label>
            </p>
            <input
              type="password"
              name="password"
              id="password"
              autocomplete="current-password"
              value={password ?? ""}
              placeholder="Password"
              required
              onInput={(e): void => {
                setPassword(e.currentTarget.value);
              }}
            />
            <ShowInputErrorLabel
              message={errors?.password}
              isDirty={password !== undefined}
            />
            <br />
            <button
              type="submit"
              class="pure-button pure-button-primary"
              disabled={!!errors}
              onClick={async (e) => {
                e.preventDefault();
                if (!username || !password) return;
                const testResult = await testLogin(username, password);
                if (testResult.valid) {
                  backend.logIn({ username, password });
                } else {
                  if (testResult.requestError) {
                    const { cause } = testResult;
                    switch (cause.type) {
                      case ErrorType.CLIENT: {
                        if (cause.status === HttpStatusCode.Unauthorized) {
                          saveError({
                            title: i18n.str`Wrong credentials for "${username}"`,
                          });
                        }
                        if (cause.status === HttpStatusCode.NotFound) {
                          saveError({
                            title: i18n.str`Account not found`,
                          });
                        } else {
                          saveError({
                            title: i18n.str`Could not load due to a client error`,
                            description: cause.payload.error.description,
                            debug: JSON.stringify(cause.payload),
                          });
                        }
                        break;
                      }
                      case ErrorType.SERVER: {
                        saveError({
                          title: i18n.str`Server had a problem, try again later or report.`,
                          description: cause.payload.error.description,
                          debug: JSON.stringify(cause.payload),
                        });
                        break;
                      }
                      case ErrorType.TIMEOUT: {
                        saveError({
                          title: i18n.str`Request timeout, try again later.`,
                        });
                        break;
                      }
                      case ErrorType.UNREADABLE: {
                        saveError({
                          title: i18n.str`Unexpected error.`,
                          description: `Response from ${cause.info?.url} is unreadable, http status: ${cause.status}`,
                          debug: JSON.stringify(cause),
                        });
                        break;
                      }
                      default: {
                        saveError({
                          title: i18n.str`Unexpected error, please report.`,
                          description: `Diagnostic from ${cause.info?.url} is "${cause.message}"`,
                          debug: JSON.stringify(cause),
                        });
                        break;
                      }
                    }
                  } else {
                    saveError({
                      title: i18n.str`Unexpected error, please report.`,
                      debug: JSON.stringify(testResult.error),
                    });
                  }
                  backend.logOut();
                }
                setUsername(undefined);
                setPassword(undefined);
              }}
            >
              {i18n.str`Login`}
            </button>

            {bankUiSettings.allowRegistrations && onRegister ? (
              <button
                class="pure-button pure-button-secondary btn-cancel"
                onClick={(e) => {
                  e.preventDefault();
                  onRegister();
                }}
              >
                {i18n.str`Register`}
              </button>
            ) : (
              <div />
            )}
          </div>
        </form>
      </div>
    </Fragment>
  );
}
