/*
  This file is part of TALER
  Copyright (C) 2014-2024 Taler Systems SA

  TALER is free software; you can redistribute it and/or modify it under the
  terms of the GNU Affero 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 Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License along with
  TALER; see the file COPYING.LIB.  If not, see <http://www.gnu.org/licenses/>
*/
/**
 * @file taler_merchant_pay_service.h
 * @brief Payment‑specific interface extracted from taler_merchant_service.h
 *        **Only** declarations and helper‑macros live here – the actual
 *        implementation must be provided in the accompanying *.c file.
 *        This header can be included by both merchant front‑ends and wallets
 *        that want to create or proxy /orders/$ID/pay requests.
 */
#ifndef TALER_MERCHANT_PAY_SERVICE_H
#define TALER_MERCHANT_PAY_SERVICE_H

#include <taler/taler_util.h>
#include <taler/taler_signatures.h>
#include <taler/taler_error_codes.h>
#include <taler/taler_exchange_service.h>
#include "taler_merchant_service.h"
#include <gnunet/gnunet_time_lib.h>


#ifndef TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE
#define TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE void
#endif

/**
 * Opaque handle returned by the payment helper APIs declared below.
 */
struct TALER_MERCHANT_OrderPayHandle;

/**
 * Which option is being supplied to #TALER_MERCHANT_order_pay_set_options().
 */
enum TALER_MERCHANT_OrderPayOptionType
{
  TALER_MERCHANT_OrderPayOptionType_END = 0,
  TALER_MERCHANT_OrderPayOptionType_MERCHANT_URL,
  TALER_MERCHANT_OrderPayOptionType_SESSION_ID,
  TALER_MERCHANT_OrderPayOptionType_H_CONTRACT,
  TALER_MERCHANT_OrderPayOptionType_CHOICE_INDEX,
  TALER_MERCHANT_OrderPayOptionType_AMOUNT,
  TALER_MERCHANT_OrderPayOptionType_MAX_FEE,
  TALER_MERCHANT_OrderPayOptionType_MERCHANT_PUB,
  TALER_MERCHANT_OrderPayOptionType_TIMESTAMP,
  TALER_MERCHANT_OrderPayOptionType_REFUND_DEADLINE,
  TALER_MERCHANT_OrderPayOptionType_PAY_DEADLINE,
  TALER_MERCHANT_OrderPayOptionType_H_WIRE,
  TALER_MERCHANT_OrderPayOptionType_ORDER_ID,
  TALER_MERCHANT_OrderPayOptionType_COINS,
  TALER_MERCHANT_OrderPayOptionType_INPUT_TOKENS,
  TALER_MERCHANT_OrderPayOptionType_OUTPUT_TOKENS,
  TALER_MERCHANT_OrderPayOptionType_WALLET_DATA, // Used privately so no #define is present for it
  TALER_MERCHANT_OrderPayOptionType_DONAU_URL,
  TALER_MERCHANT_OrderPayOptionType_DONAU_YEAR,
  TALER_MERCHANT_OrderPayOptionType_DONAU_BUDIS,
  /*New objects go in here*/
  TALER_MERCHANT_OrderPayOptionType_LENGTH
};

/**
 * Container describing *one* option for a payment request.
 *
 * Applications should typically not use this struct directly,
 * only through respective TALER_MERCHANT_ORDER_PAY_OPTION_-macros
 */
struct TALER_MERCHANT_OrderPayOption
{
  /**
   * Type of the option being supplied.
   */
  enum TALER_MERCHANT_OrderPayOptionType ot;

