/*
 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 {
  LocalNotificationBanner,
  RouteDefinition,
  ShowInputErrorLabel,
  useBankCoreApiContext,
  useLocalNotification,
  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 { undefinedIfEmpty } from "../utils.js";
import { getRandomPassword, getRandomUsername } from "./rnd.js";

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.
 */
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, , handleError] = useLocalNotification();
  const settings = useSettingsContext();

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

  const errors = undefinedIfEmpty({
    name: !name ? i18n.str`Missing name` : undefined,
    username: !username
      ? i18n.str`Missing username`
      : !USERNAME_REGEX.test(username)
        ? i18n.str`Use letters, numbers or any of these characters: - . _ ~`
        : undefined,
    // phone: !phone
    //   ? undefined
    //   : !PHONE_REGEX.test(phone)
    //     ? i18n.str`Use letters and numbers only, and start with a lowercase letter`
    //     : undefined,
    // email: !email
    //   ? undefined
    //   : !EMAIL_REGEX.test(email)
    //     ? 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 doRegistrationAndLogin(
    name: string,
    username: string,
    password: string,
    onComplete: () => void,
  ) {
    await handleError(async (onError) => {
      const resp = await api.createAccount(undefined, {
        name,
        username,
        password,
      });
      if (resp.type === "ok") {
        onComplete();
      } else {
        onError(resp, (_case) => {
          switch (_case) {
            case HttpStatusCode.BadRequest:
              return i18n.str`Server replied with invalid phone or email.`;
            case HttpStatusCode.Unauthorized:
              return i18n.str`No enough permission to create that 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 admin is allow to set debt limit.`;
            case TalerErrorCode.BANK_NON_ADMIN_SET_TAN_CHANNEL:
              return i18n.str`Only admin can create accounts with second factor authentication.`;
          }
        });
      }
    });
  }

  async function doRegistrationStep() {
    if (!username || !password || !name) return;
    await doRegistrationAndLogin(name, username, password, () => {
      setUsername(undefined);
      setPassword(undefined);
      setRepeatPassword(undefined);
      onRegistrationSuccesful(username, password);
    });
  }

  async function doRandomRegistration() {
    const user = getRandomUsername();

    const password = settings.simplePasswordForRandomAccounts
      ? "123"
      : getRandomPassword();
    const username = `_${user.first}-${user.second}_`;
    const name = `${capitalizeFirstLetter(user.first)} ${capitalizeFirstLetter(
      user.second,
    )}`;
    await doRegistrationAndLogin(name, username, password, () => {
      onRegistrationSuccesful(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 style={{ color: "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 style={{ color: "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>
            </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 style={{ color: "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 style={{ color: "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);
                  }}
                />
                {/* <ShowInputErrorLabel
                  message={errors?.name}
                  isDirty={name !== undefined}
                /> */}
              </div>
            </div>

            {/* <div>
              <label for="phone" class="block text-sm font-medium leading-6 text-gray-900">
                <i18n.Translate>Phone</i18n.Translate>
              </label>
              <div class="mt-2">
                <input
                  autoFocus
                  type="text"
                  name="phone"
                  id="phone"
                  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={phone ?? ""}
                  enterkeyhint="next"
                  placeholder="your phone"
                  autocomplete="none"
                  onInput={(e): void => {
                    setPhone(e.currentTarget.value);
                  }}
                />
                <ShowInputErrorLabel
                  message={errors?.phone}
                  isDirty={phone !== undefined}
                />
              </div>
            </div>
            <div>
              <label for="email" class="block text-sm font-medium leading-6 text-gray-900">
                <i18n.Translate>Email</i18n.Translate>
              </label>
              <div class="mt-2">
                <input
                  autoFocus
                  type="text"
                  name="email"
                  id="email"
                  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={email ?? ""}
                  enterkeyhint="next"
                  placeholder="your email"
                  autocomplete="email"
                  onInput={(e): void => {
                    setEmail(e.currentTarget.value);
                  }}
                />
                <ShowInputErrorLabel
                  message={errors?.email}
                  isDirty={email !== undefined}
                />
              </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>
              <button
                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"
                disabled={!!errors}
                onClick={async (e) => {
                  e.preventDefault();

                  doRegistrationStep();
                }}
              >
                <i18n.Translate>Register</i18n.Translate>
              </button>
            </div>
          </form>

          {settings.allowRandomAccountCreation && (
            <p class="mt-10 text-center text-sm text-gray-500 border-t">
              <button
                type="submit"
                name="create random"
                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 temporary user</i18n.Translate>
              </button>
            </p>
          )}
        </div>
      </div>
    </Fragment>
  );
}

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