/*
 This file is part of GNU Taler
 (C) 2022-2025 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 {
  ExchangeUpdateStatus,
  WalletBankAccountInfo,
  assertUnreachable,
  ScopeType,
  parseScopeInfoShort,
  stringifyScopeInfoShort,
  PaytoString,
  ScopeInfo,
  WalletBalance,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { useState } from "preact/hooks";
import { alertFromError, useAlertContext } from "../../context/alert.js";
import { useBackendContext } from "../../context/backend.js";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { RecursiveState } from "../../utils/index.js";
import { Props, State } from "./index.js";

export function useComponentState(props: Props): RecursiveState<State> {
  const api = useBackendContext();
  const { pushAlertOnError } = useAlertContext();

  const [scope, setScope] = useState(props.scope);

  const hook = useAsyncAsHook(async () => {
    const { accounts } = await api.wallet.call(
      WalletApiOperation.ListBankAccounts,
      {},
    );
    const { balances } = await api.wallet.call(
      WalletApiOperation.GetBalances,
      {},
    );

    return { accounts, balances };
  });

  const previous: WalletBankAccountInfo[] =
    props.type === "send" && hook && !hook.hasError
      ? hook.response.accounts
      : [];

  if (!scope) {
    return () => {
      const { i18n } = useTranslationContext();
      const hook = useAsyncAsHook(async () => {
        const resp = await api.wallet.call(
          WalletApiOperation.ListExchanges,
          {},
        );

        const unknownIndex = resp.exchanges.findIndex(
          (d) => d.exchangeUpdateStatus === ExchangeUpdateStatus.Initial,
        );
        if (unknownIndex === -1) return resp;

        await api.wallet.call(WalletApiOperation.UpdateExchangeEntry, {
          exchangeBaseUrl: resp.exchanges[unknownIndex].exchangeBaseUrl,
          force: true,
        });

        return await api.wallet.call(WalletApiOperation.ListExchanges, {});
      });

      if (!hook) {
        return {
          status: "loading",
          error: undefined,
        };
      }
      if (hook.hasError) {
        return {
          status: "error",
          error: alertFromError(i18n, i18n.str`Could not load exchanges`, hook),
        };
      }
      const currencies: Record<string, string> = {};
      hook.response.exchanges.forEach((b) => {
        switch (b.scopeInfo.type) {
          case ScopeType.Global: {
            currencies[stringifyScopeInfoShort(b.scopeInfo)] =
              b.scopeInfo.currency;
            break;
          }
          case ScopeType.Exchange: {
            currencies[stringifyScopeInfoShort(b.scopeInfo)] =
              `${b.scopeInfo.currency} ${b.scopeInfo.url}`;
            break;
          }
          case ScopeType.Auditor: {
            currencies[stringifyScopeInfoShort(b.scopeInfo)] =
              `${b.scopeInfo.currency} ${b.scopeInfo.url}`;
            break;
          }
          default: {
            assertUnreachable(b.scopeInfo);
          }
        }
      });
      currencies[""] = "Select a currency";

      return {
        status: "select-currency",
        error: undefined,
        onCurrencySelected: (c: string) => {
          const scope = parseScopeInfoShort(c);
          setScope(scope);
        },
        currencies,
      };
    };
  }

  const disablePeerPayments: boolean =
    (hook && !hook.hasError
      ? getBalanceForScope(hook.response.balances, scope)?.disablePeerPayments
      : undefined) ?? false;

  switch (props.type) {
    case "send":
      return {
        status: "ready",
        error: undefined,
        previous,
        onSelectAccount: pushAlertOnError(async (account: PaytoString) => {
          props.goToWalletKnownBankDeposit(scope, account);
        }),
        goToBank: {
          onClick: pushAlertOnError(async () => {
            props.goToWalletNewBankDeposit(scope);
          }),
        },
        goToWallet: {
          onClick: pushAlertOnError(async () => {
            props.goToWalletWalletSend(scope);
          }),
        },
        disablePeerPayments,
        type: props.type,
      };
    case "get":
      return {
        status: "ready",
        error: undefined,
        previous,
        goToBank: {
          onClick: pushAlertOnError(async () => {
            props.goToWalletManualWithdraw(scope);
          }),
        },
        onSelectAccount: () => {},
        goToWallet: {
          onClick: pushAlertOnError(async () => {
            props.goToWalletWalletInvoice(scope);
          }),
        },
        disablePeerPayments,
        type: props.type,
      };
    default:
      assertUnreachable(props);
  }
}

function getBalanceForScope(
  balances: WalletBalance[],
  scope: ScopeInfo,
): WalletBalance | undefined {
  return balances.find((b) => {
    const bs = b.scopeInfo;
    if (bs.type !== scope.type) return false;
    if (bs.currency !== scope.currency) return false;
    if (scope.type === ScopeType.Global || bs.type === ScopeType.Global) return false;
    if (scope.url !== bs.url) return false;
    return true;
  });
}