  union
  {
    const char *merchant_url;
    const char *session_id;
    const struct TALER_PrivateContractHashP *h_contract;
    int choice_index;
    struct TALER_Amount amount;
    struct TALER_Amount max_fee;
    struct TALER_MerchantPublicKeyP merchant_pub;
    struct GNUNET_TIME_Timestamp timestamp;
    struct GNUNET_TIME_Timestamp refund_deadline;
    struct GNUNET_TIME_Timestamp pay_deadline;
    struct TALER_MerchantWireHashP h_wire;
    const char *order_id;
    struct
    {
      unsigned int num_coins;
      const struct TALER_MERCHANT_PayCoin *coins;
    } coins;
    struct
    {
      unsigned int num_tokens;
      const struct TALER_MERCHANT_UseToken *tokens;
    } input_tokens;
    struct
    {
      unsigned int num_output_tokens;
      const struct TALER_MERCHANT_OutputToken *output_tokens;
    } output_tokens;
    const char *donau_url;
    uint64_t donau_year;
    json_t *donau_budis_json;
  } details;
};


/**
 * Status codes returned from #TALER_MERCHANT_order_pay_set_options() and
 * #TALER_MERCHANT_order_pay_start().
 */
enum TALER_MERCHANT_OrderPayErrorCode
{
  TALER_MERCHANT_OPOEC_OK = 0,               /**< everything fine */
  TALER_MERCHANT_OPOEC_UNKNOWN_OPTION,       /**< unrecognised option kind */
  TALER_MERCHANT_OPOEC_DUPLICATE_OPTION,     /**< option given more than once */
  TALER_MERCHANT_OPOEC_INVALID_VALUE,        /**< semantic/format error in value */
  TALER_MERCHANT_OPOEC_MISSING_MANDATORY,    /**< required field missing at start */
  TALER_MERCHANT_OPOEC_URL_FAILURE,          /**< failed to build request URL   */
  TALER_MERCHANT_OPOEC_CURL_FAILURE          /**< failed to init/schedule CURL  */
};


/**
 * Terminate the list of payment options.
 *
 * @par Example
 * \code
 * struct TALER_MERCHANT_OrderPayOption opts[] = {
 *   TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE()
 * };
 * \endcode
 */
#define TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE() \
        (struct TALER_MERCHANT_OrderPayOption){           \
          .ot = TALER_MERCHANT_OrderPayOptionType_END   \
        }


/**
 * Specify the merchant’s URL for the payment request.
 *
 * @param _url  NULL-terminated string holding the merchant endpoint.
 *
 * @par Example
 * \code
 * struct TALER_MERCHANT_OrderPayOption opts[] = {
 *   TALER_MERCHANT_ORDER_PAY_OPTION_MERCHANT_URL("https://shop.example/pay"),
 *   TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE()
 * };
 * \endcode
 */
#define TALER_MERCHANT_ORDER_PAY_OPTION_MERCHANT_URL(_url) \
        (struct TALER_MERCHANT_OrderPayOption){                  \
          .ot = TALER_MERCHANT_OrderPayOptionType_MERCHANT_URL, \
          .details.merchant_url = (_url)                       \
        }


/**
 * Supply the session identifier for this payment.
 *
 * @param _sid  session ID string.
 *
 * @par Example
 * \code
 * struct TALER_MERCHANT_OrderPayOption opts[] = {
 *   TALER_MERCHANT_ORDER_PAY_OPTION_SESSION_ID("ABC123"),
 *   TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE()
 * };
 * \endcode
 */
#define TALER_MERCHANT_ORDER_PAY_OPTION_SESSION_ID(_sid) \
        (struct TALER_MERCHANT_OrderPayOption){                \
          .ot = TALER_MERCHANT_OrderPayOptionType_SESSION_ID, \
          .details.session_id = (_sid)                       \
        }


/**
 * Supply a private contract hash for the order.
 *
 * @param _h  pointer to a TALER_PrivateContractHashP.
 *
 * @par Example
 * \code
 * struct TALER_PrivateContractHashP *h = …;
 * struct TALER_MERCHANT_OrderPayOption opts[] = {
 *   TALER_MERCHANT_ORDER_PAY_OPTION_H_CONTRACT(h),
 *   TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE()
 * };
 * \endcode
 */
