/*
 This file is part of GNU Taler
 (C) 2021 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 {
  Duration,
  j2s,
  PaytoString,
  succeedOrThrow,
  TalerMerchantInstanceHttpClient,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { defaultCoinConfig } from "../harness/denomStructures.js";
import { createWalletDaemonWithClient } from "../harness/environments.js";
import {
  ExchangeService,
  GlobalTestState,
  MerchantService,
  setupDb,
} from "../harness/harness.js";

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

  const db = await setupDb(t);

  const { walletClient } = await createWalletDaemonWithClient(t, {
    name: "wallet",
  });

  const exchange1 = ExchangeService.create(t, {
    name: "testexchange-1",
    currency: "TESTKUDOS",
    httpPort: 8081,
    database: db.connStr,
  });

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

  const paytoXtb =
    "payto://x-taler-bank/localhost/exchange?receiver-name=exchange" as PaytoString;

  const paytoIban =
    "payto://iban/DE76500202009817493529?receiver-name=exchange" as PaytoString;

  await exchange1.addBankAccount("1", {
    accountPaytoUri: paytoXtb,
    wireGatewayApiBaseUrl: "http://localhost:8082/",
    wireGatewayAuth: {
      password: "test",
      username: "test",
    },
  });

  const exchange2 = ExchangeService.create(t, {
    name: "testexchange-2",
    currency: "TESTKUDOS",
    httpPort: 8181,
    database: db.connStr,
  });

  await exchange2.addBankAccount("1", {
    accountPaytoUri: paytoIban,
    wireGatewayApiBaseUrl: "http://localhost:8082/",
    wireGatewayAuth: {
      password: "test",
      username: "test",
    },
  });

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

  const merchant = await MerchantService.create(t, {
    name: "testmerchant-1",
    httpPort: 8083,
    database: db.connStr,
  });

  merchant.addExchange(exchange1);
  merchant.addExchange(exchange2);

  await exchange1.start();
  await exchange2.start();

  await merchant.start();
  await merchant.pingUntilAvailable();

  const { accessToken: adminAccessToken } =
    await merchant.addInstanceWithWireAccount({
      id: "admin",
      name: "Default Instance",
      paytoUris: [paytoIban, paytoXtb],
      defaultWireTransferDelay: Duration.toTalerProtocolDuration(
        Duration.fromSpec({ minutes: 1 }),
      ),
    });

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

  {
    const ordResp1 = succeedOrThrow(
      await merchApi.createOrder(adminAccessToken, {
        payment_target: "iban",
        order: {
          amount: "TESTKUDOS:5",
          summary: "Test!",
        },
      }),
    );

    console.log(j2s(ordResp1));

    const ordDet1 = succeedOrThrow(
      await merchApi.getOrderDetails(adminAccessToken, ordResp1.order_id),
    );

    t.assertDeepEqual(ordDet1.order_status, "unpaid");

    await walletClient.call(WalletApiOperation.PreparePayForUri, {
      talerPayUri: ordDet1.taler_pay_uri,
    });

    const ordDet2 = succeedOrThrow(
      await merchApi.getOrderDetails(adminAccessToken, ordResp1.order_id),
    );

    console.log(j2s(ordDet2));

    t.assertDeepEqual(ordDet2.order_status, "claimed");

    const numExch1 = ordDet2.contract_terms.exchanges.filter(
      (x) => x.url === exchange1.baseUrl,
    ).length;
    const numExch2 = ordDet2.contract_terms.exchanges.filter(
      (x) => x.url === exchange2.baseUrl,
    ).length;

    t.assertDeepEqual(numExch1, 0);
    t.assertDeepEqual(numExch2, 1);
  }

  {
    const ordResp1 = succeedOrThrow(
      await merchApi.createOrder(adminAccessToken, {
        payment_target: "x-taler-bank",
        order: {
          amount: "TESTKUDOS:5",
          summary: "Test!",
        },
      }),
    );

    console.log(j2s(ordResp1));

    const ordDet1 = succeedOrThrow(
      await merchApi.getOrderDetails(adminAccessToken, ordResp1.order_id),
    );

    t.assertDeepEqual(ordDet1.order_status, "unpaid");

    await walletClient.call(WalletApiOperation.PreparePayForUri, {
      talerPayUri: ordDet1.taler_pay_uri,
    });

    const ordDet2 = succeedOrThrow(
      await merchApi.getOrderDetails(adminAccessToken, ordResp1.order_id),
    );

    console.log(j2s(ordDet2));

    t.assertDeepEqual(ordDet2.order_status, "claimed");

    const numExch1 = ordDet2.contract_terms.exchanges.filter(
      (x) => x.url === exchange1.baseUrl,
    ).length;
    const numExch2 = ordDet2.contract_terms.exchanges.filter(
      (x) => x.url === exchange2.baseUrl,
    ).length;

    t.assertDeepEqual(numExch1, 1);
    t.assertDeepEqual(numExch2, 0);
  }
}

runMerchantAcctselTest.suites = ["merchant"];
