/*
  This file is part of TALER
  Copyright (C) 2021 Taler Systems SA

  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.

  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 TALER; see the file COPYING.  If not, see
  <http://www.gnu.org/licenses/>
*/
/**
 * @file testing_api_cmd_kyc_get.c
 * @brief command to test kyc_get request
 * @author Christian Grothoff
 */
#include "platform.h"
#include <taler/taler_exchange_service.h>
#include <taler/taler_testing_lib.h>
#include "taler_merchant_service.h"
#include "taler_merchant_testing_lib.h"


/**
 * State for a "/kyc" GET CMD.
 */
struct KycGetState
{
  /**
   * Operation handle for a GET /private/kyc GET request.
   */
  struct TALER_MERCHANT_KycGetHandle *kgh;

  /**
   * Base URL of the merchant serving the request.
   */
  const char *merchant_url;

  /**
   * Instance to query, NULL if part of @e merchant_url
   */
  const char *instance_id;

  /**
   * Reference to command providing wire hash, NULL to
   * query all accounts.
   */
  const char *h_wire_ref;

  /**
   * URL of exchange to query.
   */
  const char *exchange_url;

  /**
   * Set to the payto hash of the first account
   * for which we failed to pass the KYC check.
   */
  struct TALER_NormalizedPaytoHashP h_payto;

  /**
   * Access token the user needs to start a KYC process.
   */
  struct TALER_AccountAccessTokenP access_token;

  /**
   * Expected HTTP response code.
   */
  unsigned int expected_http_status;

  /**
   * Target for long-polling.
   */
  enum TALER_EXCHANGE_KycLongPollTarget lpt;

  /**
   * Expected KYC state.
   */
  bool expected_kyc_state;

  /**
   * Expected KYC state.
   */
  bool have_access_token;

  /**
   * Interpreter state.
   */
  struct TALER_TESTING_Interpreter *is;

};


/**
 * Free the state of a "/kyc" GET CMD, and
 * possibly cancel a pending "kyc" GET operation.
 *
 * @param cls closure with the `struct KycGetState`
 * @param cmd command currently being freed.
 */
static void
kyc_get_cleanup (void *cls,
                 const struct TALER_TESTING_Command *cmd)
{
  struct KycGetState *cs = cls;

  if (NULL != cs->kgh)
  {
    TALER_LOG_WARNING ("/kyc GET operation did not complete\n");
    TALER_MERCHANT_kyc_get_cancel (cs->kgh);
  }
  GNUNET_free (cs);
}


/**
 * Process "GET /public/kyc_get" (lookup) response.
 *
 * @param cls closure
 * @param kr response we got
 */
static void
kyc_get_cb (void *cls,
            const struct TALER_MERCHANT_KycResponse *kr)
{
  struct KycGetState *cs = cls;

  cs->kgh = NULL;
  if (kr->hr.http_status != cs->expected_http_status)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Expected status %u, got %u\n",
                cs->expected_http_status,
                kr->hr.http_status);
    TALER_TESTING_FAIL (cs->is);
  }
  switch (kr->hr.http_status)
  {
  case MHD_HTTP_OK:
    if (! cs->expected_kyc_state)
    {
      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                  "Expected KYC state %u, got %u\n",
                  cs->expected_kyc_state,
                  kr->details.ok.kycs_length);
      TALER_TESTING_FAIL (cs->is);
    }
    for (unsigned int i = 0; i<kr->details.ok.kycs_length; i++)
    {
      struct TALER_FullPayto payto_uri;

      payto_uri = kr->details.ok.kycs[i].payto_uri;
      if (NULL == payto_uri.full_payto)
      {
        continue;
      }
      TALER_full_payto_normalize_and_hash (payto_uri,
                                           &cs->h_payto);
      if (! kr->details.ok.kycs[i].no_access_token)
      {
        cs->access_token
          = kr->details.ok.kycs[i].access_token;
        cs->have_access_token = true;
      }
      break;
    }
    break;
  }
  TALER_TESTING_interpreter_next (cs->is);
}