#define TALER_MERCHANT_ORDER_PAY_OPTION_H_CONTRACT(_h) \
        (struct TALER_MERCHANT_OrderPayOption){              \
          .ot = TALER_MERCHANT_OrderPayOptionType_H_CONTRACT, \
          .details.h_contract = (_h)                       \
        }


/**
 * Supply which choice index the customer(wallet) selected.
 * from contract terms.
 *
 * @param _idx choice index.
 *
 * @par Example
 * \code
 * struct TALER_MERCHANT_OrderPayOption opts[] = {
 *   TALER_MERCHANT_ORDER_PAY_OPTION_CHOICE_INDEX(2),
 *   TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE()
 * };
 * \endcode
 */
#define TALER_MERCHANT_ORDER_PAY_OPTION_CHOICE_INDEX(_idx) \
        (struct TALER_MERCHANT_OrderPayOption){                  \
          .ot = TALER_MERCHANT_OrderPayOptionType_CHOICE_INDEX, \
          .details.choice_index = (_idx)                       \
        }


/**
 * Specify the amount to be paid.
 *
 * @param _amt  pointer to a TALER_Amount struct.
 *
 * @par Example
 * \code
 * struct TALER_Amount amt = ...;
 * struct TALER_MERCHANT_OrderPayOption opts[] = {
 *   TALER_MERCHANT_ORDER_PAY_OPTION_AMOUNT(&amt),
 *   TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE()
 * };
 * \endcode
 */
#define TALER_MERCHANT_ORDER_PAY_OPTION_AMOUNT(_amt) \
        (struct TALER_MERCHANT_OrderPayOption){            \
          .ot = TALER_MERCHANT_OrderPayOptionType_AMOUNT, \
          .details.amount = *(_amt)                      \
        }


/**
 * Set the maximum acceptable fee.
 *
 * @param _fee  pointer to a TALER_Amount struct for the fee cap.
 *
 * @par Example
 * \code
 * struct TALER_Amount fee = ...;
 * struct TALER_MERCHANT_OrderPayOption opts[] = {
 *   TALER_MERCHANT_ORDER_PAY_OPTION_MAX_FEE(&fee),
 *   TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE()
 * };
 * \endcode
 */
#define TALER_MERCHANT_ORDER_PAY_OPTION_MAX_FEE(_fee) \
        (struct TALER_MERCHANT_OrderPayOption){             \
          .ot = TALER_MERCHANT_OrderPayOptionType_MAX_FEE, \
          .details.max_fee = *(_fee)                      \
        }


/**
 * Provide the merchant’s public key.
 *
 * @param _mpub  pointer to a TALER_MerchantPublicKeyP.
 *
 * @par Example
 * \code
 * struct TALER_MerchantPublicKeyP *mp = …;
 * struct TALER_MERCHANT_OrderPayOption opts[] = {
 *   TALER_MERCHANT_ORDER_PAY_OPTION_MERCHANT_PUB(mp),
 *   TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE()
 * };
 * \endcode
 */
#define TALER_MERCHANT_ORDER_PAY_OPTION_MERCHANT_PUB(_mpub) \
        (struct TALER_MERCHANT_OrderPayOption){                   \
          .ot = TALER_MERCHANT_OrderPayOptionType_MERCHANT_PUB, \
          .details.merchant_pub = *(_mpub)                      \
        }


/**
 * Stamp the request with a specific time.
 *
 * @param _ts  GNUNET_TIME_Timestamp value.
 *
 * @par Example
 * \code
 * struct GNUNET_TIME_Timestamp now = …;
 * struct TALER_MERCHANT_OrderPayOption opts[] = {
 *   TALER_MERCHANT_ORDER_PAY_OPTION_TIMESTAMP(now),
 *   TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE()
 * };
 * \endcode
 */
