/*
 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/>
 */

/**
 *
 * @author Sebastian Javier Marchano (sebasjm)
 * @author Nicola Eigel
 */

import { TranslatedString } from "@gnu-taler/taler-util";
import { VNode, h } from "preact";
import { Route, Router, route } from "preact-router";
import { useEffect, useErrorBoundary, useMemo, useState } from "preact/hooks";
import { Menu, NotificationCard } from "./components/menu/index.js";
import { EntityContextProvider } from "./context/entity.js";
import { AuditorBackend } from "./declaration.js";
import DefaultList from "./paths/default/index.js";
import DetailsDashboard from "./paths/details/index.js";
import FinanceDashboard from "./paths/finance/index.js";
import NotFoundPage from "./paths/notfound/index.js";
import OperationsDashboard from "./paths/operations/index.js";
import SecurityDashboard from "./paths/security/index.js";
import { Settings } from "./paths/settings/index.js";
import { Notification } from "./utils/types.js";

export enum Paths {
  error = "/error",
  settings = "/settings",

  key_figures = "/key-figures",
  critical_errors = "/critical-errors",
  operating_status = "/operating-status",
  detail_view = "/detail-view",

  amount_arithmethic_inconsistency_list = "/amount-arithmetic-inconsistencies",

  bad_sig_losses_list = "/bad-sig-losses",

  balance_list = "/balance",

  closure_lag_list = "/closure-lags",

  coin_inconsistency_list = "/coin-inconsistencies",

  denomination_key_validity_withdraw_inconsistency_list = "/denomination-key-validity-withdraw-inconsistencies",

  denomination_pending_list = "/denominations-pending",

  denomination_without_sig_list = "/denominations-without-sig",

  deposit_confirmation_list = "/deposit-confirmations",
  deposit_confirmation_update = "/deposit-confirmation/:rowid/update",

  emergency_list = "/emergencies",

  emergency_by_count_list = "/emergencies-by-count",

  exchange_signkey_list = "/exchange-sign-keys",

  fee_time_inconsistency_list = "/fee-time-inconsistencies",

  historic_denomination_revenue_list = "/historic-denomination-revenues",

  misattribution_in_inconsistency_list = "/misattribution-in-inconsistencies",

  progress_list = "/progress",

  purse_not_closed_inconsistency_list = "/purse-not-closed-inconsistencies",

  purse_list = "/purses",

  refresh_hanging_list = "/refreshes-hanging",

  reserve_balance_insufficient_inconsistency_list = "/reserve-balance-insufficient-inconsistencies",

  reserve_balance_summary_wrong_inconsistency_list = "/reserve-balance-summary-wrong-inconsistencies",

  reserve_in_inconsistency_list = "/reserve-in-inconsistencies",

  reserve_not_closed_inconsistency_list = "/reserve-not-closed-inconsistencies",

  reserves_list = "/reserves",

  row_inconsistency_list = "/row-inconsistencies",

  row_minor_inconsistency_list = "/row-minor-inconsistencies",

  wire_format_inconsistency_list = "/wire-format-inconsistencies",

  wire_out_inconsistency_list = "/wire-out-inconsistencies",
}

interface TestProps {
  title: string;
  endpoint: string;
  entity: any;
}

