/*
 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 {
  HttpStatusCode,
  TranslatedString,
  assertUnreachable,
} from "@gnu-taler/taler-util";
import {
  LocalNotificationBanner,
  ShowInputErrorLabel,
  useLocalNotification,
  useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { VNode, h } from "preact";
import { useEffect, useRef, useState } from "preact/hooks";
import { useBankCoreApiContext } from "../context/config.js";
import { useBackendState } from "../hooks/backend.js";
import { undefinedIfEmpty } from "../utils.js";
import { doAutoFocus } from "./PaytoWireTransferForm.js";
import { EmptyObject, RouteDefinition } from "../route.js";

/**
 * Collect and submit login data.
 */
export function LoginForm({
  currentUser,
  fixedUser,
  routeRegister,
}: {
  fixedUser?: boolean;
  currentUser?: string;
  routeRegister?: RouteDefinition;
}): VNode {
  const backend = useBackendState();

  const sessionUser =
    backend.state.status !== "loggedOut" ? backend.state.username : undefined;
  const [username, setUsername] = useState<string | undefined>(
    currentUser ?? sessionUser,
  );
  const [password, setPassword] = useState<string | undefined>();
  const { i18n } = useTranslationContext();
  const { api } = useBankCoreApiContext();
  const [notification, notify, handleError] = useLocalNotification();
  const { config } = useBankCoreApiContext();

  const ref = useRef<HTMLInputElement>(null);
  useEffect(function focusInput() {
    ref.current?.focus();
  }, []);

  const [busy, setBusy] = useState<Record<string, undefined>>();

  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,
    }) ?? busy;

  async function doLogout() {
    backend.logOut();
  }

  async function doLogin() {
    if (!username || !password) return;
    setBusy({});
    await handleError(async () => {
      const resp = await api
        .getAuthenticationAPI(username)
        .createAccessToken(password, {
          // scope: "readwrite" as "write", // FIX: different than merchant
          scope: "readwrite",
          duration: { d_us: "forever" },
          refreshable: true,
        });
      if (resp.type === "ok") {
        backend.logIn({ username, token: resp.body.access_token });
      } else {
        switch (resp.case) {
          case HttpStatusCode.Unauthorized:
            return notify({
              type: "error",
              title: i18n.str`Wrong credentials for "${username}"`,
              description: resp.detail.hint as TranslatedString,
              debug: resp.detail,
            });
          case HttpStatusCode.NotFound:
            return notify({
              type: "error",
              title: i18n.str`Account not found`,
              description: resp.detail.hint as TranslatedString,
              debug: resp.detail,
            });
          default:
            assertUnreachable(resp);
        }
      }
    });
    setPassword(undefined);
    setBusy(undefined);
  }

  return (
    <div class="flex min-h-full flex-col justify-center ">
      <LocalNotificationBanner notification={notification} />
      <div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
        <form
          class="space-y-6"
          noValidate
          onSubmit={(e) => {
            e.preventDefault();
          }}
          autoCapitalize="none"
          autoCorrect="off"
        >
          <div>
            <label
              for="username"
              class="block text-sm font-medium leading-6 text-gray-900"
            >
              <i18n.Translate>Username</i18n.Translate>
            </label>
            <div class="mt-2">
              <input
                ref={doAutoFocus}
                type="text"
                name="username"
                id="username"
                class="block w-full disabled:bg-gray-200 rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
                value={username ?? ""}
                disabled={fixedUser}
                enterkeyhint="next"
                placeholder="identification"
                autocomplete="username"
                title={i18n.str`Username of the account`}
                required
                onInput={(e): void => {
                  setUsername(e.currentTarget.value);
                }}
              />
              <ShowInputErrorLabel
                message={errors?.username}
                isDirty={username !== undefined}
              />
            </div>
          </div>

          <div>
            <div class="flex items-center justify-between">
              <label
                for="password"
                class="block text-sm font-medium leading-6 text-gray-900"
              >
                <i18n.Translate>Password</i18n.Translate>
              </label>
            </div>
            <div class="mt-2">
              <input
                type="password"
                name="password"
                id="password"
                autocomplete="current-password"
                class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
                enterkeyhint="send"
                value={password ?? ""}
                placeholder="Password"
                title={i18n.str`Password of the account`}
                required
                onInput={(e): void => {
                  setPassword(e.currentTarget.value);
                }}
              />
              <ShowInputErrorLabel
                message={errors?.password}
                isDirty={password !== undefined}
              />
            </div>
          </div>

          {backend.state.status !== "loggedOut" ? (
            <div class="flex justify-between">
              <button
                type="submit"
                name="cancel"
                class="rounded-md bg-white-600 px-3 py-1.5 text-sm font-semibold leading-6 text-black shadow-sm hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-600"
                onClick={(e) => {
                  e.preventDefault();
                  doLogout();
                }}
              >
                <i18n.Translate>Cancel</i18n.Translate>
              </button>

              <button
                type="submit"
                name="check"
                class="rounded-md bg-indigo-600 disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 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"
                disabled={!!errors}
                onClick={async (e) => {
                  e.preventDefault();
                  doLogin();
                }}
              >
                <i18n.Translate>Check</i18n.Translate>
              </button>
            </div>
          ) : (
            <div>
              <button
                type="submit"
                name="login"
                class="flex w-full justify-center rounded-md bg-indigo-600 disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 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"
                disabled={!!errors}
                onClick={(e) => {
                  e.preventDefault();
                  doLogin();
                }}
              >
                <i18n.Translate>Log in</i18n.Translate>
              </button>
            </div>
          )}
        </form>

        {config.allow_registrations && routeRegister && (
          <a
            name="register"
            href={routeRegister.url({})}
            class="flex justify-center border-t mt-4 rounded-md bg-blue-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-blue-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
          >
            <i18n.Translate>Register</i18n.Translate>
          </a>
        )}
      </div>
    </div>
  );
}
