/*
 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 {
  codecForAny,
  codecForKycProcessClientInformation,
  Configuration,
  decodeCrock,
  encodeCrock,
  j2s,
  signAmlQuery,
  TransactionIdStr,
  TransactionMajorState,
  TransactionMinorState,
} from "@gnu-taler/taler-util";
import { readResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import {
  configureCommonKyc,
  createKycTestkudosEnvironment,
  withdrawViaBankV3,
} from "../harness/environments.js";
import { GlobalTestState, harnessHttpLib } from "../harness/harness.js";

function adjustExchangeConfig(config: Configuration) {
  configureCommonKyc(config);

  config.setString("KYC-RULE-R1", "operation_type", "withdraw");
  config.setString("KYC-RULE-R1", "enabled", "yes");
  config.setString("KYC-RULE-R1", "exposed", "yes");
  config.setString("KYC-RULE-R1", "is_and_combinator", "yes");
  config.setString("KYC-RULE-R1", "threshold", "TESTKUDOS:5");
  config.setString("KYC-RULE-R1", "timeframe", "1d");
  config.setString("KYC-RULE-R1", "next_measures", "M1 M2");

  config.setString("KYC-MEASURE-M1", "check_name", "C1");
  config.setString("KYC-MEASURE-M1", "context", "{}");
  config.setString("KYC-MEASURE-M1", "program", "P1");

  config.setString("KYC-MEASURE-M2", "check_name", "C2");
  config.setString("KYC-MEASURE-M2", "context", "{}");
  config.setString("KYC-MEASURE-M2", "program", "NONE");

  config.setString(
    "AML-PROGRAM-P1",
    "command",
    "taler-exchange-helper-measure-test-form",
  );
  config.setString("AML-PROGRAM-P1", "enabled", "true");
  config.setString(
    "AML-PROGRAM-P1",
    "description",
    "test for FULL_NAME and DATE_OF_BIRTH",
  );
  config.setString("AML-PROGRAM-P1", "description_i18n", "{}");
  config.setString("AML-PROGRAM-P1", "fallback", "FREEZE");

  config.setString("KYC-CHECK-C1", "type", "FORM");
  config.setString("KYC-CHECK-C1", "form_name", "myform");
  config.setString("KYC-CHECK-C1", "description", "my check!");
  config.setString("KYC-CHECK-C1", "description_i18n", "{}");
  config.setString("KYC-CHECK-C1", "outputs", "FULL_NAME DATE_OF_BIRTH");
  config.setString("KYC-CHECK-C1", "fallback", "FREEZE");

  config.setString("KYC-CHECK-C2", "type", "INFO");
  config.setString("KYC-CHECK-C2", "description", "my check info!");
  config.setString("KYC-CHECK-C2", "description_i18n", "{}");
  config.setString("KYC-CHECK-C2", "fallback", "FREEZE");
}

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

  const { walletClient, bankClient, exchange, amlKeypair } =
    await createKycTestkudosEnvironment(t, { adjustExchangeConfig });

  // Withdraw digital cash into the wallet.

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

  await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
    transactionId: wres.transactionId as TransactionIdStr,
    txState: {
      major: TransactionMajorState.Pending,
      minor: TransactionMinorState.KycRequired,
    },
  });

  const txDetails = await walletClient.call(
    WalletApiOperation.GetTransactionById,
    {
      transactionId: wres.transactionId,
    },
  );

  console.log(j2s(txDetails));
  const accessToken = txDetails.kycAccessToken;
  t.assertTrue(!!accessToken);

  const infoResp = await harnessHttpLib.fetch(
    new URL(`kyc-info/${txDetails.kycAccessToken}`, exchange.baseUrl).href,
  );

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

  console.log(j2s(clientInfo));

  const kycId = clientInfo.requirements.find((x) => x.id != null)?.id;
  t.assertTrue(!!kycId);

  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",
        DATE_OF_BIRTH: "2000-01-01",
        FORM_ID: "test",
      },
    },
  );

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

  t.assertDeepEqual(uploadResp.status, 204);

  {
    // Do a GET on kyc-info here as this reproduces a bug in the
    // exchange.
    const infoResp = await harnessHttpLib.fetch(
      new URL(`kyc-info/${txDetails.kycAccessToken}`, exchange.baseUrl).href,
    );

    await readResponseJsonOrThrow(
      infoResp,
      codecForKycProcessClientInformation(),
    );
  }

  const sig = signAmlQuery(decodeCrock(amlKeypair.priv));

  const decisionsResp = await harnessHttpLib.fetch(
    new URL(`aml/${amlKeypair.pub}/decisions`, exchange.baseUrl).href,
    {
      headers: {
        "Taler-AML-Officer-Signature": encodeCrock(sig),
      },
    },
  );

  const decisions = await readResponseJsonOrThrow(decisionsResp, codecForAny());
  console.log(j2s(decisions));

  t.assertDeepEqual(decisionsResp.status, 200);

  // KYC should pass now

  await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
    transactionId: wres.transactionId as TransactionIdStr,
    txState: {
      major: TransactionMajorState.Done,
    },
  });
}

runKycFormWithdrawalTest.suites = ["wallet"];