/**
 * Run the "kyc_get" CMD.
 *
 * @param cls closure.
 * @param cmd command being currently run.
 * @param is interpreter state.
 */
static void
kyc_get_run (void *cls,
             const struct TALER_TESTING_Command *cmd,
             struct TALER_TESTING_Interpreter *is)
{
  struct KycGetState *cs = cls;
  const struct TALER_MerchantWireHashP *h_wire = NULL;

  cs->is = is;
  if (NULL != cs->h_wire_ref)
  {
    const struct TALER_TESTING_Command *wire_cmd;

    if (NULL ==
        (wire_cmd =
           TALER_TESTING_interpreter_lookup_command (cs->is,
                                                     cs->h_wire_ref)))
    {
      TALER_TESTING_FAIL (cs->is);
    }
    /* Note: at the time of writing, no command offers an h_wire trait,
       so for now this code is dead and 'h_wire_ref' must always be NULL... */
    if (GNUNET_OK !=
        TALER_TESTING_get_trait_h_wire (wire_cmd,
                                        &h_wire))
    {
      TALER_TESTING_FAIL (cs->is);
    }
  }
  if (NULL == cs->instance_id)
    cs->kgh = TALER_MERCHANT_kyc_get (
      TALER_TESTING_interpreter_get_context (is),
      cs->merchant_url,
      h_wire,
      cs->exchange_url,
      cs->lpt,
      TALER_EXCHANGE_KLPT_NONE == cs->lpt
      ? GNUNET_TIME_UNIT_ZERO
      : GNUNET_TIME_UNIT_MINUTES,
      &kyc_get_cb,
      cs);
  else
    cs->kgh = TALER_MERCHANT_management_kyc_get (
      TALER_TESTING_interpreter_get_context (is),
      cs->merchant_url,
      cs->instance_id,
      h_wire,
      cs->exchange_url,
      cs->lpt,
      TALER_EXCHANGE_KLPT_NONE == cs->lpt
      ? GNUNET_TIME_UNIT_ZERO
      : GNUNET_TIME_UNIT_MINUTES,
      &kyc_get_cb,
      cs);

  GNUNET_assert (NULL != cs->kgh);
}


/**
 * Offer internal data from "KYC" GET CMD.
 *
 * @param cls closure.
 * @param[out] ret result (could be anything).
 * @param trait name of the trait.
 * @param index index number of the object to offer.
 * @return #GNUNET_OK on success.
 */
static enum GNUNET_GenericReturnValue
kyc_get_traits (void *cls,
                const void **ret,
                const char *trait,
                unsigned int index)
{
  struct KycGetState *cs = cls;
  struct TALER_TESTING_Trait traits[] = {
    /* Must be first, skipped if we have no token! */
    TALER_TESTING_make_trait_account_access_token (
      &cs->access_token),
    TALER_TESTING_make_trait_h_normalized_payto (
      &cs->h_payto),
    TALER_TESTING_trait_end ()
  };

  return TALER_TESTING_get_trait (
    &traits[cs->have_access_token
            ? 0
            : 1],
    ret,
    trait,
    index);
}


struct TALER_TESTING_Command
TALER_TESTING_cmd_merchant_kyc_get (
  const char *label,
  const char *merchant_url,
  const char *instance_id,
  const char *h_wire_ref,
  const char *exchange_url,
  enum TALER_EXCHANGE_KycLongPollTarget lpt,
  unsigned int expected_http_status,
  bool expected_kyc_state)
{
  struct KycGetState *cs;

  cs = GNUNET_new (struct KycGetState);
  cs->merchant_url = merchant_url;
  cs->instance_id = instance_id;
  cs->h_wire_ref = h_wire_ref;
  cs->exchange_url = exchange_url;
  cs->lpt = lpt;
  cs->expected_http_status = expected_http_status;
  cs->expected_kyc_state = expected_kyc_state;
  {
    struct TALER_TESTING_Command cmd = {
      .cls = cs,
      .label = label,
      .run = &kyc_get_run,
      .cleanup = &kyc_get_cleanup,
      .traits = &kyc_get_traits
    };

    return cmd;
  }
}


/* end of testing_api_cmd_kyc_get.c */
