/*
 This file is part of GNU Taler
 (C) 2020-2025 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 {
  AccessToken,
  ConfirmPayResultType,
  DonauHttpClient,
  Duration,
  j2s,
  MerchantContractOutputType,
  MerchantContractVersion,
  OrderOutputType,
  OrderVersion,
  PreparePayResultType,
  succeedOrThrow,
  TalerMerchantInstanceHttpClient,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { defaultCoinConfig } from "../harness/denomStructures.js";
import {
  createSimpleTestkudosEnvironmentV3,
  withdrawViaBankV3,
} from "../harness/environments.js";
import { DonauService } from "../harness/harness-donau.js";
import {
  delayMs,
  getTestHarnessPaytoForLabel,
  GlobalTestState,
} from "../harness/harness.js";

export async function runDonauMultiTest(t: GlobalTestState) {
  // Set up test environment

  const {
    walletClient,
    bankClient,
    exchange,
    merchant,
    merchantAdminAccessToken,
    commonDb,
  } = await createSimpleTestkudosEnvironmentV3(t, undefined, {
    merchantUseDonau: true,
    walletConfig: {
      features: {
        enableV1Contracts: true,
      },
    },
  });

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

  const donau = DonauService.create(t, {
    currency: "TESTKUDOS",
    database: commonDb.connStr,
    httpPort: 8084,
    name: "donau",
    domain: "Bern",
  });

  donau.addCoinConfigList(defaultCoinConfig.map((x) => x("TESTKUDOS")));

  await donau.start();

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

  const inst1 = succeedOrThrow(
    await merchantClient1.getCurrentInstanceDetails(merchantAdminAccessToken),
  );
  const merchantPub1 = inst1.merchant_pub;

  const donauClient = new DonauHttpClient(donau.baseUrl);

  const currentYear = new Date().getFullYear();

  const charityResp1 = succeedOrThrow(
    await donauClient.createCharity("" as AccessToken, {
      charity_pub: merchantPub1,
      current_year: currentYear,
      max_per_year: "TESTKUDOS:1000",
      charity_name: "42",
      receipts_to_date: "TESTKUDOS:0",
      charity_url: merchant.makeInstanceBaseUrl(),
    }),
  );

  const charityId1 = charityResp1["charity_id"];

  succeedOrThrow(
    await merchantClient1.postDonau({
      body: {
        charity_id: charityId1,
        donau_url: donau.baseUrl,
      },
      token: merchantAdminAccessToken,
    }),
  );

  // Second merchant instance with donau

  const { accessToken: tok2 } = await merchant.addInstanceWithWireAccount(
    {
      id: "minst2",
      name: "minst2",
      paytoUris: [getTestHarnessPaytoForLabel("minst2")],
      defaultWireTransferDelay: Duration.toTalerProtocolDuration(
        Duration.fromSpec({ minutes: 1 }),
      ),
    },
    { adminAccessToken: merchantAdminAccessToken },
  );

  const merchantClient2 = new TalerMerchantInstanceHttpClient(
    merchant.makeInstanceBaseUrl("minst2"),
  );

  const inst2 = succeedOrThrow(
    await merchantClient2.getCurrentInstanceDetails(tok2),
  );
  const merchantPub2 = inst2.merchant_pub;

  console.log(`pub1: ${merchantPub1}`);
  console.log(`pub2: ${merchantPub2}`);

  const charityResp2 = succeedOrThrow(
    await donauClient.createCharity("" as AccessToken, {
      charity_pub: merchantPub2,
      current_year: currentYear,
      max_per_year: "TESTKUDOS:1000",
      charity_name: "43",
      receipts_to_date: "TESTKUDOS:0",
      charity_url: merchant.makeInstanceBaseUrl("minst2"),
    }),
  );

  const charityId2 = charityResp2["charity_id"];

  succeedOrThrow(
    await merchantClient2.postDonau({
      body: {
        charity_id: charityId2,
        donau_url: donau.baseUrl,
      },
      token: tok2,
    }),
  );

  // Wait for donaukeysupdate
  // We don't use -t since it doesn't seem to work at the moment.
  await delayMs(2000);

  {
    // Just test the GetDonau request (initial)
    const getRes = await walletClient.call(WalletApiOperation.GetDonau, {});
    t.assertDeepEqual(getRes.currentDonauInfo, undefined);
  }

  await walletClient.call(WalletApiOperation.SetDonau, {
    donauBaseUrl: donau.baseUrl,
    taxPayerId: "test-tax-payer",
  });

  {
    // Just test the GetDonau request (after setting)
    const getRes = await walletClient.call(WalletApiOperation.GetDonau, {});
    t.assertTrue(getRes.currentDonauInfo != null);
    t.assertDeepEqual(getRes.currentDonauInfo.donauBaseUrl, donau.baseUrl);
  }

  for (let i = 0; i < 3; i++) {
    const orderResp = succeedOrThrow(
      await merchantClient1.createOrder(merchantAdminAccessToken, {
        order: {
          version: OrderVersion.V1,
          summary: "Test Donation",
          choices: [
            {
              amount: "TESTKUDOS:9",
              outputs: [
                {
                  type: OrderOutputType.TaxReceipt,
                  amount: "TESTKUDOS:9",
                  donau_urls: [donau.baseUrl],
                },
              ],
            },
          ],
        },
      }),
    );

    console.log(`order resp: ${j2s(orderResp)}`);

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

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

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

    console.log(`preparePayResult: ${j2s(preparePayResult)}`);

    t.assertTrue(
      preparePayResult.status === PreparePayResultType.ChoiceSelection,
    );

    t.assertDeepEqual(
      preparePayResult.contractTerms.version,
      MerchantContractVersion.V1,
    );
    const outTok = preparePayResult.contractTerms.choices[0].outputs[0];
    t.assertDeepEqual(outTok.type, MerchantContractOutputType.TaxReceipt);

    // t.assertTrue(!!outTok.amount);

    // t.assertAmountEquals(outTok.amount, "TESTKUDOS:5");

    const r2 = await walletClient.call(WalletApiOperation.ConfirmPay, {
      transactionId: preparePayResult.transactionId,
      choiceIndex: 0,
      useDonau: true,
    });

    t.assertDeepEqual(r2.type, ConfirmPayResultType.Done);

    // Check if payment was successful.

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

    t.assertDeepEqual(orderStatus.order_status, "paid");
  }

  const statements = await walletClient.call(
    WalletApiOperation.GetDonauStatements,
    {},
  );
  console.log(j2s(statements));
}

runDonauMultiTest.suites = ["donau"];