#define TALER_MERCHANT_ORDER_PAY_OPTION_TIMESTAMP(_ts) \
        (struct TALER_MERCHANT_OrderPayOption){              \
          .ot = TALER_MERCHANT_OrderPayOptionType_TIMESTAMP, \
          .details.timestamp = (_ts)                       \
        }


/**
 * Set a deadline by which refunds may be issued.
 *
 * @param _ts  GNUNET_TIME_Timestamp for the refund deadline.
 *
 * @par Example
 * \code
 * struct GNUNET_TIME_Timestamp rd = …;
 * struct TALER_MERCHANT_OrderPayOption opts[] = {
 *   TALER_MERCHANT_ORDER_PAY_OPTION_REFUND_DEADLINE(rd),
 *   TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE()
 * };
 * \endcode
 */
#define TALER_MERCHANT_ORDER_PAY_OPTION_REFUND_DEADLINE(_ts) \
        (struct TALER_MERCHANT_OrderPayOption){                    \
          .ot = TALER_MERCHANT_OrderPayOptionType_REFUND_DEADLINE, \
          .details.refund_deadline = (_ts)                       \
        }


/**
 * Set a deadline by which payment must be completed.
 *
 * @param _ts  GNUNET_TIME_Timestamp for the payment deadline.
 *
 * @par Example
 * \code
 * struct GNUNET_TIME_Timestamp pd = …;
 * struct TALER_MERCHANT_OrderPayOption opts[] = {
 *   TALER_MERCHANT_ORDER_PAY_OPTION_PAY_DEADLINE(pd),
 *   TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE()
 * };
 * \endcode
 */
#define TALER_MERCHANT_ORDER_PAY_OPTION_PAY_DEADLINE(_ts) \
        (struct TALER_MERCHANT_OrderPayOption){                 \
          .ot = TALER_MERCHANT_OrderPayOptionType_PAY_DEADLINE, \
          .details.pay_deadline = (_ts)                       \
        }


/**
 * Supply the merchant wire transaction hash.
 *
 * @param _hwire  pointer to a TALER_MerchantWireHashP.
 *
 * @par Example
 * \code
 * struct TALER_MerchantWireHashP *wh = …;
 * struct TALER_MERCHANT_OrderPayOption opts[] = {
 *   TALER_MERCHANT_ORDER_PAY_OPTION_H_WIRE(wh),
 *   TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE()
 * };
 * \endcode
 */
#define TALER_MERCHANT_ORDER_PAY_OPTION_H_WIRE(_hwire) \
        (struct TALER_MERCHANT_OrderPayOption){              \
          .ot = TALER_MERCHANT_OrderPayOptionType_H_WIRE,  \
          .details.h_wire = *(_hwire)                      \
        }


/**
 * Provide the unique order identifier.
 *
 * @param _oid  NULL-terminated string of the order ID.
 *
 * @par Example
 * \code
 * struct TALER_MERCHANT_OrderPayOption opts[] = {
 *   TALER_MERCHANT_ORDER_PAY_OPTION_ORDER_ID("order-42"),
 *   TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE()
 * };
 * \endcode
 */
#define TALER_MERCHANT_ORDER_PAY_OPTION_ORDER_ID(_oid) \
        (struct TALER_MERCHANT_OrderPayOption){              \
          .ot = TALER_MERCHANT_OrderPayOptionType_ORDER_ID, \
          .details.order_id = (_oid)                       \
        }


/**
 * Include a list of pay coins.
 *
 * @param _num    number of coins in the array
 * @param _coins  pointer to array of TALER_MERCHANT_PayCoin.
 *
 * @par Example
 * \code
 * const struct TALER_MERCHANT_PayCoin coins[2] = { … };
 * struct TALER_MERCHANT_OrderPayOption opts[] = {
 *   TALER_MERCHANT_ORDER_PAY_OPTION_COINS(2, coins),
 *   TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE()
 * };
 * \endcode
 */
