/*
  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_post_orders_paid.c
 * @brief command to test POST /orders/$ID/paid.
 * @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 of a "POST /orders/$ID/paid" CMD.
 */
struct PostOrdersPaidState
{

  /**
   * Handle for a "POST /paid" request.
   */
  struct TALER_MERCHANT_OrderPaidHandle *oph;

  /**
   * The interpreter state.
   */
  struct TALER_TESTING_Interpreter *is;

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

  /**
   * Reference to the "pay" command to verify.
   */
  const char *pay_reference;

  /**
   * The session to use for the request.
   */
  const char *session_id;

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

};


/**
 * Response from the merchant after POST /paid.
 *
 * @param cls pointer to `struct PostOrdersPaidState`.
 * @param opr the response.
 */
static void
paid_cb (void *cls,
         const struct TALER_MERCHANT_OrderPaidResponse *opr)
{
  struct PostOrdersPaidState *ops = cls;

  ops->oph = NULL;
  if (ops->http_status != opr->hr.http_status)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Unexpected response code %u (%d) to command %s\n",
                opr->hr.http_status,
                (int) opr->hr.ec,
                TALER_TESTING_interpreter_get_current_label (ops->is));
    TALER_TESTING_FAIL (ops->is);
  }
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Successful order-paid (HTTP status: %u)\n",
              ops->http_status);
  TALER_TESTING_interpreter_next (ops->is);
}


/**
 * Run a "paid" CMD.
 *
 * @param cls closure
 * @param cmd command being run.
 * @param is interpreter state
 */
static void
paid_run (void *cls,
          const struct TALER_TESTING_Command *cmd,
          struct TALER_TESTING_Interpreter *is)
{
  struct PostOrdersPaidState *ops = cls;
  const struct TALER_TESTING_Command *pay_cmd;
  const char *proposal_reference;
  const struct TALER_TESTING_Command *proposal_cmd;
  const char *order_id;
  const struct TALER_PrivateContractHashP *h_contract_terms;
  const struct TALER_MerchantSignatureP *merchant_sig;

  ops->is = is;
  pay_cmd = TALER_TESTING_interpreter_lookup_command (is,
                                                      ops->pay_reference);
  if (NULL == pay_cmd)
    TALER_TESTING_FAIL (is);
  if (GNUNET_OK !=
      TALER_TESTING_get_trait_merchant_sig (pay_cmd,
                                            &merchant_sig))
    TALER_TESTING_FAIL (is);
  if (GNUNET_OK !=
      TALER_TESTING_get_trait_proposal_reference (pay_cmd,
                                                  &proposal_reference))
    TALER_TESTING_FAIL (is);
  proposal_cmd = TALER_TESTING_interpreter_lookup_command (is,
                                                           proposal_reference);

  if (NULL == proposal_cmd)
    TALER_TESTING_FAIL (is);

  {
    const json_t *contract_terms;
    const char *error_name;
    unsigned int error_line;

    if (GNUNET_OK !=
        TALER_TESTING_get_trait_contract_terms (proposal_cmd,
                                                &contract_terms))
      TALER_TESTING_FAIL (is);
    {
      /* Get information that needs to be put verbatim in the
       * deposit permission */
      struct GNUNET_JSON_Specification spec[] = {
        GNUNET_JSON_spec_string ("order_id",
                                 &order_id),
        GNUNET_JSON_spec_end ()
      };

      if (GNUNET_OK !=
          GNUNET_JSON_parse (contract_terms,
                             spec,
                             &error_name,
                             &error_line))
      {
        char *js;

        js = json_dumps (contract_terms,
                         JSON_INDENT (1));
        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                    "Parser failed on %s:%u for input `%s'\n",
                    error_name,
                    error_line,
                    js);
        free (js);
        TALER_TESTING_FAIL (is);
      }
    }
  }

  if (GNUNET_OK !=
      TALER_TESTING_get_trait_h_contract_terms (proposal_cmd,
                                                &h_contract_terms))
    TALER_TESTING_FAIL (is);

  ops->oph = TALER_MERCHANT_order_paid (TALER_TESTING_interpreter_get_context (
                                          is),
                                        ops->merchant_url,
                                        order_id,
                                        ops->session_id,
                                        h_contract_terms,
                                        NULL,
                                        merchant_sig,
                                        &paid_cb,
                                        ops);
  if (NULL == ops->oph)
    TALER_TESTING_FAIL (is);
}


/**
 * Free a "paid" CMD, and cancel it if need be.
 *
 * @param cls closure.
 * @param cmd command currently being freed.
 */
static void
paid_cleanup (void *cls,
              const struct TALER_TESTING_Command *cmd)
{
  struct PostOrdersPaidState *ops = cls;

  if (NULL != ops->oph)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                "Command `%s' did not complete.\n",
                TALER_TESTING_interpreter_get_current_label (
                  ops->is));
    TALER_MERCHANT_order_paid_cancel (ops->oph);
  }
  GNUNET_free (ops);
}


struct TALER_TESTING_Command
TALER_TESTING_cmd_merchant_post_orders_paid (const char *label,
                                             const char *merchant_url,
                                             const char *pay_reference,
                                             const char *session_id,
                                             unsigned int http_status)
{
  struct PostOrdersPaidState *ops;

  ops = GNUNET_new (struct PostOrdersPaidState);
  ops->http_status = http_status;
  ops->pay_reference = pay_reference;
  ops->merchant_url = merchant_url;
  ops->session_id = session_id;
  {
    struct TALER_TESTING_Command cmd = {
      .cls = ops,
      .label = label,
      .run = &paid_run,
      .cleanup = &paid_cleanup
    };

    return cmd;
  }
}