function getInstanceTitle(path: string): TestProps {
  switch (path) {
    case Paths.key_figures:
      return { title: `Key figures`, endpoint: "helper", entity: null };
    case Paths.critical_errors:
      return { title: `Critical errors`, endpoint: "helper", entity: null };
    case Paths.operating_status:
      return { title: `Operating status`, endpoint: "helper", entity: null };
    case Paths.detail_view:
      return { title: `Inconsistencies`, endpoint: "helper", entity: null };
    case Paths.amount_arithmethic_inconsistency_list:
      let amountArithmeticInconsistency: AuditorBackend.AmountArithmeticInconsistency.ClassAmountArithmeticInconsistency =
        {} as AuditorBackend.AmountArithmeticInconsistency.ClassAmountArithmeticInconsistency;
      return {
        title: `Amount arithmetic inconsistencies`,
        endpoint: "amount-arithmetic-inconsistency",
        entity: amountArithmeticInconsistency,
      };
    case Paths.bad_sig_losses_list:
      return {
        title: `Bad Sig Losses`,
        endpoint: "bad-sig-losses",
        entity: null,
      };
    case Paths.balance_list:
      return { title: "Balances", endpoint: "balances", entity: null };
    case Paths.closure_lag_list:
      return { title: `Closure Lags`, endpoint: "closure-lags", entity: null };
    case Paths.coin_inconsistency_list:
      return {
        title: `Coin inconsistencies`,
        endpoint: "coin-inconsistency",
        entity: null,
      };
    case Paths.denomination_key_validity_withdraw_inconsistency_list:
      return {
        title: `Denomination key validity withdraw inconsistency`,
        endpoint: "denomination-key-validity-withdraw-inconsistency",
        entity: null,
      };
    case Paths.denomination_pending_list:
      return {
        title: `Denominations pending`,
        endpoint: "denomination-pending",
        entity: null,
      };
    case Paths.denomination_without_sig_list:
      return {
        title: `Denominations without sigs`,
        endpoint: "denominations-without-sigs",
        entity: null,
      };
    case Paths.deposit_confirmation_list:
      return {
        title: "Deposit Confirmations",
        endpoint: "deposit-confirmation",
        entity: null,
      };
    case Paths.emergency_list:
      return { title: "Emergencies", endpoint: "emergency", entity: null };
    case Paths.emergency_by_count_list:
      return {
        title: "Emergencies by count",
        endpoint: "emergency-by-count",
        entity: null,
      };
    case Paths.fee_time_inconsistency_list:
      return {
        title: "Fee time inconsistencies",
        endpoint: "fee-time-inconsistency",
        entity: null,
      };
    case Paths.historic_denomination_revenue_list:
      return {
        title: "Historic denomination revenue",
        endpoint: "historic-denomination-revenue",
        entity: null,
      };
    case Paths.misattribution_in_inconsistency_list:
      return {
        title: "Misattribution in inconsistencies",
        endpoint: "misattribution-in-inconsistency",
        entity: null,
      };
    case Paths.progress_list:
      return { title: "Progress", endpoint: "progress", entity: null };
    case Paths.purse_not_closed_inconsistency_list:
      return {
        title: "Purse not closed inconsistencies",
        endpoint: "purse-not-closed-inconsistencies",
        entity: null,
      };
    case Paths.purse_list:
      return { title: "Purses", endpoint: "purses", entity: null };
    case Paths.refresh_hanging_list:
      return {
        title: "Refreshes hanging",
        endpoint: "refreshes-hanging",
        entity: null,
      };
    case Paths.reserves_list:
      return { title: "Reserves", endpoint: "reserves  ", entity: null };
    case Paths.reserve_balance_insufficient_inconsistency_list:
      return {
        title: "Reserve balance insufficient inconsistencies",
        endpoint: "reserve-balance-insufficient-inconsistency",
        entity: null,
      };
    case Paths.reserve_balance_summary_wrong_inconsistency_list:
      return {
        title: "Reserve balance summary wrong inconsistencies",
        endpoint: "reserve-balance-summary-wrong-inconsistency",
        entity: null,
      };
    case Paths.reserve_in_inconsistency_list:
      return {
        title: "Reserves in inconsistencies",
        endpoint: "reserve-in-inconsistency",
        entity: null,
      };
    case Paths.reserve_not_closed_inconsistency_list:
      return {
        title: "Reserves not closed inconsistencies",
        endpoint: "reserve-not-closed-inconsistency",
        entity: null,
      };
    case Paths.row_inconsistency_list:
      return {
        title: "Row inconsistencies",
        endpoint: "row-inconsistency",
        entity: null,
      };
    case Paths.row_minor_inconsistency_list:
      return {
        title: "Row minor inconsistencies",
        endpoint: "row-minor-inconsistencies",
        entity: null,
      };
    case Paths.wire_format_inconsistency_list:
      let wireFormatInconsistency: AuditorBackend.WireFormatInconsistency.ClassWireFormatInconsistency =
        {} as AuditorBackend.WireFormatInconsistency.ClassWireFormatInconsistency;
      return {
        title: "Wire format inconsistencies",
        endpoint: "wire-format-inconsistency",
        entity: wireFormatInconsistency,
      };
    case Paths.wire_out_inconsistency_list:
      return {
        title: "Wire out inconsistencies",
        endpoint: "wire-out-inconsistency",
        entity: null,
      };
    case Paths.settings:
      return { title: `Settings`, endpoint: "settings", entity: null };
    default:
      return { title: "", endpoint: "", entity: null };
  }
}