#define TALER_MERCHANT_ORDER_PAY_OPTION_COINS(_num,_coins) \
        (struct TALER_MERCHANT_OrderPayOption){                  \
          .ot = TALER_MERCHANT_OrderPayOptionType_COINS,       \
          .details.coins = { .num_coins = (_num),              \
                             .coins = (_coins) } \
        }


/**
 * Include a list of input tokens.
 *
 * @param _num     number of tokens
 * @param _tokens  pointer to array of TALER_MERCHANT_UseToken.
 *
 * @par Example
 * \code
 * const struct TALER_MERCHANT_UseToken toks[1] = { … };
 * struct TALER_MERCHANT_OrderPayOption opts[] = {
 *   TALER_MERCHANT_ORDER_PAY_OPTION_INPUT_TOKENS(1, toks),
 *   TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE()
 * };
 * \endcode
 */

#define TALER_MERCHANT_ORDER_PAY_OPTION_INPUT_TOKENS(_num,_tokens) \
        (struct TALER_MERCHANT_OrderPayOption){                          \
          .ot = TALER_MERCHANT_OrderPayOptionType_INPUT_TOKENS,        \
          .details.input_tokens = { .num_tokens = (_num),              \
                                    .tokens = (_tokens) } \
        }


/**
 * Include a list of output tokens.
 *
 * @param _num      number of output tokens
 * @param _otokens  pointer to array of TALER_MERCHANT_OutputToken.
 *
 * @par Example
 * \code
 * const struct TALER_MERCHANT_OutputToken ots[3] = { … };
 * struct TALER_MERCHANT_OrderPayOption opts[] = {
 *   TALER_MERCHANT_ORDER_PAY_OPTION_OUTPUT_TOKENS(3, ots),
 *   TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE()
 * };
 * \endcode
 */
#define TALER_MERCHANT_ORDER_PAY_OPTION_OUTPUT_TOKENS(_num,_otokens) \
        (struct TALER_MERCHANT_OrderPayOption){                            \
          .ot = TALER_MERCHANT_OrderPayOptionType_OUTPUT_TOKENS,         \
          .details.output_tokens = { .num_output_tokens = (_num),        \
                                     .output_tokens = (_otokens) }      \
        }


/**
 * Supply the Donau service URL.
 *
 * @param _u  NULL-terminated string of the Donau endpoint.
 *
 * @par Example
 * \code
 * struct TALER_MERCHANT_OrderPayOption opts[] = {
 *   TALER_MERCHANT_ORDER_PAY_OPTION_DONAU_URL("https://donau.example"),
 *   TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE()
 * };
 * \endcode
 */
#define TALER_MERCHANT_ORDER_PAY_OPTION_DONAU_URL(_u) \
        (struct TALER_MERCHANT_OrderPayOption){             \
          .ot = TALER_MERCHANT_OrderPayOptionType_DONAU_URL, \
          .details.donau_url = (_u)                       \
        }


/**
 * Specify the Donau “year” parameter.
 *
 * @param _y  64-bit unsigned integer for the Donau year.
 *
 * @par Example
 * \code
 * struct TALER_MERCHANT_OrderPayOption opts[] = {
 *   TALER_MERCHANT_ORDER_PAY_OPTION_DONAU_YEAR(2025),
 *   TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE()
 * };
 * \endcode
 */
#define TALER_MERCHANT_ORDER_PAY_OPTION_DONAU_YEAR(_y) \
        (struct TALER_MERCHANT_OrderPayOption){              \
          .ot = TALER_MERCHANT_OrderPayOptionType_DONAU_YEAR, \
          .details.donau_year = (_y)                       \
        }


/**
 * Supply the Donau “budis” JSON structure.
 *
 * @param _js  pointer to a json_t* holding the budis data.
 *
 * @par Example
 * \code
 * json_t *budis = json_pack("{s:i}", "quota", 100);
 * struct TALER_MERCHANT_OrderPayOption opts[] = {
 *   TALER_MERCHANT_ORDER_PAY_OPTION_DONAU_BUDIS(budis),
 *   TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE()
 * };
 * \endcode
 */
