/*
 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, TalerErrorCode } from "@gnu-taler/taler-util";
import {
  ButtonBetter,
  LocalNotificationBanner,
  RouteDefinition,
  ShowInputErrorLabel,
  useBankCoreApiContext,
  useLocalNotificationBetter,
  useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
import { useState } from "preact/hooks";
import { useSettingsContext } from "../context/settings.js";
import { usePreferences } from "../hooks/preferences.js";
import { undefinedIfEmpty } from "../utils.js";
import { getRandomPassword, getRandomUsername } from "./rnd.js";
import { TalerCorebankApi } from "@gnu-taler/taler-util";

const TALER_SCREEN_ID = 110;

RegistrationPage.SCREEN_ID = TALER_SCREEN_ID;
export function RegistrationPage({
  onRegistrationSuccesful,
  routeCancel,
}: {
  onRegistrationSuccesful: (user: string, password: string) => void;
  routeCancel: RouteDefinition;
}): VNode {
  const { i18n } = useTranslationContext();
  const { config } = useBankCoreApiContext();
  if (!config.allow_registrations) {
    return (
      <p>{i18n.str`Currently, the bank is not accepting new registrations!`}</p>
    );
  }
  return (
    <RegistrationForm
      onRegistrationSuccesful={onRegistrationSuccesful}
      routeCancel={routeCancel}
    />
  );
}

// eslint-disable-next-line no-useless-escape
export const USERNAME_REGEX = /^[a-zA-Z0-9\-\.\_\~]*$/;
// export const PHONE_REGEX = /^(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4}$/;
// export const EMAIL_REGEX = /^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$/;

/**
 * Collect and submit registration data.
 */
