/*
 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 {
  AbsoluteTime,
  HttpStatusCode,
  TalerCorebankApi,
  TalerErrorCode,
  TranslatedString,
  assertUnreachable,
} from "@gnu-taler/taler-util";
import {
  Attention,
  LocalNotificationBanner,
  notifyInfo,
  useLocalNotification,
  useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
import { useState } from "preact/hooks";
import { useBankCoreApiContext } from "@gnu-taler/web-util/browser";
import { useSessionState } from "../../hooks/session.js";
import { RouteDefinition } from "@gnu-taler/web-util/browser";
import { AccountForm } from "./AccountForm.js";

const TALER_SCREEN_ID = 123;

export function CreateNewAccount({
  routeCancel,
  onCreateSuccess,
}: {
  routeCancel: RouteDefinition;
  onCreateSuccess: () => void;
}): VNode {
  const { i18n } = useTranslationContext();
  const { state: credentials } = useSessionState();
  const token =
    credentials.status !== "loggedIn" ? undefined : credentials.token;
  const {
    lib: { bank: api },
  } = useBankCoreApiContext();

  const [submitAccount, setSubmitAccount] = useState<
    TalerCorebankApi.RegisterAccountRequest | undefined
  >();
  const [notification, notify, handleError] = useLocalNotification();

  async function doCreate() {
    if (!submitAccount || !token) return;
    await handleError(async () => {
      const resp = await api.createAccount(token, submitAccount);
      if (resp.type === "ok") {
        notifyInfo(
          i18n.str`Account created with password "${submitAccount.password}".`,
        );
        onCreateSuccess();
      } else {
        switch (resp.case) {
          case HttpStatusCode.BadRequest:
            return notify({
              type: "error",
              title: i18n.str`Server replied that phone or email is invalid`,
              description: resp.detail?.hint as TranslatedString,
              debug: resp.detail,
              when: AbsoluteTime.now(),
            });
          case HttpStatusCode.Unauthorized:
            return notify({
              type: "error",
              title: i18n.str`The rights to perform the operation are not sufficient`,
              description: resp.detail?.hint as TranslatedString,
              debug: resp.detail,
              when: AbsoluteTime.now(),
            });
          case TalerErrorCode.BANK_REGISTER_USERNAME_REUSE:
            return notify({
              type: "error",
              title: i18n.str`Account username is already taken`,
              description: resp.detail?.hint as TranslatedString,
              debug: resp.detail,
              when: AbsoluteTime.now(),
            });
          case TalerErrorCode.BANK_REGISTER_PAYTO_URI_REUSE:
            return notify({
              type: "error",
              title: i18n.str`Account ID is already taken`,
              description: resp.detail?.hint as TranslatedString,
              debug: resp.detail,
              when: AbsoluteTime.now(),
            });
          case TalerErrorCode.BANK_UNALLOWED_DEBIT:
            return notify({
              type: "error",
              title: i18n.str`Bank ran out of bonus credit.`,
              description: resp.detail?.hint as TranslatedString,
              debug: resp.detail,
              when: AbsoluteTime.now(),
            });
          case TalerErrorCode.BANK_RESERVED_USERNAME_CONFLICT:
            return notify({
              type: "error",
              title: i18n.str`Account username can't be used because is reserved`,
              description: resp.detail?.hint as TranslatedString,
              debug: resp.detail,
              when: AbsoluteTime.now(),
            });
          case TalerErrorCode.BANK_NON_ADMIN_PATCH_DEBT_LIMIT:
            return notify({
              type: "error",
              title: i18n.str`Only an administrator is allowed to set the debt limit.`,
              description: resp.detail?.hint as TranslatedString,
              debug: resp.detail,
              when: AbsoluteTime.now(),
            });
          case TalerErrorCode.BANK_MISSING_TAN_INFO:
            return notify({
              type: "error",
              title: i18n.str`No information for the selected authentication channel.`,
              description: resp.detail?.hint as TranslatedString,
              debug: resp.detail,
              when: AbsoluteTime.now(),
            });
          case TalerErrorCode.BANK_TAN_CHANNEL_NOT_SUPPORTED:
            return notify({
              type: "error",
              title: i18n.str`Authentication channel is not supported.`,
              description: resp.detail?.hint as TranslatedString,
              debug: resp.detail,
              when: AbsoluteTime.now(),
            });
          case TalerErrorCode.BANK_NON_ADMIN_SET_TAN_CHANNEL:
            return notify({
              type: "error",
              title: i18n.str`Only admin can create accounts with second factor authentication.`,
              description: resp.detail?.hint as TranslatedString,
              debug: resp.detail,
              when: AbsoluteTime.now(),
            });
          case TalerErrorCode.BANK_NON_ADMIN_SET_CONVERSION_RATE_CLASS: {
            return notify({
              type: "error",
              title: i18n.str`Only the administrator can change the conversion rate.`,
              description: resp.detail?.hint as TranslatedString,
              debug: resp.detail,
              when: AbsoluteTime.now(),
            });
          }
          case TalerErrorCode.BANK_CONVERSION_RATE_CLASS_UNKNOWN: {
            return notify({
              type: "error",
              title: i18n.str`The conversion rate class doesn't exist.`,
              description: resp.detail?.hint as TranslatedString,
              debug: resp.detail,
              when: AbsoluteTime.now(),
            });
          }
          case TalerErrorCode.BANK_PASSWORD_TOO_SHORT: {
            return notify({
              type: "error",
              title: i18n.str`The password is too short. Can't have less than 8 characters.`,
              description: resp.detail?.hint as TranslatedString,
              debug: resp.detail,
              when: AbsoluteTime.now(),
            });
          }
          case TalerErrorCode.BANK_PASSWORD_TOO_LONG: {
            return notify({
              type: "error",
              title: i18n.str`The password is too long. Can't have more than 64 characters.`,
              description: resp.detail?.hint as TranslatedString,
              debug: resp.detail,
              when: AbsoluteTime.now(),
            });
          }
          default:
            assertUnreachable(resp);
        }
      }
    });
  }

  if (!(credentials.status === "loggedIn" && credentials.isUserAdministrator)) {
    return (
      <Fragment>
        <Attention type="warning" title={i18n.str`Can't create accounts`}>
          <i18n.Translate>
            Only system admin can create accounts.
          </i18n.Translate>
        </Attention>
        <div class="mt-5 sm:mt-6">
          <a
            href={routeCancel.url({})}
            name="close"
            class="inline-flex w-full justify-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold 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"
          >
            <i18n.Translate>Close</i18n.Translate>
          </a>
        </div>
      </Fragment>
    );
  }

  return (
    <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-6 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
      <LocalNotificationBanner notification={notification} />

      <div class="px-4 sm:px-0">
        <h2 class="text-base font-semibold leading-7 text-gray-900">
          <i18n.Translate>New bank account</i18n.Translate>
        </h2>
      </div>
      <AccountForm
        template={undefined}
        purpose="create"
        onChange={(a) => {
          setSubmitAccount(a);
        }}
      >
        <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
          <a
            href={routeCancel.url({})}
            name="cancel"
            class="text-sm font-semibold leading-6 text-gray-900"
          >
            <i18n.Translate>Cancel</i18n.Translate>
          </a>
          <button
            type="submit"
            name="create"
            class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold 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={!submitAccount}
            onClick={(e) => {
              e.preventDefault();
              doCreate();
            }}
          >
            <i18n.Translate>Create</i18n.Translate>
          </button>
        </div>
      </AccountForm>
    </div>
  );
}
