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

  TALER is free software; you can redistribute it and/or modify it under the
  terms of the GNU Lesser General Public License as published by the Free Software
  Foundation; either version 2.1, 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.LGPL.  If not, see
  <http://www.gnu.org/licenses/>
*/
/**
 * @file src/include/taler/merchant/post-private-orders.h
 * @brief C interface for the POST /private/orders endpoint
 * @author Christian Grothoff
 */
#ifndef _TALER_MERCHANT__POST_PRIVATE_ORDERS_H
#define _TALER_MERCHANT__POST_PRIVATE_ORDERS_H

#include <taler/merchant/common.h>


/**
 * A product from inventory to include in an order.
 */
struct TALER_MERCHANT_PostPrivateOrdersInventoryProduct
{

  /**
   * Identifier of the product.
   */
  const char *product_id;

  /**
   * Quantity of the product to include.
   */
  uint32_t quantity;

  /**
   * Fractional part of the quantity.
   */
  uint32_t quantity_frac;

  /**
   * Money pot to use for this product, overrides value from
   * the inventory if given.  -1 if not set.
   * @since protocol v25.
   */
  int32_t product_money_pot;

  /**
   * Whether to use fractional quantity.
   */
  bool use_fractional_quantity;

};


/**
 * Possible options for the POST /private/orders request.
 */
enum TALER_MERCHANT_PostPrivateOrdersOption
{
  /**
   * End of list of options.
   */
  TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_END = 0,

  /**
   * Refund delay.
   */
  TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_REFUND_DELAY,

  /**
   * Payment target (type of payment method).
   */
  TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_PAYMENT_TARGET,

  /**
   * Session identifier for session-bound payments.
   */
  TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_SESSION_ID,

  /**
   * Whether to create a claim token.
   */
  TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_CREATE_TOKEN,

  /**
   * OTP device ID.
   */
  TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_OTP_ID,

  /**
   * Inventory products to include in the order.
   */
  TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_INVENTORY_PRODUCTS,

  /**
   * Lock UUIDs for inventory locks.
   */
  TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_LOCK_UUIDS

};


/**
 * Value for an option for the POST /private/orders request.
 */
struct TALER_MERCHANT_PostPrivateOrdersOptionValue
{

  /**
   * Type of the option being set.
   */
  enum TALER_MERCHANT_PostPrivateOrdersOption option;

  /**
   * Specific option value.
   */
  union
  {

    /**
     * Value if @e option is
     * #TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_REFUND_DELAY.
     */
    struct GNUNET_TIME_Relative refund_delay;

    /**
     * Value if @e option is
     * #TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_PAYMENT_TARGET.
     */
    const char *payment_target;

    /**
     * Value if @e option is
     * #TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_SESSION_ID.
     */
    const char *session_id;

    /**
     * Value if @e option is
     * #TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_CREATE_TOKEN.
     */
    bool create_token;

    /**
     * Value if @e option is
     * #TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_OTP_ID.
     */
    const char *otp_id;

    /**
     * Value if @e option is
     * #TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_INVENTORY_PRODUCTS.
     */
    struct
    {
      unsigned int num;
      const struct TALER_MERCHANT_PostPrivateOrdersInventoryProduct *products;
    } inventory_products;

    /**
     * Value if @e option is
     * #TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_LOCK_UUIDS.
     */
    struct
    {
      unsigned int num;
      const char **uuids;
    } lock_uuids;

  } details;

};


/**
 * Handle for a POST /private/orders request.
 */
struct TALER_MERCHANT_PostPrivateOrdersHandle;


/**
 * Detail about why a specific exchange was rejected when
 * creating an order.
 */
struct TALER_MERCHANT_ExchangeRejectionDetail
{

  /**
   * Base URL of the rejected exchange.
   */
  const char *exchange_url;

  /**
   * Error code indicating why this exchange was not acceptable.
   */
  enum TALER_ErrorCode code;

  /**
   * Human-readable hint about the error, or NULL.
   */
  const char *hint;

};


/**
 * Response details for a POST /private/orders request.
 */
struct TALER_MERCHANT_PostPrivateOrdersResponse
{

  /**
   * HTTP response details.
   */
  struct TALER_MERCHANT_HttpResponse hr;

  /**
   * Details depending on the HTTP status code.
   */
  union
  {

    /**
     * Details on #MHD_HTTP_OK.
     */
    struct
    {