#define TALER_MERCHANT_ORDER_PAY_OPTION_DONAU_BUDIS(_js) \
        (struct TALER_MERCHANT_OrderPayOption){                  \
          .ot = TALER_MERCHANT_OrderPayOptionType_DONAU_BUDIS, \
          .details.donau_budis_json = (_js)       \
        }


/**
 * Helper to call TALER_MERCHANT_order_pay_set_options()
 * with a compound‐literal array terminated by TERMINATE().
 *
 * @param ph       the payment handle
 * @param ...      a comma-separated list of `_OPTION_*()` macros
 *
 * @par Example
 * \code
 * TALER_MERCHANT_ORDER_PAY_SET_OPTIONS(handle,
 *   TALER_MERCHANT_ORDER_PAY_OPTION_MERCHANT_URL("https://…"),
 *   TALER_MERCHANT_ORDER_PAY_OPTION_AMOUNT(&amt),
 *   TALER_MERCHANT_ORDER_PAY_OPTION_MAX_FEE(&fee)
 * );
 * \endcode
 */
#define TALER_MERCHANT_ORDER_PAY_SET_OPTIONS(ph,...)          \
        MHD_NOWARN_COMPOUND_LITERALS_                   \
        TALER_MERCHANT_order_pay_set_options (                        \
          daemon,                                       \
          ((const struct TALER_MERCHANT_OrderPayHandle[])    \
           {__VA_ARGS__, TALER_MERCHANT_ORDER_PAY_OPTION_TERMINATE ()}),   \
          MHD_OPTIONS_ARRAY_MAX_SIZE)                   \


/**
 * @brief Create and initialize a new payment handle.
 *
 * @param ctx GNUNET CURL context used for HTTP operations
 * @param pay_cb callback to invoke when the payment completes or fails
 * @param pay_cb_cls closure data passed back to @a pay_cb
 * @return pointer to a newly allocated handle, or NULL on error
 */
struct TALER_MERCHANT_OrderPayHandle *
TALER_MERCHANT_order_pay_create (struct GNUNET_CURL_Context *ctx,
                                 TALER_MERCHANT_OrderPayCallback pay_cb,
                                 TALER_MERCHANT_ORDER_PAY_CALLBACK_CLOSURE_TYPE
                                 *pay_cb_cls);


/**
 * @brief Configure payment options on a handle.
 *
 * @param ph payment handle to configure
 * @param options NULL-terminated array of options (use
 *                TALER_MERCHANT_ORDER_PAY_OPTION_* macros)
 * @param max_options maximum number of options in the @a options array
 * @return #TALER_MERCHANT_OPOEC_OK on success;
 *                     error code otherwise
 */
enum TALER_MERCHANT_OrderPayErrorCode
TALER_MERCHANT_order_pay_set_options (struct TALER_MERCHANT_OrderPayHandle *ph,
                                      const struct TALER_MERCHANT_OrderPayOption
                                      options[],
                                      size_t max_options);


/**
 * @brief Start processing the payment request.
 *
 * @param ph fully configured payment handle
 * @return #TALER_MERCHANT_OPOEC_OK on successful dispatch;
 *            error code on validation or dispatch failure
 */
enum TALER_MERCHANT_OrderPayErrorCode
TALER_MERCHANT_order_pay_start (struct TALER_MERCHANT_OrderPayHandle *ph);


/**
 * @brief Cancel an in-flight or pending payment.
 *
 * @param ph payment handle to cancel and free
 */
void
TALER_MERCHANT_order_pay_cancel1 (struct TALER_MERCHANT_OrderPayHandle *ph);

#endif /* TALER_MERCHANT_PAY_SERVICE_H */