export interface Props {
  path: string;
}

export function InstanceRoutes({ path }: Props): VNode {
  type GlobalNotifState =
    | (Notification & { to: string | undefined })
    | undefined;
  const [globalNotification, setGlobalNotification] =
    useState<GlobalNotifState>(undefined);

  const [error] = useErrorBoundary();
  const { title, endpoint, entity } = getInstanceTitle(path);

  const value = useMemo(
    () => ({ title, path, endpoint, entity }),
    [title, path, endpoint, entity],
  );

  //TODO add if needed
  /*function ServerErrorRedirectTo(to: Paths) {
      return function ServerErrorRedirectToImpl(
        error: HttpError<AuditorBackend.ErrorDetail>,
      ) {
        if (error.type === ErrorType.TIMEOUT) {
          setGlobalNotification({
            message: `The request to the backend take too long and was cancelled`,
            description: `Diagnostic from ${error.info.url} is "${error.message}"`,
            type: "ERROR",
            to,
          });
        } else {
          setGlobalNotification({
            message: `The backend reported a problem: HTTP status #${error.status}`,
            description: `Diagnostic from ${error.info.url} is '${error.message}'`,
            details:
              error.type === ErrorType.CLIENT || error.type === ErrorType.SERVER
                ? error.payload.detail
                : undefined,
            type: "ERROR",
            to,
          });
        }
        return <Redirect to={to} />;
      };
    }*/

  return (
    <EntityContextProvider value={value}>
      <Menu
        //    instance={id}
        path={path}
        title={"Settings"}
        onShowSettings={() => {
          route(Paths.settings);
        }}
      />
      <NotificationCard notification={globalNotification} />
      {error && (
        <NotificationCard
          notification={{
            message: "Internal error, please report",
            type: "ERROR",
            description: (
              <pre>
                {
                  (error instanceof Error
                    ? error.stack
                    : String(error)) as TranslatedString
                }
              </pre>
            ),
          }}
        />
      )}
      <Router
        onChange={(e) => {
          const movingOutFromNotification =
            globalNotification && e.url !== globalNotification.to;
          if (movingOutFromNotification) {
            setGlobalNotification(undefined);
          }
        }}
      >
        <Route path="/" component={Redirect} to={Paths.key_figures} />

        <Route
          path={Paths.key_figures}
          component={FinanceDashboard}
          onNotFound={NotFoundPage}
          //onLoadError={ServerErrorRedirectTo(Paths.balance_list)}
        />
        <Route
          path={Paths.critical_errors}
          component={SecurityDashboard}
          onNotFound={NotFoundPage}
          //onLoadError={ServerErrorRedirectTo(Paths.balance_list)}
        />
        <Route
          path={Paths.operating_status}
          component={OperationsDashboard}
          onNotFound={NotFoundPage}
          //onLoadError={ServerErrorRedirectTo(Paths.balance_list)}
        />
        <Route
          path={Paths.detail_view}
          component={DetailsDashboard}
          onNotFound={NotFoundPage}
          //onLoadError={ServerErrorRedirectTo(Paths.balance_list)}
        />
        <Route
          path={Paths.amount_arithmethic_inconsistency_list}
          component={DefaultList}
          onNotFound={NotFoundPage}
          //onLoadError={ServerErrorRedirectTo(Paths.balance_list)}
        />
        <Route
          path={Paths.bad_sig_losses_list}
          component={DefaultList}
          onNotFound={NotFoundPage}
          //onLoadError={ServerErrorRedirectTo(Paths.balance_list)}
        />
        <Route
          path={Paths.balance_list}
          component={DefaultList}
          onNotFound={NotFoundPage}
          //onLoadError={ServerErrorRedirectTo(Paths.balance_list)}
        />
        <Route
          path={Paths.closure_lag_list}
          component={DefaultList}
          onNotFound={NotFoundPage}
          //onLoadError={ServerErrorRedirectTo(Paths.balance_list)}
        />
        <Route
          path={Paths.coin_inconsistency_list}
          component={DefaultList}
          onNotFound={NotFoundPage}
          //onLoadError={ServerErrorRedirectTo(Paths.balance_list)}
        />
        <Route
          path={Paths.denomination_key_validity_withdraw_inconsistency_list}
          component={DefaultList}
          onNotFound={NotFoundPage}
          //onLoadError={ServerErrorRedirectTo(Paths.balance_list)}
        />
        <Route
          path={Paths.denomination_pending_list}
          component={DefaultList}
          onNotFound={NotFoundPage}
          //onLoadError={ServerErrorRedirectTo(Paths.balance_list)}
        />
        <Route
          path={Paths.denomination_without_sig_list}
          component={DefaultList}
          onNotFound={NotFoundPage}
          //onLoadError={ServerErrorRedirectTo(Paths.balance_list)}
        />
        <Route
          path={Paths.deposit_confirmation_list}
          component={DefaultList}
          onNotFound={NotFoundPage}
          //onLoadError={ServerErrorRedirectTo(Paths.balance_list)}
        />
        <Route
          path={Paths.emergency_list}
          component={DefaultList}
          onNotFound={NotFoundPage}
          //onLoadError={ServerErrorRedirectTo(Paths.balance_list)}
        />
        <Route
          path={Paths.emergency_by_count_list}
          component={DefaultList}
          onNotFound={NotFoundPage}
          //onLoadError={ServerErrorRedirectTo(Paths.balance_list)}
        />
        {
          <Route
            path={Paths.exchange_signkey_list}
            component={DefaultList}
            onNotFound={NotFoundPage}
          />
        }
        {
          <Route
            path={Paths.fee_time_inconsistency_list}
            component={DefaultList}
            onNotFound={NotFoundPage}
          />
        }
        {
          <Route
            path={Paths.historic_denomination_revenue_list}
            component={DefaultList}
            onNotFound={NotFoundPage}
          />
        }
        {
          <Route
            path={Paths.misattribution_in_inconsistency_list}
            component={DefaultList}
            onNotFound={NotFoundPage}
          />
        }
        {
          <Route
            path={Paths.progress_list}
            component={DefaultList}
            onNotFound={NotFoundPage}
          />
        }
        {
          <Route
            path={Paths.purse_not_closed_inconsistency_list}
            component={DefaultList}
            onNotFound={NotFoundPage}
          />
        }
        {
          <Route
            path={Paths.purse_list}
            component={DefaultList}
            onNotFound={NotFoundPage}
          />
        }
        {
          <Route
            path={Paths.refresh_hanging_list}
            component={DefaultList}
            onNotFound={NotFoundPage}
          />
        }
        {
          <Route
            path={Paths.reserve_balance_insufficient_inconsistency_list}
            component={DefaultList}
            onNotFound={NotFoundPage}
          />
        }
        {
          <Route
            path={Paths.reserve_balance_summary_wrong_inconsistency_list}
            component={DefaultList}
            onNotFound={NotFoundPage}
          />
        }
        {
          <Route
            path={Paths.reserve_in_inconsistency_list}
            component={DefaultList}
            onNotFound={NotFoundPage}
          />
        }
        {
          <Route
            path={Paths.reserve_not_closed_inconsistency_list}
            component={DefaultList}
            onNotFound={NotFoundPage}
          />
        }
        {
          <Route
            path={Paths.reserves_list}
            component={DefaultList}
            onNotFound={NotFoundPage}
          />
        }
        {
          <Route
            path={Paths.row_inconsistency_list}
            component={DefaultList}
            onNotFound={NotFoundPage}
          />
        }
        {
          <Route
            path={Paths.row_minor_inconsistency_list}
            component={DefaultList}
            onNotFound={NotFoundPage}
          />
        }
        {
          <Route
            path={Paths.wire_out_inconsistency_list}
            component={DefaultList}
            onNotFound={NotFoundPage}
          />
        }
        {
          <Route
            path={Paths.wire_format_inconsistency_list}
            component={DefaultList}
            onNotFound={NotFoundPage}
          />
        }
        <Route path={Paths.settings} component={Settings} />

        {
          //TODO add if needed
          /**
           * Example pages
           */
        }
        {/*             <Route path="/loading" component={Loading}/>
                <Route default component={NotFoundPage}/>*/}
      </Router>
    </EntityContextProvider>
  );
}

export function Redirect({ to }: { to: string }): null {
  useEffect(() => {
    route(to, true);
  });
  return null;
}
