/*
  This file is part of TALER
  Copyright (C) 2025 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/testing_api_cmd_get_active_legitimization_measures.c
 * @brief command for testing /aml/$OFFICER_PUB/legitimizations
 * @author Christian Grothoff
 */

/**
 * State for a GET "active_legitimization_measures" CMD.
 */
struct GetLegitimizationMeasuresState;
#define TALER_EXCHANGE__AML_LEGITIMIZATIONS_GET_RESULT_CLOSURE  \
        struct GetLegitimizationMeasuresState

#include "taler/platform.h"
#include "taler/taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "taler/taler_testing_lib.h"
#include "taler/taler_signatures.h"
#include "taler/backoff.h"


struct GetLegitimizationMeasuresState
{

  /**
   * Handle while operation is running.
   */
  struct TALER_EXCHANGE_AmlLegitimizationsGetHandle *dh;

  /**
   * Our interpreter.
   */
  struct TALER_TESTING_Interpreter *is;

  /**
   * Reference to command to previous set officer command that gives
   * us an officer_priv trait.
   */
  const char *officer_ref_cmd;

  /**
   * Reference to command to previous AML-triggering event that gives
   * us a payto-hash trait.
   */
  const char *account_ref_cmd;

  /**
   * Payto hash of the account we are manipulating the AML settings for.
   */
  struct TALER_NormalizedPaytoHashP h_payto;

  /**
   * Expected legitimization measures.
   */
  json_t *expected_measures;

  /**
   * Expected response code.
   */
  unsigned int expected_response;
};


/**
 * Callback to analyze the /aml-decision/$OFFICER_PUB response, just used to check
 * if the response code is acceptable.
 *
 * @param ds command state
 * @param result response details
 */
static void
get_active_legitimization_measures_cb (
  TALER_EXCHANGE__AML_LEGITIMIZATIONS_GET_RESULT_CLOSURE *ds,
  const struct TALER_EXCHANGE_AmlLegitimizationsGetResult *result)
{
  const struct TALER_EXCHANGE_HttpResponse *hr = &result->hr;

  ds->dh = NULL;
  if (ds->expected_response != hr->http_status)
  {
    TALER_TESTING_unexpected_status (ds->is,
                                     hr->http_status,
                                     ds->expected_response);
    return;
  }
  if (MHD_HTTP_OK == hr->http_status)
  {
    if (NULL == ds->expected_measures)
    {
      if (0 != result->details.ok.measures_length)
      {
        GNUNET_break (0);
        TALER_TESTING_interpreter_fail (ds->is);
        return;
      }
    }
    else
    {
      if (1 != result->details.ok.measures_length)
      {
        GNUNET_break (0);
        TALER_TESTING_interpreter_fail (ds->is);
        return;
      }
      for (size_t i = 0; i<result->details.ok.measures_length; i++)
      {
        const struct TALER_EXCHANGE_AmlLegitimizationsGetMeasureDetails *d
          = &result->details.ok.measures[i];

        json_dumpf (d->measures,
                    stderr,
                    0);
        if (1 != json_equal (d->measures,
                             ds->expected_measures))
        {
          GNUNET_break (0);
          json_dumpf (d->measures,
                      stderr,
                      0);
          TALER_TESTING_interpreter_fail (ds->is);
          return;
        }
      }
    }
  }
  TALER_TESTING_interpreter_next (ds->is);
}


/**
 * Run the command.
 *
 * @param cls closure.
 * @param cmd the command to execute.
 * @param is the interpreter state.
 */
static void
get_active_legitimization_measures_run (
  void *cls,
  const struct TALER_TESTING_Command *cmd,
  struct TALER_TESTING_Interpreter *is)
{
  struct GetLegitimizationMeasuresState *ds = cls;
  const struct TALER_NormalizedPaytoHashP *h_payto;
  const struct TALER_AmlOfficerPrivateKeyP *officer_priv;
  const struct TALER_TESTING_Command *ref;
  const char *exchange_url;

