/*
 This file is part of GNU Taler
 (C) 2020 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/>
 */

/**
 * Imports.
 */
import {
  AmountString,
  encodeCrock,
  getRandomBytes,
  j2s,
  TalerError,
} from "@gnu-taler/taler-util";
import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
import {
  CryptoDispatcher,
  SynchronousCryptoWorkerFactoryPlain,
} from "@gnu-taler/taler-wallet-core";
import {
  checkReserve,
  depositCoin,
  downloadExchangeInfo,
  findDenomOrThrow,
  topupReserveWithBank,
  withdrawCoin,
} from "@gnu-taler/taler-wallet-core/dbless";
import { createSimpleTestkudosEnvironmentV2 } from "../harness/environments.js";
import { GlobalTestState } from "../harness/harness.js";

/**
 * Run test for basic, bank-integrated withdrawal and payment.
 */
export async function runExchangeDepositTest(t: GlobalTestState) {
  // Set up test environment

  const { bank, exchange } = await createSimpleTestkudosEnvironmentV2(t);

  const http = createPlatformHttpLib({
    enableThrottling: false,
  });
  const cryptiDisp = new CryptoDispatcher(
    new SynchronousCryptoWorkerFactoryPlain(),
  );
  const cryptoApi = cryptiDisp.cryptoApi;

  const merchantPair = await cryptoApi.createEddsaKeypair({});
  const merchantPub = merchantPair.pub;
  const merchantPriv = merchantPair.priv;

  try {
    // Withdraw digital cash into the wallet.

    const exchangeInfo = await downloadExchangeInfo(exchange.baseUrl, http);

    const reserveKeyPair = await cryptoApi.createEddsaKeypair({});

    await topupReserveWithBank({
      http,
      amount: "TESTKUDOS:10" as AmountString,
      corebankApiBaseUrl: bank.corebankApiBaseUrl,
      exchangeInfo,
      reservePub: reserveKeyPair.pub,
    });

    await exchange.runWirewatchOnce();

    await checkReserve(http, exchange.baseUrl, reserveKeyPair.pub);

    const d1 = findDenomOrThrow(
      exchangeInfo,
      "TESTKUDOS:8" as AmountString,
      {},
    );

    const coin = await withdrawCoin({
      http,
      cryptoApi,
      reserveKeyPair: {
        reservePriv: reserveKeyPair.priv,
        reservePub: reserveKeyPair.pub,
      },
      denom: d1,
      exchangeBaseUrl: exchange.baseUrl,
    });

    const wireSalt = encodeCrock(getRandomBytes(16));
    const contractTermsHash = encodeCrock(getRandomBytes(64));

    await depositCoin({
      contractTermsHash,
      merchantPriv,
      wireSalt,
      amount: "TESTKUDOS:4" as AmountString,
      coin: coin,
      cryptoApi,
      exchangeBaseUrl: exchange.baseUrl,
      http,
    });

    // Idempotency
    await depositCoin({
      contractTermsHash,
      merchantPriv,
      wireSalt,
      amount: "TESTKUDOS:4" as AmountString,
      coin: coin,
      cryptoApi,
      exchangeBaseUrl: exchange.baseUrl,
      http,
    });

    try {
      // Non-idempotent request with different amount
      await depositCoin({
        contractTermsHash,
        merchantPriv,
        wireSalt,
        amount: "TESTKUDOS:3.5" as AmountString,
        coin: coin,
        cryptoApi,
        exchangeBaseUrl: exchange.baseUrl,
        http,
      });
    } catch (e) {
      if (e instanceof TalerError && e.errorDetail.code === 7005) {
        if (e.errorDetail.httpStatusCode === 409) {
          console.log("got expected error response from exchange");
          console.log(e);
          console.log(j2s(e.errorDetail));
        } else {
          console.log("did not expect deposit error from exchange");
          throw e;
        }
      } else {
        throw e;
      }
    }
  } catch (e) {
    if (e instanceof TalerError) {
      console.log(e);
      console.log(j2s(e.errorDetail));
    } else {
      console.log(e);
    }
    throw e;
  }
}

runExchangeDepositTest.suites = ["exchange"];
