/*
 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 { AccessToken, TalerCorebankApi } from "@gnu-taler/taler-util";
import {
  ButtonBetter,
  LocalNotificationBanner,
  RouteDefinition,
  ShowInputErrorLabel,
  notifyInfo,
  useBankCoreApiContext,
  useChallengeHandler,
  useLocalNotificationBetter,
  useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
import { useState } from "preact/hooks";
import { useSessionState } from "../../hooks/session.js";
import { undefinedIfEmpty } from "../../utils.js";
import { doAutoFocus } from "../PaytoWireTransferForm.js";
import { ProfileNavigation } from "../ProfileNavigation.js";
import { SolveMFAChallenges } from "../SolveMFA.js";
import { TalerErrorCode } from "@gnu-taler/taler-util";
import { HttpStatusCode } from "@gnu-taler/taler-util";

const TALER_SCREEN_ID = 119;

export function UpdateAccountPassword({
  account: accountName,
  routeClose,
  onUpdateSuccess,

  routeMyAccountCashout,
  routeMyAccountDelete,
  routeMyAccountDetails,
  routeMyAccountPassword,
  routeConversionConfig,
  focus,
}: {
  routeClose: RouteDefinition;
  routeMyAccountDetails: RouteDefinition;
  routeMyAccountDelete: RouteDefinition;
  routeMyAccountPassword: RouteDefinition;
  routeMyAccountCashout: RouteDefinition;
  routeConversionConfig: RouteDefinition;
  focus?: boolean;

  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 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, safeFunctionHandler] = useLocalNotificationBetter();
  const mfa = useChallengeHandler();

  const update = safeFunctionHandler(
    (
      token: AccessToken,
      request: TalerCorebankApi.AccountPasswordChange,
      challengeIds: string[],
    ) =>
      api.updatePassword({ username: accountName, token }, request, {
        challengeIds,
      }),
    !password || !token
      ? undefined
      : [
          token,
          {
            old_password: current,
            new_password: password,
          },
          [],
        ],
  );

  update.onSuccess = (success) => {
    notifyInfo(i18n.str`Password changed`);
    onUpdateSuccess();
  };
  update.onFail = (fail) => {
    switch (fail.case) {
      case HttpStatusCode.Unauthorized:
        return i18n.str`Not authorized to change the password, maybe the session is invalid.`;
      case HttpStatusCode.NotFound:
        return i18n.str`Account not found`;
      case TalerErrorCode.BANK_NON_ADMIN_PATCH_MISSING_OLD_PASSWORD:
        return i18n.str`You need to provide the old password. If you don't have it contact your account administrator.`;
      case TalerErrorCode.BANK_PATCH_BAD_OLD_PASSWORD:
        return i18n.str`Your current password doesn't match, can't change to a new password.`;
      case HttpStatusCode.Accepted: {
        mfa.onChallengeRequired(fail.body);
        return i18n.str`A second factor authentication is required.`;
      }
      case HttpStatusCode.Forbidden:
        return i18n.str`You don't have the rights to change the password.`;
      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 repeatUpdate = update.lambda((ids: string[]) => {
    return [update.args![0], update.args![1], ids];
  });

  if (mfa.pendingChallenge) {
    return (
      <SolveMFAChallenges
        currentChallenge={mfa.pendingChallenge}
        description={i18n.str`Update account password.`}
        username={accountName}
        onCancel={mfa.doCancelChallenge}
        onCompleted={repeatUpdate}
      />
    );
  }
  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 class="text-[red]"> *</b>
                  </label>
                  <div class="mt-2">
                    <input
                      type="password"
                      ref={focus ? doAutoFocus : undefined}
                      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 class="text-[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="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 class="text-[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>
            <ButtonBetter
              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"
              onClick={update}
            >
              <i18n.Translate>Change</i18n.Translate>
            </ButtonBetter>
          </div>
        </form>
      </div>
    </Fragment>
  );
}
