/*
 This file is part of GNU Taler
 (C) 2021 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 {
  alternativeOrThrow,
  Duration,
  HttpStatusCode,
  LoginTokenScope,
  MerchantAuthMethod,
  succeedOrThrow,
  TalerMerchantInstanceHttpClient,
  TalerMerchantManagementHttpClient,
  TanChannel,
} from "@gnu-taler/taler-util";
import { createSimpleTestkudosEnvironmentV3 } from "harness/environments.js";
import { startTanHelper } from "harness/tan-helper.js";
import { randomBytes } from "node:crypto";
import { chmodSync, writeFileSync } from "node:fs";
import { GlobalTestState } from "../harness/harness.js";
import { solveMFA } from "./test-merchant-self-provision-inactive-account-permissions.js";

/**
 * Do basic checks on instance management and authentication.
 */
export async function runMerchantSelfProvisionActivationTest(
  t: GlobalTestState,
) {
  // Set up test environment

  // FIXME: maybe merchant can use commands?
  const RND = randomBytes(10).toString("hex");
  const socketFile = `${t.testDir}/tan-helper-${RND}.socket`;
  const helperScript = `${t.testDir}/harness-helper-${RND}.sh`;
  writeFileSync(
    helperScript,
    `#!/bin/bash
taler-harness run-helper --socket ${socketFile} -- $@
`,
  );
  chmodSync(helperScript, "777");

  const {
    walletClient,
    bankClient,
    exchange,
    merchant,
    bank,
    merchantAdminAccessToken,
  } = await createSimpleTestkudosEnvironmentV3(t, undefined, {
    additionalMerchantConfig(m) {
      m.modifyConfig(async (cfg) => {
        cfg.setString("merchant", "ENABLE_SELF_PROVISIONING", "yes");
        cfg.setString("merchant", "HELPER_SMS", helperScript);
        cfg.setString("merchant", "HELPER_EMAIL", helperScript);
        cfg.setString("merchant", "MANDATORY_TAN_CHANNELS", "email sms");
      });
    },
  });
  const helper = await startTanHelper({ socketFile });

  const merchantClient = new TalerMerchantManagementHttpClient(
    merchant.makeInstanceBaseUrl(),
  );

  {
    const r = succeedOrThrow(
      await merchantClient.listInstances(merchantAdminAccessToken),
    );
    t.assertDeepEqual(r.instances.length, 2);
  }

  const instanceInfo = {
    id: "self-instance",
    name: "My instance",
    auth: {
      method: MerchantAuthMethod.TOKEN,
      password: "123",
    },
    default_pay_delay: Duration.toTalerProtocolDuration(
      Duration.fromSpec({ days: 14 }),
    ),
    default_wire_transfer_delay: Duration.toTalerProtocolDuration(
      Duration.fromSpec({ days: 14 }),
    ),
    jurisdiction: {},
    address: {},
    email: "some@taler.net",
    phone_number: "+1111",
    use_stefan: false,
  };
  const signupStart = alternativeOrThrow(
    await merchantClient.createInstanceSelfProvision(instanceInfo),
    HttpStatusCode.Accepted,
  );

  // creation requires 2fa
  t.assertDeepEqual(signupStart.challenges.length, 2);
  t.assertDeepEqual(signupStart.combi_and, true);

  const firstChallenge = signupStart.challenges[0];
  const secondChallenge = signupStart.challenges[1];

  //FIXME: check the order
  // always first emails since is cheaper
  // t.assertDeepEqual(firstChallenge.challenge_type, TanChannel.EMAIL);
  // t.assertDeepEqual(secondChallenge.challenge_type, TanChannel.SMS);

  {
    // new instance is pending, then is not listed
    const r = succeedOrThrow(
      await merchantClient.listInstances(merchantAdminAccessToken),
    );
    t.assertDeepEqual(r.instances.length, 2);
  }

  {
    succeedOrThrow(
      await merchantClient.sendChallenge(firstChallenge.challenge_id),
    );

    const message = helper.getLastCodeForAddress(instanceInfo.phone_number);
    const [tanCode] = message.split("\n");
    succeedOrThrow(
      await merchantClient.confirmChallenge(firstChallenge.challenge_id, {
        tan: tanCode,
      }),
    );
  }

  {
    succeedOrThrow(
      await merchantClient.sendChallenge(secondChallenge.challenge_id),
    );

    const message = helper.getLastCodeForAddress(instanceInfo.email);
    const [tanCode] = message.split("\n");
    succeedOrThrow(
      await merchantClient.confirmChallenge(secondChallenge.challenge_id, {
        tan: tanCode,
      }),
    );
  }

  const completeSignup = await merchantClient.createInstanceSelfProvision(
    instanceInfo,
    {
      challengeIds: [firstChallenge.challenge_id, secondChallenge.challenge_id],
    },
  );

  t.assertDeepEqual(completeSignup.type, "ok");

  const instanceApi = new TalerMerchantInstanceHttpClient(
    merchantClient.getSubInstanceAPI(instanceInfo.id),
    merchantClient.httpLib,
  );

  {
    // new instance is completed, now it should be visible
    const r = succeedOrThrow(
      await merchantClient.listInstances(merchantAdminAccessToken),
    );
    t.assertDeepEqual(r.instances.length, 3);
  }

  const loginChallenge = alternativeOrThrow(
    await instanceApi.createAccessToken(
      instanceInfo.id,
      instanceInfo.auth.password,
      {
        scope: LoginTokenScope.All,
      },
    ),
    HttpStatusCode.Accepted,
  );

  await solveMFA(merchantClient, helper, loginChallenge, {
    [TanChannel.EMAIL]: instanceInfo.email,
    [TanChannel.SMS]: instanceInfo.phone_number,
  });

  const { access_token: token } = succeedOrThrow(
    await instanceApi.createAccessToken(
      instanceInfo.id,
      instanceInfo.auth.password,
      {
        scope: LoginTokenScope.All,
      },
      {
        challengeIds: loginChallenge.challenges.map((c) => c.challenge_id),
      },
    ),
  );

  const det = succeedOrThrow(
    await instanceApi.getCurrentInstanceDetails(token),
  );

  // check that the instance has the new email
  t.assertDeepEqual(det.email, instanceInfo.email);
  t.assertDeepEqual(det.email_validated, true);
  t.assertDeepEqual(det.phone_number, instanceInfo.phone_number);
  t.assertDeepEqual(det.phone_validated, true);

  helper.stop();
}

runMerchantSelfProvisionActivationTest.suites = ["merchant", "self-provision"];
