/*
 This file is part of GNU Taler
 (C) 2022 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 {
  HttpStatusCode,
  Logger,
  parseWithdrawUri,
  stringifyWithdrawUri,
} from "@gnu-taler/taler-util";
import {
  ErrorType,
  HttpResponse,
  HttpResponsePaginated,
  useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
import { Loading } from "../components/Loading.js";
import { useBackendContext } from "../context/backend.js";
import { getInitialBackendBaseURL } from "../hooks/backend.js";
import { notifyError, notifyInfo } from "../hooks/notification.js";
import { useSettings } from "../hooks/settings.js";
import { AccountPage } from "./AccountPage/index.js";
import { AdminPage } from "./AdminPage.js";
import { LoginForm } from "./LoginForm.js";
import { WithdrawalQRCode } from "./WithdrawalQRCode.js";

const logger = new Logger("AccountPage");

/**
 * show content based on state:
 * - LoginForm if the user is not logged in
 * - qr code if withdrawal in progress
 * - else account information
 * Use the handler to catch error cases
 *
 * @param param0
 * @returns
 */
export function HomePage({
  onRegister,
  onPendingOperationFound,
}: {
  onPendingOperationFound: (id: string) => void;
  onRegister: () => void;
}): VNode {
  const backend = useBackendContext();
  const [settings] = useSettings();
  const { i18n } = useTranslationContext();

  if (backend.state.status === "loggedOut") {
    return <LoginForm onRegister={onRegister} />;
  }

  if (settings.currentWithdrawalOperationId) {
    onPendingOperationFound(settings.currentWithdrawalOperationId);
    return <Loading />;
  }

  if (backend.state.isUserAdministrator) {
    return <AdminPage onRegister={onRegister} />;
  }

  return (
    <AccountPage
      account={backend.state.username}
      onLoadNotOk={handleNotOkResult(i18n, onRegister)}
    />
  );
}

export function WithdrawalOperationPage({
  operationId,
  onLoadNotOk,
  onContinue,
}: {
  operationId: string;
  onLoadNotOk: () => void;
  onContinue: () => void;
}): VNode {
  //FIXME: libeufin sandbox should return show to create the integration api endpoint
  //or return withdrawal uri from response
  const uri = stringifyWithdrawUri({
    bankIntegrationApiBaseUrl: `${getInitialBackendBaseURL()}/integration-api`,
    withdrawalOperationId: operationId,
  });
  const parsedUri = parseWithdrawUri(uri);
  const { i18n } = useTranslationContext();

  if (!parsedUri) {
    notifyError({
      title: i18n.str`The Withdrawal URI is not valid: "${uri}"`,
    });
    return <Loading />;
  }

  return (
    <WithdrawalQRCode
      withdrawUri={parsedUri}
      onContinue={onContinue}
      onLoadNotOk={onLoadNotOk}
    />
  );
}

export function handleNotOkResult(
  i18n: ReturnType<typeof useTranslationContext>["i18n"],
  onRegister?: () => void,
): <T>(
  result:
    | HttpResponsePaginated<T, SandboxBackend.SandboxError>
    | HttpResponse<T, SandboxBackend.SandboxError>,
) => VNode {
  return function handleNotOkResult2<T>(
    result:
      | HttpResponsePaginated<T, SandboxBackend.SandboxError>
      | HttpResponse<T, SandboxBackend.SandboxError>,
  ): VNode {
    if (result.loading) return <Loading />;
    if (!result.ok) {
      switch (result.type) {
        case ErrorType.TIMEOUT: {
          notifyError({
            title: i18n.str`Request timeout, try again later.`,
          });
          break;
        }
        case ErrorType.CLIENT: {
          if (result.status === HttpStatusCode.Unauthorized) {
            notifyError({
              title: i18n.str`Wrong credentials`,
            });
            return <LoginForm onRegister={onRegister} />;
          }
          const errorData = result.payload;
          notifyError({
            title: i18n.str`Could not load due to a client error`,
            description: errorData.error.description,
            debug: JSON.stringify(result),
          });
          break;
        }
        case ErrorType.SERVER: {
          notifyError({
            title: i18n.str`Server returned with error`,
            description: result.payload.error.description,
            debug: JSON.stringify(result.payload),
          });
          break;
        }
        case ErrorType.UNREADABLE: {
          notifyError({
            title: i18n.str`Unexpected error.`,
            description: `Response from ${result.info?.url} is unreadable, http status: ${result.status}`,
            debug: JSON.stringify(result),
          });
          break;
        }
        case ErrorType.UNEXPECTED: {
          notifyError({
            title: i18n.str`Unexpected error.`,
            description: `Diagnostic from ${result.info?.url} is "${result.message}"`,
            debug: JSON.stringify(result),
          });
          break;
        }
        default: {
          assertUnreachable(result);
        }
      }

      return <div>error</div>;
    }
    return <div />;
  };
}
export function assertUnreachable(x: never): never {
  throw new Error("Didn't expect to get here");
}
