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

/**
 * Imports.
 */
import {
  codecForAccountKycRedirects,
  codecForQueryInstancesResponse,
  j2s,
  Logger,
  MerchantAccountKycRedirectsResponse,
  MerchantAccountKycStatus,
  parsePaytoUri,
  succeedOrThrow,
} from "@gnu-taler/taler-util";
import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
import { createKycTestkudosEnvironment } from "../harness/environments.js";
import {
  delayMs,
  GlobalTestState,
  harnessHttpLib,
} from "../harness/harness.js";

const logger = new Logger(`test-kyc-merchant-activate-bank-account.ts`);

export async function runKycMerchantActivateBankAccountTest(
  t: GlobalTestState,
) {
  // Set up test environment
  const {
    merchant,
    bankClient,
    bank,
    wireGatewayApi,
    merchantAdminAccessToken,
  } = await createKycTestkudosEnvironment(t, {
    adjustExchangeConfig(config) {
      config.setString("exchange", "enable_kyc", "yes");

      // no kyc deposit requirement, the exchange
      // just need the merchant to prove the that it is the
      // legal owner of the account making a simple
      // wire transfer
    },
  });

  let accountPub: string;
  const headers = {
    Authorization: `Bearer ${merchantAdminAccessToken}`,
  };

  {
    const instanceUrl = new URL("private", merchant.makeInstanceBaseUrl());
    const resp = await harnessHttpLib.fetch(instanceUrl.href, { headers });
    const parsedResp = await readSuccessResponseJsonOrThrow(
      resp,
      codecForQueryInstancesResponse(),
    );
    accountPub = parsedResp.merchant_pub;
  }

  let kycRespOne: MerchantAccountKycRedirectsResponse | undefined = undefined;

  while (1) {
    const kycStatusUrl = new URL("private/kyc", merchant.makeInstanceBaseUrl())
      .href;
    logger.info(`requesting GET ${kycStatusUrl}`);
    const resp = await harnessHttpLib.fetch(kycStatusUrl, { headers });
    if (resp.status === 200) {
      kycRespOne = await readSuccessResponseJsonOrThrow(
        resp,
        codecForAccountKycRedirects(),
      );
      if (
        kycRespOne.kyc_data[0].status ==
        MerchantAccountKycStatus.EXCHANGE_UNREACHABLE
      ) {
        logger.info(`merchant claims exchange is not reachable (yet)`);
      } else {
        break;
      }
    }
    // Wait 500ms
    await delayMs(500);
  }

  t.assertTrue(!!kycRespOne);

  logger.info(`mechant kyc status: ${j2s(kycRespOne)}`);

  // the exchange replies with 404
  // meaning that the bank account is not yet known by the exchange
  t.assertDeepEqual(kycRespOne.kyc_data[0].exchange_http_status, 404);

  // prove that we own the account by sending some money from
  // the merchant account
  bankClient.setAuth({
    username: "merchant-default",
    password: "merchant-default",
  });

  await bankClient.registerAccountExtended({
    name: "merchant-default",
    password: "merchant-default",
    username: "merchant-default",
    payto_uri: kycRespOne.kyc_data[0].payto_uri, //this bank user needs to have the same payto that the exchange is asking from
  });

  const kycauthPayto = kycRespOne.kyc_data[0].payto_kycauths![0];
  logger.info(`kycauth payto: ${kycauthPayto}`);
  const p = parsePaytoUri(kycauthPayto);

  const msgAccountPub = p?.params["message"];
  t.assertTrue(!!accountPub);

  // FIXME: This is kinda brittle, would be better to pick out
  // what looks like a public key from the message.
  t.assertDeepEqual(`KYC:${accountPub}`, msgAccountPub);

  succeedOrThrow(
    await wireGatewayApi.addKycAuth({
      auth: bank.getAdminAuth(),
      body: {
        amount: "TESTKUDOS:0.1",
        debit_account: kycRespOne.kyc_data[0].payto_uri,
        account_pub: accountPub,
      },
    }),
  );

  let kycRespTwo: MerchantAccountKycRedirectsResponse | undefined = undefined;

  // Loop requesting the KYC status.
  // The merchant currently doesn't support long-polling for this.
  while (true) {
    const kycStatusLongpollUrl = new URL(
      "private/kyc",
      merchant.makeInstanceBaseUrl(),
    );
    kycStatusLongpollUrl.searchParams.set("lpt", "1");
    const resp = await harnessHttpLib.fetch(kycStatusLongpollUrl.href, {
      headers,
    });
    t.assertDeepEqual(resp.status, 200);
    const parsedResp = await readSuccessResponseJsonOrThrow(
      resp,
      codecForAccountKycRedirects(),
    );
    logger.info(`kyc resp 2: ${j2s(parsedResp)}`);
    if (
      parsedResp.kyc_data[0].status !==
      MerchantAccountKycStatus.EXCHANGE_UNREACHABLE
    ) {
      if (parsedResp.kyc_data[0].payto_kycauths == null) {
        kycRespTwo = parsedResp;
        break;
      }
    } else {
      logger.info(`kyc status is exchange-unreachable`);
    }
    // Wait 500ms
    await delayMs(500);
  }

  t.assertTrue(!!kycRespTwo);

  t.assertDeepEqual(kycRespTwo.kyc_data[0].exchange_http_status, 200);
}

runKycMerchantActivateBankAccountTest.suites = ["wallet", "merchant", "kyc"];
