/*
 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,
  codecForKycProcessClientInformation,
  codecForQueryInstancesResponse,
  Configuration,
  j2s,
  Logger,
  MerchantAccountKycRedirectsResponse,
  MerchantAccountKycStatus,
  succeedOrThrow,
  TalerMerchantApi,
} from "@gnu-taler/taler-util";
import {
  readResponseJsonOrThrow,
  readSuccessResponseJsonOrThrow,
} from "@gnu-taler/taler-util/http";
import {
  configureCommonKyc,
  createKycTestkudosEnvironment,
} from "../harness/environments.js";
import {
  delayMs,
  GlobalTestState,
  harnessHttpLib,
} from "../harness/harness.js";

const logger = new Logger(`test-kyc-merchant-deposit.ts`);

const myAmlConfig = `
# Fallback measure on errors.
[kyc-measure-freeze-investigate]
CHECK_NAME = skip
PROGRAM = freeze-investigate
VOLUNTARY = NO
CONTEXT = {}

[aml-program-freeze-investigate]
DESCRIPTION = "Fallback measure on errors that freezes the account and asks AML staff to investigate the system failure."
COMMAND = taler-exchange-helper-measure-freeze
ENABLED = YES
FALLBACK = freeze-investigate

[aml-program-inform-investigate]
DESCRIPTION = "Measure that asks AML staff to investigate an account and informs the account owner about it."
COMMAND = taler-exchange-helper-measure-inform-investigate
ENABLED = YES
FALLBACK = freeze-investigate

[kyc-check-form-gls-merchant-onboarding]
TYPE = FORM
FORM_NAME = gls-merchant-onboarding
DESCRIPTION = "GLS Merchant Onboarding"
DESCRIPTION_I18N = {}
OUTPUTS =
FALLBACK = freeze-investigate

[kyc-measure-merchant-onboarding]
CHECK_NAME = form-gls-merchant-onboarding
PROGRAM = inform-investigate
CONTEXT = {}
VOLUNTARY = NO

[kyc-rule-deposit-limit-zero]
OPERATION_TYPE = DEPOSIT
NEXT_MEASURES = merchant-onboarding
EXPOSED = YES
ENABLED = YES
THRESHOLD = TESTKUDOS:0
TIMEFRAME = "1 days"
`;

function adjustExchangeConfig(config: Configuration) {
  configureCommonKyc(config);
  config.loadFromString(myAmlConfig);
}

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

  const {
    merchant,
    bankClient,
    exchange,
    bank,
    wireGatewayApi,
    merchantAdminAccessToken,
  } = await createKycTestkudosEnvironment(t, {
    adjustExchangeConfig,
  });

  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;
  }

  // Withdraw digital cash into the wallet.

  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) {
      const pr = await readSuccessResponseJsonOrThrow(
        resp,
        codecForAccountKycRedirects(),
      );
      if (
        pr.kyc_data[0].status === MerchantAccountKycStatus.EXCHANGE_UNREACHABLE
      ) {
        logger.info(`exchange still unreachable according to merchant`);
      } else {
        kycRespOne = pr;
        break;
      }
    }
    // Wait 500ms
    await delayMs(500);
  }

  t.assertTrue(!!kycRespOne);

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

  t.assertDeepEqual(
    kycRespOne.kyc_data[0].status,
    MerchantAccountKycStatus.KYC_WIRE_REQUIRED,
  );

  t.assertDeepEqual(kycRespOne.kyc_data[0].exchange_http_status, 404);

  t.assertTrue(
    (kycRespOne.kyc_data[0].limits?.length ?? 0) > 0,
    "kyc status should contain non-empty limits",
  );

  // Order creation should fail!
  await t.runSpanAsync("order creation should be rejected", async () => {
    let url = new URL("private/orders", merchant.makeInstanceBaseUrl());
    const order = {
      summary: "Test",
      amount: "TESTKUDOS:5",
      fulfillment_url: "taler://fulfillment-success/thx",
    } satisfies TalerMerchantApi.Order;
    const resp = await harnessHttpLib.fetch(url.href, {
      method: "POST",
      body: {
        order,
      },
      headers,
    });

    logger.info(`order creation status: ${resp.status}`);
    t.assertTrue(resp.status !== 200);
  });

  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
  });
  succeedOrThrow(
    await wireGatewayApi.addKycAuth({
      auth: bank.getAdminAuth(),
      body: {
        amount: "TESTKUDOS:0.1",
        account_pub: accountPub,
        debit_account: kycRespOne.kyc_data[0].payto_uri,
      },
    }),
  );

  let kycRespTwo: MerchantAccountKycRedirectsResponse | undefined = undefined;

  // We do this in a loop as a work-around.
  // Not exactly the correct behavior from the merchant right now.
  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].payto_kycauths == null) {
      kycRespTwo = parsedResp;
      break;
    }
    // Wait 500ms
    await delayMs(500);
  }

  // FIXME: Use exchange client!

  const infoResp = await harnessHttpLib.fetch(
    new URL(`kyc-info/${kycRespTwo.kyc_data[0].access_token}`, exchange.baseUrl)
      .href,
  );

  const clientInfo = await readResponseJsonOrThrow(
    infoResp,
    codecForKycProcessClientInformation(),
  );

  console.log(j2s(clientInfo));

  const kycId = clientInfo.requirements[0].id;
  t.assertTrue(typeof kycId === "string");

  const uploadResp = await harnessHttpLib.fetch(
    new URL(`kyc-upload/${kycId}`, exchange.baseUrl).href,
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: {
        full_name: "Alice Abc",
        birthdate: "2000-01-01",
      },
    },
  );

  console.log("resp status", uploadResp.status);
}

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