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

/**
 * Integration test for the wallet testing functionality used by the exchange
 * test cases.
 */

/**
 * Imports.
 */
import { AmountString, Amounts, CoinStatus } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { createSimpleTestkudosEnvironmentV3 } from "../harness/environments.js";
import { GlobalTestState, setupDb } from "../harness/harness.js";

/**
 * Run test for basic, bank-integrated withdrawal.
 */
export async function runWallettestingTest(t: GlobalTestState) {
  const db = await setupDb(t);

  const { bankClient, walletClient, exchange, merchant, merchantAdminAccessToken } =
    await createSimpleTestkudosEnvironmentV3(t, undefined, {});

  await walletClient.call(WalletApiOperation.RunIntegrationTest, {
    amountToSpend: "TESTKUDOS:5" as AmountString,
    amountToWithdraw: "TESTKUDOS:10" as AmountString,
    corebankApiBaseUrl: bankClient.baseUrl,
    exchangeBaseUrl: exchange.baseUrl,
    merchantAuthToken: merchantAdminAccessToken,
    merchantBaseUrl: merchant.makeInstanceBaseUrl(),
  });

  let txns = await walletClient.call(WalletApiOperation.GetTransactions, {});
  console.log(JSON.stringify(txns, undefined, 2));
  let txTypes = txns.transactions.map((x) => x.type);

  t.assertDeepEqual(txTypes, [
    "withdrawal",
    "payment",
    "withdrawal",
    "payment",
    "refund",
    "payment",
  ]);

  await walletClient.call(WalletApiOperation.ClearDb, {});

  await walletClient.call(WalletApiOperation.WithdrawTestBalance, {
    amount: "TESTKUDOS:10" as AmountString,
    corebankApiBaseUrl: bankClient.baseUrl,
    exchangeBaseUrl: exchange.baseUrl,
  });

  await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});

  await walletClient.call(WalletApiOperation.TestPay, {
    amount: "TESTKUDOS:5" as AmountString,
    merchantAuthToken: merchantAdminAccessToken,
    merchantBaseUrl: merchant.makeInstanceBaseUrl(),
    summary: "foo",
  });

  await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});

  txns = await walletClient.call(WalletApiOperation.GetTransactions, {});
  console.log(JSON.stringify(txns, undefined, 2));
  txTypes = txns.transactions.map((x) => x.type);

  t.assertDeepEqual(txTypes, ["withdrawal", "payment"]);

  await walletClient.call(WalletApiOperation.ClearDb, {});

  await walletClient.call(WalletApiOperation.WithdrawTestBalance, {
    amount: "TESTKUDOS:10" as AmountString,
    corebankApiBaseUrl: bankClient.baseUrl,
    exchangeBaseUrl: exchange.baseUrl,
  });

  await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});

  const coinDump = await walletClient.call(WalletApiOperation.DumpCoins, {});

  console.log("coin dump:", JSON.stringify(coinDump, undefined, 2));

  let susp: string | undefined;
  {
    for (const c of coinDump.coins) {
      if (
        c.coinStatus === CoinStatus.Fresh &&
        0 === Amounts.cmp(c.denomValue, "TESTKUDOS:8")
      ) {
        susp = c.coinPub;
      }
    }
  }

  t.assertTrue(susp !== undefined);

  console.log("suspending coin");

  await walletClient.call(WalletApiOperation.SetCoinSuspended, {
    coinPub: susp,
    suspended: true,
  });

  // This should fail, as we've suspended a coin that we need
  // to pay.
  await t.assertThrowsAsync(async () => {
    await walletClient.call(WalletApiOperation.TestPay, {
      amount: "TESTKUDOS:5" as AmountString,
      merchantAuthToken: merchantAdminAccessToken,
      merchantBaseUrl: merchant.makeInstanceBaseUrl(),
      summary: "foo",
    });
  });

  console.log("unsuspending coin");

  await walletClient.call(WalletApiOperation.SetCoinSuspended, {
    coinPub: susp,
    suspended: false,
  });

  await walletClient.call(WalletApiOperation.TestPay, {
    amount: "TESTKUDOS:5" as AmountString,
    merchantAuthToken: merchantAdminAccessToken,
    merchantBaseUrl: merchant.makeInstanceBaseUrl(),
    summary: "foo",
  });

  await walletClient.call(WalletApiOperation.ClearDb, {});
  t.logStep("cleared-before-integration-test");
  await walletClient.call(WalletApiOperation.RunIntegrationTestV2, {
    corebankApiBaseUrl: bankClient.baseUrl,
    exchangeBaseUrl: exchange.baseUrl,
    merchantAuthToken: merchantAdminAccessToken,
    merchantBaseUrl: merchant.makeInstanceBaseUrl(),
  });
  t.logStep("after-integration-test");

  console.log("end of test");
}

runWallettestingTest.suites = ["wallet"];