RegistrationForm.SCREEN_ID = TALER_SCREEN_ID;
function RegistrationForm({
  onRegistrationSuccesful,
  routeCancel,
}: {
  onRegistrationSuccesful: (user: string, password: string) => void;
  routeCancel: RouteDefinition;
}): VNode {
  const [username, setUsername] = useState<string | undefined>();
  const [name, setName] = useState<string | undefined>();
  const [password, setPassword] = useState<string | undefined>();
  // const [phone, setPhone] = useState<string | undefined>();
  // const [email, setEmail] = useState<string | undefined>();
  const [repeatPassword, setRepeatPassword] = useState<string | undefined>();
  const [notification, safeFunctionHandler] = useLocalNotificationBetter();
  const settings = useSettingsContext();
  const [pref] = usePreferences();

  const {
    lib: { bank: api },
  } = useBankCoreApiContext();
  // const { register } = useTestingAPI();
  const { i18n } = useTranslationContext();

  const errors = undefinedIfEmpty({
    name: !name ? i18n.str`The name is missing` : undefined,
    username: !username
      ? i18n.str`Missing username`
      : !USERNAME_REGEX.test(username)
        ? i18n.str`Use letters, numbers or any of these characters: - . _ ~`
        : undefined,
    password: !password
      ? i18n.str`Missing password`
      : password.length < 8
        ? i18n.str`The password should be longer than 8 letters`
        : undefined,
    repeatPassword: !repeatPassword
      ? i18n.str`Missing password`
      : repeatPassword !== password
        ? i18n.str`The passwords do not match`
        : undefined,
  });

  const reg: TalerCorebankApi.RegisterAccountRequest | undefined =
    !name || !username || !password || !!errors
      ? undefined
      : {
          name,
          username,
          password,
        };

  const register = safeFunctionHandler(
    (account: TalerCorebankApi.RegisterAccountRequest) =>
      api.createAccount(undefined, account),
    !!errors || !reg ? undefined : [reg],
  );

  register.onSuccess = (success, acc) => {
    setUsername(undefined);
    setPassword(undefined);
    setRepeatPassword(undefined);
    setName(undefined)
    onRegistrationSuccesful(acc.username, acc.password);
  };

  register.onFail = (fail) => {
    switch (fail.case) {
      case HttpStatusCode.BadRequest:
        return i18n.str`Server replied with invalid phone or email.`;
      case HttpStatusCode.Unauthorized:
        return i18n.str`You are not authorised to create this account.`;
      case TalerErrorCode.BANK_UNALLOWED_DEBIT:
        return i18n.str`Registration is disabled because the bank ran out of bonus credit.`;
      case TalerErrorCode.BANK_RESERVED_USERNAME_CONFLICT:
        return i18n.str`That username can't be used because is reserved.`;
      case TalerErrorCode.BANK_REGISTER_USERNAME_REUSE:
        return i18n.str`That username is already taken.`;
      case TalerErrorCode.BANK_REGISTER_PAYTO_URI_REUSE:
        return i18n.str`That account ID is already taken.`;
      case TalerErrorCode.BANK_MISSING_TAN_INFO:
        return i18n.str`No information for the selected authentication channel.`;
      case TalerErrorCode.BANK_TAN_CHANNEL_NOT_SUPPORTED:
        return i18n.str`Authentication channel is not supported.`;
      case TalerErrorCode.BANK_NON_ADMIN_PATCH_DEBT_LIMIT:
        return i18n.str`Only an administrator is allowed to set the debt limit.`;
      case TalerErrorCode.BANK_NON_ADMIN_SET_CONVERSION_RATE_CLASS:
        return i18n.str`Only the administrator can change the conversion rate.`;
      case TalerErrorCode.BANK_CONVERSION_RATE_CLASS_UNKNOWN:
        return i18n.str`The conversion rate class doesn't exist.`;
      case TalerErrorCode.BANK_NON_ADMIN_SET_TAN_CHANNEL:
        return i18n.str`Only admin can create accounts with second factor authentication.`;
      case TalerErrorCode.BANK_PASSWORD_TOO_SHORT:
        return i18n.str`The password is too short. Can't have less than 8 characters.`;
      case TalerErrorCode.BANK_PASSWORD_TOO_LONG:
        return i18n.str`The password is too long. Can't have more than 64 characters.`;
    }
  };

  const registerRandom = register.lambda(() => {
    const user = getRandomUsername();

    const password = "12345678";
    const username = `_${user.first}-${user.second}_`;
    const name = `${capitalizeFirstLetter(user.first)} ${capitalizeFirstLetter(
      user.second,
    )}`;
    return [{ name, username, password }];
  }, []);

  return (
    <Fragment>
      <LocalNotificationBanner notification={notification} />

      <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`Account registration`}</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>Login username</i18n.Translate>
                <b class="text-[red]"> *</b>
              </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="account identification to login"
                  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"
                >
                  <i18n.Translate>Password</i18n.Translate>
                  <b class="text-[red]"> *</b>
                </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>
              <p class="mt-2 text-sm text-gray-500">
                <i18n.Translate>
                  Use a strong password: 8 characters minimum, don't use any
                  public information related to you (names, birthday, phone
                  number, etc...) and mix lowercase, uppercase, symbols and
                  numbers
                </i18n.Translate>
              </p>
            </div>

            <div>
              <div class="flex items-center justify-between">
                <label
                  for="register-repeat"
                  class="block text-sm font-medium leading-6 text-gray-900"
                >
                  <i18n.Translate>Repeat password</i18n.Translate>
                  <b class="text-[red]"> *</b>
                </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>
              <div class="flex items-center justify-between">
                <label
                  for="name"
                  class="block text-sm font-medium leading-6 text-gray-900"
                >
                  <i18n.Translate>Full name</i18n.Translate>
                  <b class="text-[red]"> *</b>
                </label>
              </div>
              <div class="mt-2">
                <input
                  autoFocus
                  type="text"
                  name="name"
                  id="name"
                  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={name ?? ""}
                  enterkeyhint="next"
                  placeholder="John Doe"
                  autocomplete="name"
                  required
                  onInput={(e): void => {
                    setName(e.currentTarget.value);
                  }}
                />
              </div>
            </div>

            <div class="flex w-full justify-between">
              <a
                name="cancel"
                href={routeCancel.url({})}
                class="ring-1 ring-gray-600 rounded-md bg-white disabled:bg-gray-300 px-3 py-1.5 text-sm font-semibold leading-6 text-black shadow-sm hover:bg-white-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2"
              >
                <i18n.Translate>Cancel</i18n.Translate>
              </a>
              <ButtonBetter
                type="submit"
                name="register"
                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"
                onClick={register}
              >
                <i18n.Translate>Register</i18n.Translate>
              </ButtonBetter>
            </div>
          </form>

          {settings.allowRandomAccountCreation && (
            <p class="mt-10 text-center text-sm text-gray-500 border-t">
              <ButtonBetter
                type="submit"
                name="create random"
                class="flex mt-4 w-full disabled:bg-gray-300 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={registerRandom}
              >
                <i18n.Translate>Create a random temporary user</i18n.Translate>
              </ButtonBetter>
            </p>
          )}
        </div>
      </div>
    </Fragment>
  );
}

function capitalizeFirstLetter(str: string) {
  return str.charAt(0).toUpperCase() + str.slice(1);
}
