/*
 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 {
  AbsoluteTime,
  Duration,
  succeedOrThrow,
  TalerMerchantInstanceHttpClient,
  TransactionMajorState,
  TransactionType,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import {
  applyTimeTravelV2,
  createSimpleTestkudosEnvironmentV3,
  withdrawViaBankV3,
} from "../harness/environments.js";
import { GlobalTestState } from "../harness/harness.js";

/**
 * Test wallet behavior when a refund expires before the wallet
 * can claim it.
 */
export async function runRefundGoneTest(t: GlobalTestState) {
  // Set up test environment

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

  const merchantClient = new TalerMerchantInstanceHttpClient(
    merchant.makeInstanceBaseUrl(),
  );

  // Withdraw digital cash into the wallet.

  const wres = await withdrawViaBankV3(t, {
    walletClient,
    bankClient,
    exchange,
    amount: "TESTKUDOS:20",
  });

  await wres.withdrawalFinishedCond;

  // Set up order.

  const orderResp = succeedOrThrow(
    await merchantClient.createOrder(merchantAdminAccessToken, {
      order: {
        summary: "Buy me!",
        amount: "TESTKUDOS:5",
        fulfillment_url: "taler://fulfillment-success/thx",
        pay_deadline: AbsoluteTime.toProtocolTimestamp(
          AbsoluteTime.addDuration(
            AbsoluteTime.now(),
            Duration.fromSpec({
              minutes: 10,
            }),
          ),
        ),
      },
      refund_delay: Duration.toTalerProtocolDuration(
        Duration.fromSpec({ minutes: 1 }),
      ),
    }),
  );

  let orderStatus = succeedOrThrow(
    await merchantClient.getOrderDetails(
      merchantAdminAccessToken,
      orderResp.order_id,
    ),
  );

  t.assertTrue(orderStatus.order_status === "unpaid");

  // Make wallet pay for the order

  const r1 = await walletClient.call(WalletApiOperation.PreparePayForUri, {
    talerPayUri: orderStatus.taler_pay_uri,
  });

  const r2 = await walletClient.call(WalletApiOperation.ConfirmPay, {
    transactionId: r1.transactionId,
  });

  // Check if payment was successful.

  orderStatus = succeedOrThrow(
    await merchantClient.getOrderDetails(
      merchantAdminAccessToken,
      orderResp.order_id,
    ),
  );

  t.assertTrue(orderStatus.order_status === "paid");

  console.log(orderStatus);

  const ref = succeedOrThrow(
    await merchantClient.addRefund(
      merchantAdminAccessToken,
      orderResp.order_id,
      {
        refund: "TESTKUDOS:5",
        reason: "foo",
      },
    ),
  );

  console.log(ref);

  await applyTimeTravelV2(
    Duration.toMilliseconds(Duration.fromSpec({ hours: 1 })),
    { exchange, merchant, walletClient: walletClient },
  );
  await exchange.stopAggregator();
  await exchange.runAggregatorOnce();

  await walletClient.call(WalletApiOperation.StartRefundQuery, {
    transactionId: r1.transactionId,
  });

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

  let r = await walletClient.call(WalletApiOperation.GetBalances, {});
  console.log(JSON.stringify(r, undefined, 2));

  const r3 = await walletClient.call(WalletApiOperation.GetTransactions, {});
  console.log(JSON.stringify(r3, undefined, 2));

  const refundTx = r3.transactions[2];

  t.assertDeepEqual(refundTx.type, TransactionType.Refund);
  t.assertDeepEqual(refundTx.txState.major, TransactionMajorState.Failed);
}

runRefundGoneTest.suites = ["wallet"];