      /**
       * Identifier of the created order.
       */
      const char *order_id;

      /**
       * Claim token, or NULL.
       */
      const struct TALER_ClaimTokenP *token;

      /**
       * Payment deadline.
       */
      struct GNUNET_TIME_Timestamp pay_deadline;

    } ok;

    /**
     * Details on #MHD_HTTP_GONE (product out of stock).
     */
    struct
    {

      /**
       * Product that is out of stock.
       */
      const char *product_id;

      /**
       * Requested quantity (integer part).
       */
      uint64_t requested_quantity;

      /**
       * Requested quantity (fractional part).
       */
      uint32_t requested_quantity_frac;

      /**
       * Available quantity (integer part).
       */
      uint64_t available_quantity;

      /**
       * Available quantity (fractional part).
       */
      uint32_t available_quantity_frac;

      /**
       * Requested quantity as string in "<integer>[.<fraction>]" format,
       * or NULL if not provided.
       */
      const char *unit_requested_quantity;

      /**
       * Available quantity as string in "<integer>[.<fraction>]" format,
       * or NULL if not provided.
       */
      const char *unit_available_quantity;

      /**
       * Expected restock time.
       */
      struct GNUNET_TIME_Timestamp restock_expected;

    } gone;

    /**
     * Details on #MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS.
     * @since protocol v25.
     */
    struct
    {

      /**
       * Number of entries in @a exchange_rejections.
       */
      unsigned int num_exchange_rejections;

      /**
       * Array of exchange rejection details.
       */
      const struct TALER_MERCHANT_ExchangeRejectionDetail
      *exchange_rejections;

    } unavailable_for_legal_reasons;

  } details;

};


/**
 * Terminate the list of the options.
 *
 * @return the terminating object
 */
#define TALER_MERCHANT_post_private_orders_option_end_()                \
        (const struct TALER_MERCHANT_PostPrivateOrdersOptionValue)     \
        {                                                               \
          .option = TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_END      \
        }

/**
 * Set refund delay.
 *
 * @param d refund delay
 * @return representation of the option
 */
#define TALER_MERCHANT_post_private_orders_option_refund_delay(d)      \
        (const struct TALER_MERCHANT_PostPrivateOrdersOptionValue)     \
        {                                                               \
          .option = TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_REFUND_DELAY, \
          .details.refund_delay = (d)                                   \
        }

/**
 * Set payment target.
 *
 * @param t payment target string
 * @return representation of the option
 */
#define TALER_MERCHANT_post_private_orders_option_payment_target(t)    \
        (const struct TALER_MERCHANT_PostPrivateOrdersOptionValue)     \
        {                                                               \
          .option = TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_PAYMENT_TARGET, \
          .details.payment_target = (t)                                 \
        }

/**
 * Set session identifier.
 *
 * @param s session ID
 * @return representation of the option
 */
#define TALER_MERCHANT_post_private_orders_option_session_id(s)        \
        (const struct TALER_MERCHANT_PostPrivateOrdersOptionValue)     \
        {                                                               \
          .option = TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_SESSION_ID, \
          .details.session_id = (s)                                     \
        }

/**
 * Set whether to create a claim token.
 *
 * @param c true to create token, false to disable
 * @return representation of the option
 */
#define TALER_MERCHANT_post_private_orders_option_create_token(c)      \
        (const struct TALER_MERCHANT_PostPrivateOrdersOptionValue)     \
        {                                                               \
          .option = TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_CREATE_TOKEN, \
          .details.create_token = (c)                                   \
        }

/**
 * Set OTP device ID.
 *
 * @param o OTP device ID
 * @return representation of the option
 */
#define TALER_MERCHANT_post_private_orders_option_otp_id(o)            \
        (const struct TALER_MERCHANT_PostPrivateOrdersOptionValue)     \
        {                                                               \
          .option = TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_OTP_ID,  \
          .details.otp_id = (o)                                         \
        }

/**
 * Set inventory products.
 *
 * @param n number of products
 * @param p array of inventory products
 * @return representation of the option
 */
#define TALER_MERCHANT_post_private_orders_option_inventory_products(n,p)   \
        (const struct TALER_MERCHANT_PostPrivateOrdersOptionValue)          \
        {                                                                    \
          .option = TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_INVENTORY_PRODUCTS \
          , \
          .details.inventory_products = { .num = (n), .products = (p) }      \
        }

