/*
 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 
 */
import {
  Amounts,
  TalerError,
  TalerErrorCode,
  TalerProtocolTimestamp,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { isFuture, parse } from "date-fns";
import { useEffect, useState } 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 { Props, State } from "./index.js";
import { BackgroundError, WxApiType } from "../../wxApi.js";
export function useComponentState({
  amount: amountStr,
  onClose,
  onSuccess,
}: Props): State {
  const api = useBackendContext();
  const { pushAlertOnError } = useAlertContext();
  const amount = Amounts.parseOrThrow(amountStr);
  const { i18n } = useTranslationContext();
  const [subject, setSubject] = useState();
  const [timestamp, setTimestamp] = useState();
  const hook = useAsyncAsHook(async () => {
    const resp = await checkPeerPushDebitAndCheckMax(api, amountStr);
    return resp;
  });
  if (!hook) {
    return {
      status: "loading",
      error: undefined,
    };
  }
  if (hook.hasError) {
    return {
      status: "error",
      error: alertFromError(
        i18n.str`Could not load the status of the term of service`,
        hook,
      ),
    };
  }
  const { amountEffective, amountRaw } = hook.response;
  const debitAmount = Amounts.parseOrThrow(amountEffective);
  const toBeReceived = Amounts.parseOrThrow(amountRaw);
  let purse_expiration: TalerProtocolTimestamp | undefined = undefined;
  let timestampError: string | undefined = undefined;
  const t =
    timestamp === undefined
      ? undefined
      : parse(timestamp, "dd/MM/yyyy", new Date());
  if (t !== undefined) {
    if (Number.isNaN(t.getTime())) {
      timestampError = 'Should have the format "dd/MM/yyyy"';
    } else {
      if (!isFuture(t)) {
        timestampError = "Should be in the future";
      } else {
        purse_expiration = {
          t_s: t.getTime() / 1000,
        };
      }
    }
  }
  async function accept(): Promise {
    if (!subject || !purse_expiration) return;
    const resp = await api.wallet.call(
      WalletApiOperation.InitiatePeerPushDebit,
      {
        partialContractTerms: {
          summary: subject,
          amount: amountStr,
          purse_expiration,
        },
      },
    );
    onSuccess(resp.transactionId);
  }
  const unableToCreate =
    !subject || Amounts.isZero(amount) || !purse_expiration;
  return {
    status: "ready",
    cancel: {
      onClick: pushAlertOnError(onClose),
    },
    subject: {
      error:
        subject === undefined
          ? undefined
          : !subject
          ? "Can't be empty"
          : undefined,
      value: subject ?? "",
      onInput: pushAlertOnError(async (e) => setSubject(e)),
    },
    expiration: {
      error: timestampError,
      value: timestamp === undefined ? "" : timestamp,
      onInput: pushAlertOnError(async (e) => {
        setTimestamp(e);
      }),
    },
    create: {
      onClick: unableToCreate ? undefined : pushAlertOnError(accept),
    },
    debitAmount,
    toBeReceived,
    error: undefined,
  };
}
async function checkPeerPushDebitAndCheckMax(
  api: WxApiType,
  amountState: string,
) {
  // FIXME : https://bugs.gnunet.org/view.php?id=7872
  try {
    return await api.wallet.call(WalletApiOperation.CheckPeerPushDebit, {
      amount: amountState,
    });
  } catch (e) {
    if (!(e instanceof BackgroundError)) {
      throw e;
    }
    if (
      !e.hasErrorCode(
        TalerErrorCode.WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE,
      )
    ) {
      throw e;
    }
    const material = Amounts.parseOrThrow(
      e.errorDetail.insufficientBalanceDetails.balanceMaterial,
    );
    const gap = Amounts.parseOrThrow(
      e.errorDetail.insufficientBalanceDetails.feeGapEstimate,
    );
    const newAmount = Amounts.sub(material, gap).amount;
    const amount = Amounts.parseOrThrow(amountState);
    if (Amounts.cmp(newAmount, amount) === 0) {
      //insufficient balance and the exception didn't give
      //a good response that allow us to try again
      throw e;
    }
    return checkPeerPushDebitAndCheckMax(api, Amounts.stringify(newAmount));
  }
}