/*
 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 {
  Amounts,
  ConfirmPayResultType,
  NotificationType,
  PreparePayResultType,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useEffect } from "preact/hooks";
import { alertFromError, useAlertContext } from "../../context/alert.js";
import { useBackendContext } from "../../context/backend.js";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { ButtonHandler } from "../../mui/handlers.js";
import { Props, State } from "./index.js";

export function useComponentState({
  talerPayUri,
  cancel,
  goToWalletManualWithdraw,
  onSuccess,
}: Props): State {
  const { pushAlertOnError } = useAlertContext();
  const api = useBackendContext();
  const { i18n } = useTranslationContext();

  const hook = useAsyncAsHook(async () => {
    if (!talerPayUri) throw Error("ERROR_NO-URI-FOR-PAYMENT");
    const payStatus = await api.wallet.call(
      WalletApiOperation.PreparePayForUri,
      {
        talerPayUri: talerPayUri,
      },
    );
    const balance = await api.wallet.call(WalletApiOperation.GetBalances, {});
    return { payStatus, balance, uri: talerPayUri };
  }, []);

  useEffect(
    () =>
      api.listener.onUpdateNotification(
        [NotificationType.TransactionStateTransition],
        hook?.retry,
      ),
    [hook],
  );

  const hookResponse = !hook || hook.hasError ? undefined : hook.response;

  useEffect(() => {
    if (!hookResponse) return;
    const { payStatus } = hookResponse;
    if (
      payStatus &&
      payStatus.status === PreparePayResultType.AlreadyConfirmed &&
      payStatus.paid
    ) {
      const fu = payStatus.contractTerms.fulfillment_url;
      if (fu) {
        setTimeout(() => {
          document.location.href = fu;
        }, 3000);
      }
    }
  }, [hookResponse]);

  if (!hook) return { status: "loading", error: undefined };
  if (hook.hasError) {
    return {
      status: "error",
      error: alertFromError(
        i18n,
        i18n.str`Could not load the payment and balance status`,
        hook,
      ),
    };
  }
  // if (hook.hasError) {
  //   return {
  //     status: "loading-uri",
  //     error: hook,
  //   };
  // }
  const { payStatus } = hook.response;

  const amount = Amounts.parseOrThrow(payStatus.amountRaw);

  const foundBalance = hook.response.balance.balances.find(
    (b) => Amounts.parseOrThrow(b.available).currency === amount.currency,
  );

  const baseResult = {
    uri: hook.response.uri,
    amount,
    error: undefined,
    cancel,
    goToWalletManualWithdraw,
  };

  if (!foundBalance) {
    return {
      status: "no-balance-for-currency",
      balance: undefined,
      payStatus,
      ...baseResult,
    };
  }

  const foundAmount = Amounts.parseOrThrow(foundBalance.available);

  if (payStatus.status === PreparePayResultType.InsufficientBalance) {
    return {
      status: "no-enough-balance",
      balance: foundAmount,
      payStatus,
      ...baseResult,
    };
  }

  if (payStatus.status === PreparePayResultType.AlreadyConfirmed) {
    return {
      status: "confirmed",
      balance: foundAmount,
      payStatus,
      ...baseResult,
    };
  }

  async function doPayment(): Promise<void> {
    const res = await api.wallet.call(WalletApiOperation.ConfirmPay, {
      proposalId: payStatus.proposalId,
    });
    // handle confirm pay
    if (res.type !== ConfirmPayResultType.Done) {
      onSuccess(res.transactionId);
      return;
    }
    const fu = res.contractTerms.fulfillment_url;
    if (fu) {
      if (typeof window !== "undefined") {
        document.location.href = fu;
      }
    }
    onSuccess(res.transactionId);
  }

  const payHandler: ButtonHandler = {
    onClick: pushAlertOnError(doPayment),
  };

  // (payStatus.status === PreparePayResultType.PaymentPossible)
  return {
    status: "ready",
    payHandler,
    payStatus,
    ...baseResult,
    balance: foundAmount,
  };
}
