/*
 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,
} 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 } 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(
    (token: AccessToken, pwd: string, challengeIds: string[]) =>
      lib.instance.updateCurrentInstanceAuthentication(
        token,
        {
          method: MerchantAuthMethod.TOKEN,
          password: pwd,
        },
        { 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 i18n.str`Second factor authentication required.`;
      case HttpStatusCode.Unauthorized:
        return i18n.str`Unaouthorized.`;
      case HttpStatusCode.NotFound:
        return i18n.str`Not found.`;
    }
  };

  const retry = changePassword.lambda((ids: string[]) => [
    changePassword.args![0],
    changePassword.args![1],
    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((pass: string) => [
          changePassword.args![0],
          pass,
          changePassword.args![2],
        ])}
      />
    </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(
    (token: AccessToken, id: string, pwd: string, challengeIds: string[]) =>
      lib.instance.updateInstanceAuthentication(
        token,
        id,
        {
          method: MerchantAuthMethod.TOKEN,
          password: pwd,
        },
        { challengeIds },
      ),
  );
  changePassword.onSuccess = (suc) => {
    onChange();
    return i18n.str`Password changed`;
  };
  changePassword.onFail = (fail) => {
    switch (fail.case) {
      case HttpStatusCode.Accepted:
        mfa.onChallengeRequired(fail.body);
        return i18n.str`Second factor authentication required.`;
      case HttpStatusCode.Unauthorized:
        return i18n.str`Unaouthorized.`;
      case HttpStatusCode.NotFound:
        return i18n.str`Not found.`;
    }
  };
  const retry = changePassword.lambda((ids: string[]) => [
    changePassword.args![0],
    changePassword.args![2],
    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((pass: string) => [
          changePassword.args![0],
          changePassword.args![1],
          pass,
          changePassword.args![3],
        ])}
      />
    </Fragment>
  );
}
