/*
 This file is part of GNU Taler
 (C) 2021-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,
  HttpStatusCode,
  MerchantAuthMethod,
  TalerError,
  assertUnreachable,
  opKnownFailure,
} from "@gnu-taler/taler-util";
import {
  LocalNotificationBannerBulma,
  useChallengeHandler,
  useLocalNotificationBetter,
  useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
import { ErrorLoadingMerchant } from "../../../components/ErrorLoadingMerchant.js";
import { Loading } from "../../../components/exception/loading.js";
import { SolveMFAChallenges } from "../../../components/SolveMFA.js";
import { useSessionContext } from "../../../context/session.js";
import {
  useInstanceDetails,
  useManagedInstanceDetails,
} from "../../../hooks/instance.js";
import { LoginPage, TEMP_TEST_TOKEN } from "../../login/index.js";
import { NotFoundPageOrAdminCreate } from "../../notfound/index.js";
import { DetailPage } from "./DetailPage.js";

const TALER_SCREEN_ID = 54;

export interface Props {
  onChange: () => void;
  onCancel: () => void;
}

export default function PasswordPage({ onCancel, onChange }: Props): VNode {
  const [notification, safeFunctionHandler] = useLocalNotificationBetter();
  const { state: session, lib } = useSessionContext();
  const result = useInstanceDetails();
  const instanceId = session.instance;

  if (!result) return <Loading />;
  if (result instanceof TalerError) {
    return <ErrorLoadingMerchant error={result} />;
  }
  if (result.type === "fail") {
    switch (result.case) {
      case HttpStatusCode.Unauthorized: {
        return <LoginPage />;
      }
      case HttpStatusCode.NotFound: {
        return <NotFoundPageOrAdminCreate />;
      }
      default: {
        assertUnreachable(result);
      }
    }
  }
  const { i18n } = useTranslationContext();
  const mfa = useChallengeHandler();

  const changePassword = safeFunctionHandler(
    async (
      token: AccessToken,
      current: string,
      next: string,
      challengeIds: string[],
    ) => {
      const resp = await lib.instance.createAccessToken(
        instanceId,
        current,
        TEMP_TEST_TOKEN(i18n.str`Testing password change`),
      );
      if (resp.type === "fail") {
        switch (resp.case) {
          case HttpStatusCode.Unauthorized:
            return opKnownFailure("bad-current-pwd");
          case HttpStatusCode.NotFound:
            return resp;
          case HttpStatusCode.Accepted:
            break; //2fa required but the pwd is ok, continue
        }
      }
      return lib.instance.updateCurrentInstanceAuthentication(
        token,
        {
          method: MerchantAuthMethod.TOKEN,
          password: next,
        },
        { challengeIds },
      );
    },
    !session.token ? undefined : [session.token, "", "", []],
  );
  changePassword.onSuccess = (suc) => {
    onChange();
    return i18n.str`Password changed`;
  };
  changePassword.onFail = (fail) => {
    switch (fail.case) {
      case HttpStatusCode.Accepted:
        mfa.onChallengeRequired(fail.body);
        return undefined;
      case HttpStatusCode.Unauthorized:
        return i18n.str`Unauthorized.`;
      case HttpStatusCode.NotFound:
        return i18n.str`Not found.`;
      case "bad-current-pwd":
        return i18n.str`The current password is wrong.`;
    }
  };

  const retry = changePassword.lambda((ids: string[]) => [
    changePassword.args![0],
    changePassword.args![1],
    changePassword.args![2],
    ids,
  ]);
  if (mfa.pendingChallenge) {
    return (
      <SolveMFAChallenges
        currentChallenge={mfa.pendingChallenge}
        onCompleted={retry}
        onCancel={mfa.doCancelChallenge}
      />
    );
  }

  return (
    <Fragment>
      <LocalNotificationBannerBulma notification={notification} />
      <DetailPage
        onBack={onCancel}
        instanceId={result.body.name}
        changePassword={changePassword.lambda(
          (current: string, next: string) => [
            changePassword.args![0],
            current,
            next,
            changePassword.args![3],
          ],
        )}
      />
    </Fragment>
  );
}

export function AdminPassword({
  instanceId,
  onCancel,
  onChange,
}: Props & { instanceId: string }): VNode {
  const [notification, safeFunctionHandler] = useLocalNotificationBetter();
  const { state: session, lib } = useSessionContext();

  const subInstanceLib = lib.subInstanceApi(instanceId).instance;
  const result = useManagedInstanceDetails(instanceId);

  if (!result) return <Loading />;
  if (result instanceof TalerError) {
    return <ErrorLoadingMerchant error={result} />;
  }
  if (result.type === "fail") {
    switch (result.case) {
      case HttpStatusCode.Unauthorized: {
        return <LoginPage />;
      }
      case HttpStatusCode.NotFound: {
        return <NotFoundPageOrAdminCreate />;
      }
      default: {
        assertUnreachable(result);
      }
    }
  }

  const { i18n } = useTranslationContext();
  const mfa = useChallengeHandler();
  const changePassword = safeFunctionHandler(
    async (
      token: AccessToken,
      id: string,
      pwd: string,
      challengeIds: string[],
    ) => {
      // const resp = await subInstanceLib.createAccessToken(
      //   id,
      //   current,
      //   TEMP_TEST_TOKEN(i18n.str`Testing password change`),
      // );
      // if (resp.type === "fail") {
      //   switch (resp.case) {
      //     case HttpStatusCode.Unauthorized:
      //       return opKnownFailure("bad-current-pwd");
      //     case HttpStatusCode.NotFound:
      //       return resp;
      //     case HttpStatusCode.Accepted:
      //       break; //2fa required but the pwd is ok, continue
      //   }
      // }
      return await lib.instance.updateInstanceAuthentication(
        token,
        id,
        {
          method: MerchantAuthMethod.TOKEN,
          password: pwd,
        },
        { challengeIds },
      );
    },
    !session.token ? undefined : [session.token, instanceId, "", []],
  );
  changePassword.onSuccess = (suc) => {
    onChange();
    return i18n.str`Password changed`;
  };
  changePassword.onFail = (fail) => {
    switch (fail.case) {
      case HttpStatusCode.Accepted:
        mfa.onChallengeRequired(fail.body);
        return undefined;
      case HttpStatusCode.Unauthorized:
        return i18n.str`No enough rights to change the password.`;
      case HttpStatusCode.NotFound:
        return i18n.str`Account not found.`;
      default:
        assertUnreachable(fail);
    }
  };
  const retry = changePassword.lambda((ids: string[]) => [
    changePassword.args![0],
    changePassword.args![1],
    changePassword.args![2],
    ids,
  ]);
  if (mfa.pendingChallenge) {
    return (
      <SolveMFAChallenges
        currentChallenge={mfa.pendingChallenge}
        onCompleted={retry}
        onCancel={mfa.doCancelChallenge}
      />
    );
  }

  return (
    <Fragment>
      <LocalNotificationBannerBulma notification={notification} />
      <DetailPage
        onBack={onCancel}
        instanceId={result.body.name}
        changePassword={changePassword.lambda(
          (current: string, next: string) => [
            changePassword.args![0],
            changePassword.args![1],
            next,
            changePassword.args![3],
          ],
        )}
        withoutCurrentPassword
      />
    </Fragment>
  );
}
