/*
  This file is part of TALER
  Copyright (C) 2014-2023 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_refund_order.c
 * @brief command to test refunds.
 * @author Marcello Stanisci
 * @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 "refund increase" CMD.
 */
struct RefundState
{
  /**
   * Operation handle for a POST /orders/$ID/refund request.
   */
  struct TALER_MERCHANT_OrderRefundHandle *orh;

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

  /**
   * Order id of the contract to refund.
   */
  const char *order_id;

  /**
   * The amount to refund.
   */
  struct TALER_Amount refund_amount;

  /**
   * Human-readable justification for the refund.
   */
  const char *reason;

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

  /**
   * Expected HTTP response code.
   */
  unsigned int http_code;
};


/**
 * Process POST /refund (increase) response; just checking
 * if the HTTP response code is the one expected.
 *
 * @param cls closure
 * @param rr response
 */
static void
refund_cb (void *cls,
           const struct TALER_MERCHANT_RefundResponse *rr)
{
  struct RefundState *ris = cls;

  ris->orh = NULL;
  if (ris->http_code != rr->hr.http_status)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Expected status %u, got %u(%d) for refund increase\n",
                ris->http_code,
                rr->hr.http_status,
                (int) rr->hr.ec);
    TALER_TESTING_FAIL (ris->is);
  }
  switch (rr->hr.http_status)
  {
  case MHD_HTTP_OK:
    {
      struct TALER_MERCHANT_RefundUriData rud;

      if (GNUNET_OK !=
          TALER_MERCHANT_parse_refund_uri (
            rr->details.ok.taler_refund_uri,
            &rud))
      {
        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                    "Taler refund uri is malformed\n");
        TALER_TESTING_interpreter_fail (ris->is);
        return;
      }
      {
        char *host;

        host = TALER_MERCHANT_TESTING_extract_host (ris->merchant_url);
        if ((0 != strcmp (host,
                          rud.merchant_host)) ||
            (NULL != rud.merchant_prefix_path) ||
            (0 != strcmp (ris->order_id,
                          rud.order_id)) ||
            (NULL != rud.ssid))
        {
          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                      "Taler refund uri does not match\n");
          TALER_TESTING_interpreter_fail (ris->is);
          TALER_MERCHANT_parse_refund_uri_free (&rud);
          GNUNET_free (host);
          return;
        }
        GNUNET_free (host);
      }
      TALER_MERCHANT_parse_refund_uri_free (&rud);
    }
    break;
  case MHD_HTTP_UNAUTHORIZED:
    break;
  case MHD_HTTP_FORBIDDEN:
    break;
  case MHD_HTTP_NOT_FOUND:
    break;
  case MHD_HTTP_CONFLICT:
    break;
  default:
    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                "Unhandled HTTP status %u for refund order.\n",
                rr->hr.http_status);
  }
  TALER_TESTING_interpreter_next (ris->is);
}


/**
 * Run the "refund increase" CMD.
 *
 * @param cls closure.
 * @param cmd command currently being run.
 * @param is the interpreter state.
 */
static void
refund_increase_run (void *cls,
                     const struct TALER_TESTING_Command *cmd,
                     struct TALER_TESTING_Interpreter *is)
{
  struct RefundState *ris = cls;

  ris->is = is;
  ris->orh = TALER_MERCHANT_post_order_refund (
    TALER_TESTING_interpreter_get_context (is),
    ris->merchant_url,
    ris->order_id,
    &ris->refund_amount,
    ris->reason,
    &refund_cb,
    ris);
  if (NULL == ris->orh)
    TALER_TESTING_FAIL (is);
}


/**
 * Offer internal data from the "refund increase" 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 extract.
 * @return #GNUNET_OK on success
 */
static int
refund_increase_traits (void *cls,
                        const void **ret,
                        const char *trait,
                        unsigned int index)
{
  struct RefundState *ris = cls;
  struct TALER_TESTING_Trait traits[] = {
    TALER_TESTING_make_trait_amount (&ris->refund_amount),
    TALER_TESTING_make_trait_reason (ris->reason),
    TALER_TESTING_trait_end ()
  };

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


/**
 * Free the state of a "refund increase" CMD, and
 * possibly cancel a pending "refund increase" operation.
 *
 * @param cls closure
 * @param cmd command currently being freed.
 */
static void
refund_increase_cleanup (void *cls,
                         const struct TALER_TESTING_Command *cmd)
{
  struct RefundState *ris = cls;

  if (NULL != ris->orh)
  {
    TALER_LOG_WARNING ("Refund operation did not complete\n");
    TALER_MERCHANT_post_order_refund_cancel (ris->orh);
  }
  GNUNET_free (ris);
}


struct TALER_TESTING_Command
TALER_TESTING_cmd_merchant_order_refund (const char *label,
                                         const char *merchant_url,
                                         const char *reason,
                                         const char *order_id,
                                         const char *refund_amount,
                                         unsigned int http_code)
{
  struct RefundState *ris;

  ris = GNUNET_new (struct RefundState);
  ris->merchant_url = merchant_url;
  ris->order_id = order_id;
  GNUNET_assert (GNUNET_OK ==
                 TALER_string_to_amount (refund_amount,
                                         &ris->refund_amount));
  ris->reason = reason;
  ris->http_code = http_code;
  {
    struct TALER_TESTING_Command cmd = {
      .cls = ris,
      .label = label,
      .run = &refund_increase_run,
      .cleanup = &refund_increase_cleanup,
      .traits = &refund_increase_traits
    };

    return cmd;
  }
}


/* end of testing_api_cmd_refund_order.c */
