/*
 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, TranslatedString } from "@gnu-taler/taler-util";
import {
  RequestError,
  notify,
  notifyError,
  useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
import { useState } from "preact/hooks";
import { useBackendContext } from "../context/backend.js";
import { useTestingAPI } from "../hooks/access.js";
import { bankUiSettings } from "../settings.js";
import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
import { getRandomPassword, getRandomUsername } from "./rnd.js";

const logger = new Logger("RegistrationPage");

export function RegistrationPage({
  onComplete,
}: {
  onComplete: () => void;
}): VNode {
  const { i18n } = useTranslationContext();
  if (!bankUiSettings.allowRegistrations) {
    return (
      <p>{i18n.str`Currently, the bank is not accepting new registrations!`}</p>
    );
  }
  return <RegistrationForm onComplete={onComplete} />;
}

export const USERNAME_REGEX = /^[a-z][a-zA-Z0-9-]*$/;

/**
 * Collect and submit registration data.
 */
function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
  const backend = useBackendContext();
  const [username, setUsername] = useState<string | undefined>();
  const [password, setPassword] = useState<string | undefined>();
  const [repeatPassword, setRepeatPassword] = useState<string | undefined>();

  const { register } = useTestingAPI();
  const { i18n } = useTranslationContext();

  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,
    repeatPassword: !repeatPassword
      ? i18n.str`Missing password`
      : repeatPassword !== password
        ? i18n.str`Passwords don't match`
        : undefined,
  });

  async function doRegistrationStep() {
    if (!username || !password) return;
    try {
      await register({ username, password });
      setUsername(undefined);
      backend.logIn({ username, password });
      onComplete();
    } catch (error) {
      if (error instanceof RequestError) {
        notify(
          buildRequestErrorMessage(i18n, error.cause, {
            onClientError: (status) =>
              status === HttpStatusCode.Conflict
                ? i18n.str`That username is already taken`
                : undefined,
          }),
        );
      } else {
        notifyError(
          i18n.str`Operation failed, please report`,
          (error instanceof Error
            ? error.message
            : JSON.stringify(error)) as TranslatedString
        )
}
    }
    setPassword(undefined);
    setRepeatPassword(undefined);
}

  async function delay(ms: number):Promise<void> {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(undefined);
      }, ms)
    })
  }
  async function doRandomRegistration(tries: number = 3) {
    const user = getRandomUsername();
    const pass = getRandomPassword();
    try {
      setUsername(undefined);
      setPassword(undefined);
      setRepeatPassword(undefined);
      await register({ username: user, password: pass });
      backend.logIn({ username: user, password: pass });
      onComplete();
    } catch (error) {
      if (error instanceof RequestError) {
        if (tries > 0) {
          await delay(200)
          await doRandomRegistration(tries-1)
        } else {
          notify(
            buildRequestErrorMessage(i18n, error.cause, {
              onClientError: (status) =>
                status === HttpStatusCode.Conflict
                  ? i18n.str`Could not create a random user`
                  : undefined,
            }),
          );
        }
      } else {
        notifyError(
          i18n.str`Operation failed, please report`,
          (error instanceof Error
            ? error.message
            : JSON.stringify(error)) as TranslatedString
        )
}
    }
  }

  return (
    <Fragment>
      <h1 class="nav"></h1>

      <div class="flex min-h-full flex-col justify-center">
        <div class="sm:mx-auto sm:w-full sm:max-w-sm">
          <h2 class="text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">{i18n.str`Sign up!`}</h2>
        </div>

        <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
                  autoFocus
                  type="text"
                  name="username"
                  id="username"
                  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"
                  value={username ?? ""}
                  enterkeyhint="next"
                  placeholder="identification"
                  autocomplete="username"
                  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">Password</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"
                  required
                  onInput={(e): void => {
                    setPassword(e.currentTarget.value);
                  }}
                />
                <ShowInputErrorLabel
                  message={errors?.password}
                  isDirty={password !== undefined}
                />
              </div>
            </div>

            <div>
              <div class="flex items-center justify-between">
                <label for="register-repeat" class="block text-sm font-medium leading-6 text-gray-900">Repeat assword</label>
              </div>
              <div class="mt-2">
                <input
                  type="password"
                  name="register-repeat"
                  id="register-repeat"
                  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={repeatPassword ?? ""}
                  placeholder="Same password"
                  required
                  onInput={(e): void => {
                    setRepeatPassword(e.currentTarget.value);
                  }}
                />
                <ShowInputErrorLabel
                  message={errors?.repeatPassword}
                  isDirty={repeatPassword !== undefined}
                />
              </div>
            </div>

            <div>
              <button type="submit"
                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()
                  doRegistrationStep()
                }}
              >
                <i18n.Translate>Confirm</i18n.Translate>
              </button>
            </div>

          </form>

          <p class="mt-10 text-center text-sm text-gray-500 border-t">
            <button type="submit"
              class="flex mt-4 w-full justify-center rounded-md bg-green-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600"
              onClick={(e) => {
                e.preventDefault()
                doRandomRegistration()
              }}
            >
              <i18n.Translate>Create a random user</i18n.Translate>
            </button>
          </p>
        </div>
      </div>

    </Fragment>
  );
}

export function assertUnreachable(x: never): never {
  throw new Error("Didn't expect to get here");
}
