import {
  AbsoluteTime,
  AmountString,
  Configuration,
  Duration,
  j2s,
  TalerWireGatewayHttpClient,
  TransactionMajorState,
  TransactionMinorState,
  TransactionType,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import {
  configureCommonKyc,
  createKycTestkudosEnvironment,
  createWalletDaemonWithClient,
  postAmlDecisionNoRules,
  withdrawViaBankV3,
} from "../harness/environments.js";
import {
  getTestHarnessPaytoForLabel,
  GlobalTestState,
} from "../harness/harness.js";

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

  config.setString("KYC-RULE-R1", "operation_type", "deposit");
  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");

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

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

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

  // wallet that will send p2p transfer
  const {
    walletClient: w0,
    bankClient,
    exchange,
    bank,
    exchangeBankAccount,
    amlKeypair,
  } = await createKycTestkudosEnvironment(t, { adjustExchangeConfig });

  // wallet that will receive p2p transfer and do kyc + deposit
  const w1 = await createWalletDaemonWithClient(t, {
    name: "w0",
  });

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

  await w1.walletClient.call(WalletApiOperation.AddExchange, {
    exchangeBaseUrl: exchange.baseUrl,
  });

  await w1.walletClient.call(WalletApiOperation.SetExchangeTosAccepted, {
    exchangeBaseUrl: exchange.baseUrl,
  });

  const initRes = await w1.walletClient.call(
    WalletApiOperation.InitiatePeerPullCredit,
    {
      partialContractTerms: {
        amount: "TESTKUDOS:40",
        summary: "P2P payment",
        purse_expiration: AbsoluteTime.toProtocolTimestamp(
          AbsoluteTime.addDuration(
            AbsoluteTime.now(),
            Duration.fromSpec({ days: 2 }),
          ),
        ),
      },
    },
  );

  await w1.walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
    transactionId: initRes.transactionId,
    txState: {
      major: TransactionMajorState.Pending,
      minor: TransactionMinorState.Ready,
    },
  });

  const txPullCredit = await w1.walletClient.call(
    WalletApiOperation.GetTransactionById,
    {
      transactionId: initRes.transactionId,
    },
  );

  t.assertDeepEqual(txPullCredit.type, TransactionType.PeerPullCredit);

  const txPullDebit = await w0.call(WalletApiOperation.PreparePeerPullDebit, {
    talerUri: txPullCredit.talerUri!,
  });

  await w0.call(WalletApiOperation.ConfirmPeerPullDebit, {
    transactionId: txPullDebit.transactionId,
  });

  await w1.walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
    transactionId: txPullCredit.transactionId,
    txState: {
      major: TransactionMajorState.Done,
    },
  });

  const { transactionId: depositTxId } = await w1.walletClient.call(
    WalletApiOperation.GenerateDepositGroupTxId,
    {},
  );

  const deposit = await w1.walletClient.call(
    WalletApiOperation.CreateDepositGroup,
    {
      amount: "TESTKUDOS:10" as AmountString,
      depositPaytoUri: getTestHarnessPaytoForLabel("foo"),
      transactionId: depositTxId,
    },
  );

  await w1.walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
    transactionId: depositTxId,
    txState: {
      major: TransactionMajorState.Pending,
      minor: TransactionMinorState.KycAuthRequired,
    },
  });

  t.logStep("deposit pending(kyc-auth)");

  const depositTx = await w1.walletClient.call(
    WalletApiOperation.GetTransactionById,
    { transactionId: depositTxId },
  );

  t.assertDeepEqual(depositTx.type, TransactionType.Deposit);
  t.assertDeepEqual(
    depositTx.txState.minor,
    TransactionMinorState.KycAuthRequired,
  );
  t.assertTrue(depositTx.kycAuthTransferInfo !== undefined);
  t.assertTrue(depositTx.kycAuthTransferInfo.creditPaytoUris.length > 0);

  // fulfill kyc-auth

  console.log(`kyc info: ${j2s(depositTx.kycAuthTransferInfo)}`);

  const wireGatewayApiClient = new TalerWireGatewayHttpClient(
    exchangeBankAccount.wireGatewayApiBaseUrl,
  );

  await wireGatewayApiClient.addKycAuth({
    auth: bank.getAdminAuth(),
    body: {
      amount: "TESTKUDOS:0.1",
      debit_account: depositTx.kycAuthTransferInfo.debitPaytoUri,
      account_pub: depositTx.kycAuthTransferInfo.accountPub,
    },
  });

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

  await postAmlDecisionNoRules(t, {
    amlPriv: amlKeypair.priv,
    amlPub: amlKeypair.pub,
    exchangeBaseUrl: exchange.baseUrl,
    paytoHash: depositTx.kycPaytoHash!,
  });

  await w1.walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
    transactionId: depositTxId,
    txState: {
      major: TransactionMajorState.Finalizing,
      minor: TransactionMinorState.Track,
    },
  });
}

runKycDepositKycauthTest.suites = ["wallet"];
