/*
 This file is part of GNU Taler
 (C) 2024 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 {
  AmountString,
  encodeCrock,
  getRandomBytes,
  j2s,
  TalerError,
} from "@gnu-taler/taler-util";
import {
  CryptoDispatcher,
  SynchronousCryptoWorkerFactoryPlain,
} from "@gnu-taler/taler-wallet-core";
import {
  checkReserve,
  CoinInfo,
  depositCoinBatch,
  downloadExchangeInfo,
  findDenomOrThrow,
  topupReserveWithBank,
  withdrawCoin,
} from "@gnu-taler/taler-wallet-core/dbless";
import { CoinConfig } from "../harness/denomStructures.js";
import { createSimpleTestkudosEnvironmentV2 } from "../harness/environments.js";
import { GlobalTestState, harnessHttpLib } from "../harness/harness.js";

const coinCommon = {
  cipher: "RSA" as const,
  durationLegal: "3 years",
  durationSpend: "2 years",
  durationWithdraw: "7 days",
  feeDeposit: "TESTKUDOS:0",
  feeRefresh: "TESTKUDOS:0",
  feeRefund: "TESTKUDOS:0",
  feeWithdraw: "TESTKUDOS:0",
  rsaKeySize: 1024,
};

const coinConfigList: CoinConfig[] = [
  {
    ...coinCommon,
    name: "n1",
    value: "TESTKUDOS:1",
  },
];

/**
 * Test deposit with a large number of coins.
 *
 * In particular, this checks that the wallet properly
 * splits deposits into batches with <=64 coins per batch.
 *
 * Since we use an artificially large number of coins, this
 * test is a bit slower than other tests.
 */
export async function runDepositTooLargeTest(t: GlobalTestState) {
  // Set up test environment

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

  const http = harnessHttpLib;
  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({});

    let reserveUrl = new URL(
      `reserves/${reserveKeyPair.pub}`,
      exchange.baseUrl,
    );
    reserveUrl.searchParams.set("timeout_ms", "30000");
    const longpollReq = http.fetch(reserveUrl.href, {
      method: "GET",
    });

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

    console.log("waiting for longpoll request");
    const resp = await longpollReq;
    console.log(`got response, status ${resp.status}`);

    console.log(exchangeInfo);

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

    const d1 = findDenomOrThrow(exchangeInfo, "TESTKUDOS:0.1" as AmountString);

    const coins: CoinInfo[] = [];
    const amounts: AmountString[] = [];

    for (let i = 0; i < 100; i++) {
      const coin = await withdrawCoin({
        http,
        cryptoApi,
        reserveKeyPair: {
          reservePriv: reserveKeyPair.priv,
          reservePub: reserveKeyPair.pub,
        },
        denom: d1,
        exchangeBaseUrl: exchange.baseUrl,
      });
      coins.push(coin);
      amounts.push("TESTKUDOS:0.1");
    }

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

    await depositCoinBatch({
      contractTermsHash,
      merchantPriv,
      wireSalt,
      amounts,
      coins,
      cryptoApi,
      exchangeBaseUrl: exchange.baseUrl,
      http,
    });
  } catch (e) {
    if (e instanceof TalerError) {
      console.log(e);
      console.log(j2s(e.errorDetail));
    } else {
      console.log(e);
    }
  }

  {
    // Try downloading exchange info again to make
    // sure that exchange is still running and didn't crash!
    const exchangeInfo = await downloadExchangeInfo(exchange.baseUrl, http);
  }
}

runDepositTooLargeTest.suites = ["wallet", "slow"];