  (void) cmd;
  ds->is = is;
  {
    const struct TALER_TESTING_Command *exchange_cmd;

    exchange_cmd = TALER_TESTING_interpreter_get_command (is,
                                                          "exchange");
    if (NULL == exchange_cmd)
    {
      GNUNET_break (0);
      TALER_TESTING_interpreter_fail (is);
      return;
    }
    GNUNET_assert (GNUNET_OK ==
                   TALER_TESTING_get_trait_exchange_url (exchange_cmd,
                                                         &exchange_url));
  }
  ref = TALER_TESTING_interpreter_lookup_command (is,
                                                  ds->account_ref_cmd);
  if (NULL == ref)
  {
    GNUNET_break (0);
    TALER_TESTING_interpreter_fail (is);
    return;
  }
  if (GNUNET_OK !=
      TALER_TESTING_get_trait_h_normalized_payto (ref,
                                                  &h_payto))
  {
    GNUNET_break (0);
    TALER_TESTING_interpreter_fail (is);
    return;
  }
  ref = TALER_TESTING_interpreter_lookup_command (is,
                                                  ds->officer_ref_cmd);
  if (NULL == ref)
  {
    GNUNET_break (0);
    TALER_TESTING_interpreter_fail (is);
    return;
  }
  if (GNUNET_OK !=
      TALER_TESTING_get_trait_officer_priv (ref,
                                            &officer_priv))
  {
    GNUNET_break (0);
    TALER_TESTING_interpreter_fail (is);
    return;
  }
  ds->h_payto = *h_payto;
  ds->dh = TALER_EXCHANGE_aml_legitimizations_get_create (
    TALER_TESTING_interpreter_get_context (is),
    exchange_url,
    officer_priv);
  if (NULL == ds->dh)
  {
    GNUNET_break (0);
    TALER_TESTING_interpreter_fail (is);
    return;
  }
  GNUNET_assert (GNUNET_OK ==
                 TALER_EXCHANGE_aml_legitimizations_get_set_options (
                   ds->dh,
                   TALER_EXCHANGE_aml_legitimizations_get_option_h_payto (
                     h_payto),
                   TALER_EXCHANGE_aml_legitimizations_get_option_active (
                     TALER_EXCHANGE_YNA_YES)));
  GNUNET_assert (
    TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_START_OK ==
    TALER_EXCHANGE_aml_legitimizations_get_start (
      ds->dh,
      &get_active_legitimization_measures_cb,
      ds));
}


/**
 * Free the state of a "get_aml_decision" CMD, and possibly cancel a
 * pending operation thereof.
 *
 * @param cls closure, must be a `struct GetLegitimizationMeasuresState`.
 * @param cmd the command which is being cleaned up.
 */
static void
get_active_legitimization_measures_cleanup (
  void *cls,
  const struct TALER_TESTING_Command *cmd)
{
  struct GetLegitimizationMeasuresState *ds = cls;

  if (NULL != ds->dh)
  {
    TALER_TESTING_command_incomplete (ds->is,
                                      cmd->label);
    TALER_EXCHANGE_aml_legitimizations_get_cancel (ds->dh);
    ds->dh = NULL;
  }
  json_decref (ds->expected_measures);
  GNUNET_free (ds);
}


/**
 * Offer internal data of a "AML decision" CMD state to other
 * commands.
 *
 * @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
get_active_legitimization_measures_traits (void *cls,
                                           const void **ret,
                                           const char *trait,
                                           unsigned int index)
{
  struct GetLegitimizationMeasuresState *ws = cls;
  struct TALER_TESTING_Trait traits[] = {
    TALER_TESTING_make_trait_h_normalized_payto (&ws->h_payto),
    TALER_TESTING_trait_end ()
  };

  return TALER_TESTING_get_trait (traits,
                                  ret,
                                  trait,
                                  index);
}


struct TALER_TESTING_Command
TALER_TESTING_cmd_get_active_legitimization_measures (
  const char *label,
  const char *ref_officer,
  const char *ref_operation,
  unsigned int expected_response,
  const char *expected_measures)
{
  struct GetLegitimizationMeasuresState *ds;
  json_error_t err;

  ds = GNUNET_new (struct GetLegitimizationMeasuresState);
  ds->officer_ref_cmd = ref_officer;
  ds->account_ref_cmd = ref_operation;
  ds->expected_response = expected_response;
  if (NULL != expected_measures)
  {
    ds->expected_measures = json_loads (expected_measures,
                                        JSON_DECODE_ANY,
                                        &err);
    if (NULL == ds->expected_measures)
    {
      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                  "Invalid JSON in new rules of %s: %s\n",
                  label,
                  err.text);
      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                  "Input was: `%s'\n",
                  expected_measures);
      GNUNET_assert (0);
    }
  }
  {
    struct TALER_TESTING_Command cmd = {
      .cls = ds,
      .label = label,
      .run = &get_active_legitimization_measures_run,
      .cleanup = &get_active_legitimization_measures_cleanup,
      .traits = &get_active_legitimization_measures_traits
    };

    return cmd;
  }
}


/* end of testing_api_cmd_get_active_legitimization_measures.c */
