/*
 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,
  TalerErrorCode,
  TranslatedString,
  assertUnreachable,
} from "@gnu-taler/taler-util";
import {
  LocalNotificationBanner,
  ShowInputErrorLabel,
  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 { useBankState } from "../../hooks/bank-state.js";
import { RouteDefinition } from "@gnu-taler/web-util/browser";
import { undefinedIfEmpty } from "../../utils.js";
import { doAutoFocus } from "../PaytoWireTransferForm.js";
import { ProfileNavigation } from "../ProfileNavigation.js";

export function UpdateAccountPassword({
  account: accountName,
  routeClose,
  onUpdateSuccess,
  onAuthorizationRequired,
  routeMyAccountCashout,
  routeMyAccountDelete,
  routeMyAccountDetails,
  routeMyAccountPassword,
  routeConversionConfig,
  focus,
  routeHere,
}: {
  routeClose: RouteDefinition;
  routeHere: RouteDefinition<{ account: string }>;
  routeMyAccountDetails: RouteDefinition;
  routeMyAccountDelete: RouteDefinition;
  routeMyAccountPassword: RouteDefinition;
  routeMyAccountCashout: RouteDefinition;
  routeConversionConfig: RouteDefinition;
  focus?: boolean;
  onAuthorizationRequired: () => void;
  onUpdateSuccess: () => void;
  account: string;
}): VNode {
  const { i18n } = useTranslationContext();
  const { state: credentials } = useSessionState();
  const token =
    credentials.status !== "loggedIn" ? undefined : credentials.token;
  const { lib: { bank: api } } = useBankCoreApiContext();

  const [current, setCurrent] = useState<string | undefined>();
  const [password, setPassword] = useState<string | undefined>();
  const [repeat, setRepeat] = useState<string | undefined>();
  const [, updateBankState] = useBankState();

  const accountIsTheCurrentUser =
    credentials.status === "loggedIn"
      ? credentials.username === accountName
      : false;

  const errors = undefinedIfEmpty({
    current: !accountIsTheCurrentUser
      ? undefined
      : !current
        ? i18n.str`Required`
        : undefined,
    password: !password ? i18n.str`Required` : undefined,
    repeat: !repeat
      ? i18n.str`Required`
      : password !== repeat
        ? i18n.str`Repeated password doesn't match`
        : undefined,
  });
  const [notification, notify, handleError] = useLocalNotification();

  async function doChangePassword() {
    if (!!errors || !password || !token) return;
    await handleError(async () => {
      const request = {
        old_password: current,
        new_password: password,
      };
      const resp = await api.updatePassword(
        { username: accountName, token },
        request,
      );
      if (resp.type === "ok") {
        notifyInfo(i18n.str`Password changed`);
        onUpdateSuccess();
      } else {
        switch (resp.case) {
          case HttpStatusCode.Unauthorized:
            return notify({
              type: "error",
              title: i18n.str`Not authorized to change the password, maybe the session is invalid.`,
              description: resp.detail.hint as TranslatedString,
              debug: resp.detail,
              when: AbsoluteTime.now(),
            });
          case HttpStatusCode.NotFound:
            return notify({
              type: "error",
              title: i18n.str`Account not found`,
              description: resp.detail.hint as TranslatedString,
              debug: resp.detail,
              when: AbsoluteTime.now(),
            });
          case TalerErrorCode.BANK_NON_ADMIN_PATCH_MISSING_OLD_PASSWORD:
            return notify({
              type: "error",
              title: i18n.str`You need to provide the old password. If you don't have it contact your account administrator.`,
              description: resp.detail.hint as TranslatedString,
              debug: resp.detail,
              when: AbsoluteTime.now(),
            });
          case TalerErrorCode.BANK_PATCH_BAD_OLD_PASSWORD:
            return notify({
              type: "error",
              title: i18n.str`Your current password doesn't match, can't change to a new password.`,
              description: resp.detail.hint as TranslatedString,
              debug: resp.detail,
              when: AbsoluteTime.now(),
            });
          case HttpStatusCode.Accepted: {
            updateBankState("currentChallenge", {
              operation: "update-password",
              id: String(resp.body.challenge_id),
              location: routeHere.url({ account: accountName }),
              sent: AbsoluteTime.never(),
              request,
            });
            return onAuthorizationRequired();
          }
          default:
            assertUnreachable(resp);
        }
      }
    });
  }

  return (
    <Fragment>
      <LocalNotificationBanner notification={notification} />
      {accountIsTheCurrentUser ? (
        <ProfileNavigation
          current="credentials"
          routeMyAccountCashout={routeMyAccountCashout}
          routeMyAccountDelete={routeMyAccountDelete}
          routeMyAccountDetails={routeMyAccountDetails}
          routeMyAccountPassword={routeMyAccountPassword}
          routeConversionConfig={routeConversionConfig}
        />
      ) : (
        <h1 class="text-base font-semibold leading-6 text-gray-900">
          <i18n.Translate>Account "{accountName}"</i18n.Translate>
        </h1>
      )}

      <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">
        <div class="px-4 sm:px-0">
          <h2 class="text-base font-semibold leading-7 text-gray-900">
            <i18n.Translate>Update password</i18n.Translate>
          </h2>
        </div>
        <form
          class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"
          autoCapitalize="none"
          autoCorrect="off"
          onSubmit={(e) => {
            e.preventDefault();
          }}
        >
          <div class="px-4 py-6 sm:p-8">
            <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
              {accountIsTheCurrentUser ? (
                <div class="sm:col-span-5">
                  <label
                    class="block text-sm font-medium leading-6 text-gray-900"
                    for="password"
                  >
                    {i18n.str`Current password`}
                    <b style={{ color: "red" }}> *</b>
                  </label>
                  <div class="mt-2">
                    <input
                      type="password"
                      class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
                      name="current"
                      id="current-password"
                      data-error={!!errors?.current && current !== undefined}
                      value={current ?? ""}
                      onChange={(e) => {
                        setCurrent(e.currentTarget.value);
                      }}
                      autocomplete="off"
                    />
                    <ShowInputErrorLabel
                      message={errors?.current}
                      isDirty={current !== undefined}
                    />
                  </div>
                  <p class="mt-2 text-sm text-gray-500">
                    <i18n.Translate>
                      Your current password, for security
                    </i18n.Translate>
                  </p>
                </div>
              ) : undefined}

              <div class="sm:col-span-5">
                <label
                  class="block text-sm font-medium leading-6 text-gray-900"
                  for="password"
                >
                  {i18n.str`New password`}
                  <b style={{ color: "red" }}> *</b>
                </label>
                <div class="mt-2">
                  <input
                    ref={focus ? doAutoFocus : undefined}
                    type="password"
                    class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
                    name="password"
                    id="password"
                    data-error={!!errors?.password && password !== undefined}
                    value={password ?? ""}
                    onChange={(e) => {
                      setPassword(e.currentTarget.value);
                    }}
                    autocomplete="off"
                  />
                  <ShowInputErrorLabel
                    message={errors?.password}
                    isDirty={password !== undefined}
                  />
                </div>
              </div>

              <div class="sm:col-span-5">
                <label
                  class="block text-sm font-medium leading-6 text-gray-900"
                  for="repeat"
                >
                  {i18n.str`Type it again`}
                  <b style={{ color: "red" }}> *</b>
                </label>
                <div class="mt-2">
                  <input
                    type="password"
                    class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
                    name="repeat"
                    id="repeat"
                    data-error={!!errors?.repeat && repeat !== undefined}
                    value={repeat ?? ""}
                    onChange={(e) => {
                      setRepeat(e.currentTarget.value);
                    }}
                    // placeholder=""
                    autocomplete="off"
                  />
                  <ShowInputErrorLabel
                    message={errors?.repeat}
                    isDirty={repeat !== undefined}
                  />
                </div>
                <p class="mt-2 text-sm text-gray-500">
                  <i18n.Translate>Repeat the same password</i18n.Translate>
                </p>
              </div>
            </div>
          </div>
          <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={routeClose.url({})}
              name="cancel"
              class="text-sm font-semibold leading-6 text-gray-900"
            >
              <i18n.Translate>Cancel</i18n.Translate>
            </a>
            <button
              type="submit"
              name="change"
              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={!!errors}
              onClick={(e) => {
                e.preventDefault();
                doChangePassword();
              }}
            >
              <i18n.Translate>Change</i18n.Translate>
            </button>
          </div>
        </form>
      </div>
    </Fragment>
  );
}
