/*
  This file is part of TALER
  Copyright (C) 2020 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_forget_order.c
 * @brief command to forget fields of an order
 * @author Jonathan Buchanan
 */
#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 "order forget" CMD.
 */
struct OrderForgetState
{
  /**
   * The interpreter state.
   */
  struct TALER_TESTING_Interpreter *is;

  /**
   * URL of the merchant backend.
   */
  const char *merchant_url;

  /**
   * Expected status code.
   */
  unsigned int http_status;

  /**
   * PATCH /orders/$ORDER_ID/forget operation handle.
   */
  struct TALER_MERCHANT_OrderForgetHandle *ofh;

  /**
   * Reference to a order operation.
   */
  const char *order_reference;

  /**
   * Order id to forget for. If NULL, the @a order_reference
   * will offer this value.
   */
  const char *order_id;

  /**
   * The list of paths to forget in the contract terms.
   */
  const char **paths;

  /**
   * The length of @e paths.
   */
  unsigned int paths_length;
};


/**
 * Free the state of a "order forget" CMD, and possibly
 * cancel it if it did not complete.
 *
 * @param cls closure.
 * @param cmd command being freed.
 */
static void
order_forget_cleanup (void *cls,
                      const struct TALER_TESTING_Command *cmd)
{
  struct OrderForgetState *ofs = cls;

  if (NULL != ofs->ofh)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                "Command '%s' did not complete\n",
                cmd->label);
    TALER_MERCHANT_order_forget_cancel (ofs->ofh);
    ofs->ofh = NULL;
  }
  GNUNET_array_grow (ofs->paths,
                     ofs->paths_length,
                     0);
  GNUNET_free (ofs);
}


/**
 * Callback for "order forget" operation, to check the
 * response code is as expected.
 *
 * @param cls closure
 * @param hr HTTP response we got
 */
static void
order_forget_cb (void *cls,
                 const struct TALER_MERCHANT_HttpResponse *hr)
{
  struct OrderForgetState *ofs = cls;

  ofs->ofh = NULL;
  if (ofs->http_status != hr->http_status)
  {
    TALER_TESTING_unexpected_status_with_body (ofs->is,
                                               hr->http_status,
                                               ofs->http_status,
                                               hr->reply);
    return;
  }
  TALER_TESTING_interpreter_next (ofs->is);
}


/**
 * Run the "order forget" CMD.
 *
 * @param cls closure.
 * @param cmd command currently being run.
 * @param is interpreter state.
 */
static void
order_forget_run (void *cls,
                  const struct TALER_TESTING_Command *cmd,
                  struct TALER_TESTING_Interpreter *is)
{
  struct OrderForgetState *ofs = cls;
  const char *order_id;

  ofs->is = is;
  if (NULL != ofs->order_id)
  {
    order_id = ofs->order_id;
  }
  else
  {
    const struct TALER_TESTING_Command *order_cmd;

    order_cmd
      = TALER_TESTING_interpreter_lookup_command (is,
                                                  ofs->order_reference);
    if (NULL == order_cmd)
      TALER_TESTING_FAIL (is);
    if (GNUNET_OK !=
        TALER_TESTING_get_trait_order_id (order_cmd,
                                          &order_id))
      TALER_TESTING_FAIL (is);
  }
  ofs->ofh = TALER_MERCHANT_order_forget (
    TALER_TESTING_interpreter_get_context (is),
    ofs->merchant_url,
    order_id,
    ofs->paths_length,
    ofs->paths,
    &order_forget_cb,
    ofs);
  GNUNET_assert (NULL != ofs->ofh);
}


/**
 * Offer internal data 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 extract.
 * @return #GNUNET_OK on success
 */
static enum GNUNET_GenericReturnValue
order_forget_traits (void *cls,
                     const void **ret,
                     const char *trait,
                     unsigned int index)
{
  struct OrderForgetState *ofs = cls;
  struct TALER_TESTING_Trait traits[ofs->paths_length + 2];

  traits[0] = TALER_TESTING_make_trait_paths_length (&ofs->paths_length);
  for (unsigned int i = 0; i < ofs->paths_length; ++i)
    traits[i + 1] = TALER_TESTING_make_trait_paths (i,
                                                    ofs->paths[i]);
  traits[ofs->paths_length + 1] = TALER_TESTING_trait_end ();

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


struct TALER_TESTING_Command
TALER_TESTING_cmd_merchant_forget_order (
  const char *label,
  const char *merchant_url,
  unsigned int http_status,
  const char *order_reference,
  const char *order_id,
  ...)
{
  struct OrderForgetState *ofs;

  ofs = GNUNET_new (struct OrderForgetState);
  ofs->http_status = http_status;
  ofs->order_reference = order_reference;
  ofs->merchant_url = merchant_url;
  ofs->order_id = order_id;
  {
    const char *path;
    va_list ap;

    va_start (ap, order_id);
    while (NULL != (path = va_arg (ap, const char *)))
    {
      GNUNET_array_append (ofs->paths,
                           ofs->paths_length,
                           path);
    }
    va_end (ap);
  }
  {
    struct TALER_TESTING_Command cmd = {
      .cls = ofs,
      .label = label,
      .run = &order_forget_run,
      .cleanup = &order_forget_cleanup,
      .traits = &order_forget_traits
    };

    return cmd;
  }
}
