/*
 This file is part of GNU Taler
 (C) 2025 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 {
  AbsoluteTime,
  AccessToken,
  Duration,
  j2s,
  Logger,
  succeedOrThrow,
  TransactionMajorState,
  TransactionMinorState,
  TransactionType,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { withdrawViaBankV3 } from "../harness/environments.js";
import { startFakeChallenger } from "../harness/fake-challenger.js";
import { GlobalTestState, harnessHttpLib } from "../harness/harness.js";
import { createTopsEnvironment } from "../harness/tops.js";

const logger = new Logger("test-tops-aml.ts");

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

  const {
    walletClient,
    bankClient,
    exchange,
    amlKeypair,
    merchant,
    exchangeBankAccount,
    wireGatewayApi,
    exchangeApi,
  } = await createTopsEnvironment(t);

  const challenger = await startFakeChallenger({
    port: 6001,
    addressType: "postal-ch",
  });

  t.logStep("starting withdrawal");

  // Withdrawal below threshold succeeds!
  const wres = await withdrawViaBankV3(t, {
    amount: "CHF:20",
    bankClient,
    exchange,
    walletClient,
  });

  await wres.withdrawalFinishedCond;

  t.logStep("withdrawal done");

  const peerResp = await walletClient.call(
    WalletApiOperation.InitiatePeerPushDebit,
    {
      partialContractTerms: {
        amount: "CHF:5",
        purse_expiration: AbsoluteTime.toProtocolTimestamp(
          AbsoluteTime.addDuration(
            AbsoluteTime.now(),
            Duration.fromSpec({ minutes: 5 }),
          ),
        ),
        summary: "Test",
      },
    },
  );

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

  t.logStep("p2p ready");

  const pushDebitTxDet = await walletClient.call(
    WalletApiOperation.GetTransactionById,
    {
      transactionId: peerResp.transactionId,
    },
  );
  t.assertDeepEqual(pushDebitTxDet.type, TransactionType.PeerPushDebit);
  const talerUri = pushDebitTxDet.talerUri;
  t.assertTrue(typeof talerUri === "string");

  const prepareResp = await walletClient.call(
    WalletApiOperation.PreparePeerPushCredit,
    {
      talerUri,
    },
  );

  await walletClient.call(WalletApiOperation.ConfirmPeerPushCredit, {
    transactionId: prepareResp.transactionId,
  });

  t.logStep("p2p confirmed");

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

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

  const accessToken = pushCreditTxDet.kycAccessToken;

  t.assertTrue(typeof accessToken === "string");

  const infoResp = await exchangeApi.checkKycInfo(accessToken as AccessToken);

  console.log(j2s(infoResp));

  t.assertDeepEqual(infoResp.case, "ok");

  const myId = infoResp.body.requirements.find((x) =>
    x.description.includes("TAN letter"),
  )?.id;
  t.assertTrue(!!myId);

  const startResp = succeedOrThrow(
    await exchangeApi.startExternalKycProcess(myId, {}),
  );
  console.log(`start resp`, j2s(startResp));

  let challengerRedirectUrl = startResp.redirect_url;

  const resp = await harnessHttpLib.fetch(challengerRedirectUrl);
  const respJson = await resp.json();
  console.log(`challenger resp: ${j2s(respJson)}`);

  const nonce = respJson.nonce;
  t.assertTrue(typeof nonce === "string");
  const proofRedirectUrl = respJson.redirect_url;

  challenger.fakeVerification(nonce, {
    CONTACT_NAME: "Richard Stallman",
    ADDRESS_LINES: "Bundesgasse 1\n1234 Bern",
  });

  console.log("nonce", nonce);
  console.log("proof redirect URL", proofRedirectUrl);

  const proofResp = await harnessHttpLib.fetch(proofRedirectUrl, {
    redirect: "manual",
  });
  console.log("proof status:", proofResp.status);
  if (proofResp.status === 404) {
    console.log(j2s(await proofResp.text()));
  }
  t.assertDeepEqual(proofResp.status, 303);

  const infoResp2 = await exchangeApi.checkKycInfo(accessToken as AccessToken);

  console.log(j2s(infoResp2));

  if (
    !(
      infoResp2.case === "ok" &&
      infoResp2.body.requirements[0].form === "accept-tos"
    )
  ) {
    t.fail("requirements need to include ToS (due to zero measure)");
  }

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

runTopsPeerTest.suites = ["wallet"];