/**
 * Set lock UUIDs.
 *
 * @param n number of UUIDs
 * @param u array of UUID strings
 * @return representation of the option
 */
#define TALER_MERCHANT_post_private_orders_option_lock_uuids(n,u)          \
        (const struct TALER_MERCHANT_PostPrivateOrdersOptionValue)         \
        {                                                                    \
          .option = TALER_MERCHANT_POST_PRIVATE_ORDERS_OPTION_LOCK_UUIDS,   \
          .details.lock_uuids = { .num = (n), .uuids = (u) }                \
        }


/**
 * Set the requested options for the operation.
 *
 * @param ppoh the request to set the options for
 * @param num_options length of the @a options array
 * @param options an array of options
 * @return #GNUNET_OK on success,
 *         #GNUNET_NO on failure,
 *         #GNUNET_SYSERR on internal error
 */
enum GNUNET_GenericReturnValue
TALER_MERCHANT_post_private_orders_set_options_ (
  struct TALER_MERCHANT_PostPrivateOrdersHandle *ppoh,
  unsigned int num_options,
  const struct TALER_MERCHANT_PostPrivateOrdersOptionValue *options);


/**
 * Set the requested options for the operation.
 *
 * @param ppoh the request to set the options for
 * @param ... the list of the options
 * @return #GNUNET_OK on success,
 *         #GNUNET_NO on failure,
 *         #GNUNET_SYSERR on internal error
 */
#define TALER_MERCHANT_post_private_orders_set_options(ppoh,...)                \
        TALER_MERCHANT_post_private_orders_set_options_ (                       \
          ppoh,                                                                 \
          TALER_MERCHANT_COMMON_OPTIONS_ARRAY_MAX_SIZE,                        \
          ((const struct TALER_MERCHANT_PostPrivateOrdersOptionValue[])         \
           {__VA_ARGS__, TALER_MERCHANT_post_private_orders_option_end_ () }   \
          ))


/**
 * Set up POST /private/orders operation.
 * Note that you must explicitly start the operation after
 * possibly setting options.
 *
 * @param ctx the context
 * @param url base URL of the merchant backend
 * @param order order contract (JSON)
 * @return handle to operation
 */
struct TALER_MERCHANT_PostPrivateOrdersHandle *
TALER_MERCHANT_post_private_orders_create (
  struct GNUNET_CURL_Context *ctx,
  const char *url,
  const json_t *order);


#ifndef TALER_MERCHANT_POST_PRIVATE_ORDERS_RESULT_CLOSURE
/**
 * Type of the closure used by
 * the #TALER_MERCHANT_PostPrivateOrdersCallback.
 */
#define TALER_MERCHANT_POST_PRIVATE_ORDERS_RESULT_CLOSURE void
#endif /* TALER_MERCHANT_POST_PRIVATE_ORDERS_RESULT_CLOSURE */

/**
 * Callback for a POST /private/orders request.
 *
 * @param cls closure
 * @param por response details
 */
typedef void
(*TALER_MERCHANT_PostPrivateOrdersCallback)(
  TALER_MERCHANT_POST_PRIVATE_ORDERS_RESULT_CLOSURE *cls,
  const struct TALER_MERCHANT_PostPrivateOrdersResponse *por);


/**
 * Start POST /private/orders operation.
 *
 * @param[in,out] ppoh operation to start
 * @param cb function to call with the merchant's result
 * @param cb_cls closure for @a cb
 * @return status code, #TALER_EC_NONE on success
 */
enum TALER_ErrorCode
TALER_MERCHANT_post_private_orders_start (
  struct TALER_MERCHANT_PostPrivateOrdersHandle *ppoh,
  TALER_MERCHANT_PostPrivateOrdersCallback cb,
  TALER_MERCHANT_POST_PRIVATE_ORDERS_RESULT_CLOSURE *cb_cls);


/**
 * Cancel POST /private/orders operation.  This function must not be
 * called by clients after the TALER_MERCHANT_PostPrivateOrdersCallback
 * has been invoked (as in those cases it'll be called internally by the
 * implementation already).
 *
 * @param[in] ppoh operation to cancel
 */
void
TALER_MERCHANT_post_private_orders_cancel (
  struct TALER_MERCHANT_PostPrivateOrdersHandle *ppoh);


#endif /* _TALER_MERCHANT__POST_PRIVATE_ORDERS_H */
