/*
  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 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 Affero General Public License for more details.

  You should have received a copy of the GNU Affero General Public License along with
  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
/**
 * @file include/taler/exchange/get-coins-COIN_PUB-history.h
 * @brief C interface for GET /coins/$COIN_PUB/history
 * @author Christian Grothoff
 */
#ifndef _TALER_EXCHANGE__GET_COINS_COIN_PUB_HISTORY_H
#define _TALER_EXCHANGE__GET_COINS_COIN_PUB_HISTORY_H

#include <taler/exchange/common.h>

/**
 * Information we pass per coin to a /blinding-prepare request.
 */
struct TALER_EXCHANGE_NonceKey
{
  /**
   * Which denomination key is the /blinding-prepare request for?
   */
  const struct TALER_EXCHANGE_DenomPublicKey *pk;

  /**
   * What is the number to derive the client nonce for the fresh coin?
   */
  uint32_t cnc_num;
};


/**
 * Ways how a coin's balance may change.
 */
enum TALER_EXCHANGE_CoinTransactionType
{
  /**
   * Reserved for uninitialized / none.
   */
  TALER_EXCHANGE_CTT_NONE,

  /**
   * Deposit into a contract.
   */
  TALER_EXCHANGE_CTT_DEPOSIT,

  /**
   * Spent on melt.
   */
  TALER_EXCHANGE_CTT_MELT,

  /**
   * Refunded by merchant.
   */
  TALER_EXCHANGE_CTT_REFUND,

  /**
   * Debited in recoup (to reserve) operation.
   */
  TALER_EXCHANGE_CTT_RECOUP,

  /**
   * Debited in recoup-and-refresh operation.
   */
  TALER_EXCHANGE_CTT_RECOUP_REFRESH,

  /**
   * Credited in recoup-refresh.
   */
  TALER_EXCHANGE_CTT_OLD_COIN_RECOUP,

  /**
   * Deposited into purse.
   */
  TALER_EXCHANGE_CTT_PURSE_DEPOSIT,

  /**
   * Refund from purse.
   */
  TALER_EXCHANGE_CTT_PURSE_REFUND,

  /**
   * Reserve open payment operation.
   */
  TALER_EXCHANGE_CTT_RESERVE_OPEN_DEPOSIT

};


/**
 * @brief Entry in the coin's transaction history.
 */
struct TALER_EXCHANGE_CoinHistoryEntry
{

  /**
   * Type of the transaction.
   */
  enum TALER_EXCHANGE_CoinTransactionType type;

  /**
   * Amount transferred (in or out).
   */
  struct TALER_Amount amount;

  /**
   * Offset of this entry in the coin history table.
   */
  uint64_t history_offset;

  /**
   * Details depending on @e type.
   */
  union
  {

    struct
    {
      struct TALER_MerchantWireHashP h_wire;
      struct TALER_PrivateContractHashP h_contract_terms;
      struct TALER_ExtensionPolicyHashP h_policy;
      bool no_h_policy;
      struct GNUNET_HashCode wallet_data_hash;
      bool no_wallet_data_hash;
      struct GNUNET_TIME_Timestamp wallet_timestamp;
      struct TALER_MerchantPublicKeyP merchant_pub;
      struct GNUNET_TIME_Timestamp refund_deadline;
      struct TALER_CoinSpendSignatureP sig;
      struct TALER_AgeCommitmentHashP hac;
      bool no_hac;
      struct TALER_Amount deposit_fee;
    } deposit;

    struct
    {
      struct TALER_CoinSpendSignatureP sig;
      struct TALER_RefreshCommitmentP rc;
      struct TALER_AgeCommitmentHashP h_age_commitment;
      bool no_hac;
      struct TALER_Amount melt_fee;
    } melt;

    struct
    {
      struct TALER_PrivateContractHashP h_contract_terms;
      struct TALER_MerchantPublicKeyP merchant_pub;
      struct TALER_MerchantSignatureP sig;
      struct TALER_Amount refund_fee;
      struct TALER_Amount sig_amount;
      uint64_t rtransaction_id;
    } refund;

    struct
    {
      struct TALER_ReservePublicKeyP reserve_pub;
      struct GNUNET_TIME_Timestamp timestamp;
      union GNUNET_CRYPTO_BlindingSecretP coin_bks;
      struct TALER_ExchangePublicKeyP exchange_pub;
      struct TALER_ExchangeSignatureP exchange_sig;
      struct TALER_CoinSpendSignatureP coin_sig;
    } recoup;

    struct
    {
      struct TALER_CoinSpendPublicKeyP old_coin_pub;
      union GNUNET_CRYPTO_BlindingSecretP coin_bks;
      struct GNUNET_TIME_Timestamp timestamp;
      struct TALER_ExchangePublicKeyP exchange_pub;
      struct TALER_ExchangeSignatureP exchange_sig;
      struct TALER_CoinSpendSignatureP coin_sig;
    } recoup_refresh;

    struct
    {
      struct TALER_ExchangePublicKeyP exchange_pub;
      struct TALER_ExchangeSignatureP exchange_sig;
      struct TALER_CoinSpendPublicKeyP new_coin_pub;
      struct GNUNET_TIME_Timestamp timestamp;
    } old_coin_recoup;

    struct
    {
      struct TALER_PurseContractPublicKeyP purse_pub;
      struct TALER_CoinSpendSignatureP coin_sig;
      const char *exchange_base_url;
      bool refunded;
      struct TALER_AgeCommitmentHashP phac;
    } purse_deposit;

    struct
    {
      struct TALER_PurseContractPublicKeyP purse_pub;
      struct TALER_Amount refund_fee;
      struct TALER_ExchangePublicKeyP exchange_pub;
      struct TALER_ExchangeSignatureP exchange_sig;
    } purse_refund;

    struct
    {
      struct TALER_ReserveSignatureP reserve_sig;
      struct TALER_CoinSpendSignatureP coin_sig;
      struct TALER_Amount coin_contribution;
      struct TALER_AgeCommitmentHashP h_age_commitment;
      bool no_hac;
    } reserve_open_deposit;

  } details;

};


/**
 * Parses and verifies a coin's transaction history as returned by the exchange.
 * Note that in case of incremental histories, the client must first combine
 * the incremental histories into one complete history.
 *
 * @param keys /keys data of the exchange
 * @param dk denomination key of the coin
 * @param history JSON array with the coin's history
 * @param coin_pub public key of the coin
 * @param[out] total_in set to total amount credited to the coin in @a history
 * @param[out] total_out set to total amount debited from the coin in @a history
 * @param rlen length of the @a rhistory array
 * @param[out] rhistory array where to write the parsed @a history
 * @return #GNUNET_OK if @a history is valid,
 *         #GNUNET_SYSERR if not
 */
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_parse_coin_history (
  const struct TALER_EXCHANGE_Keys *keys,
  const struct TALER_EXCHANGE_DenomPublicKey *dk,
  const json_t *history,
  const struct TALER_CoinSpendPublicKeyP *coin_pub,
  struct TALER_Amount *total_in,
  struct TALER_Amount *total_out,
  unsigned int rlen,
  struct TALER_EXCHANGE_CoinHistoryEntry rhistory[static rlen]);


/**
 * Verify that @a coin_sig does NOT appear in the @a history of a coin's
 * transactions and thus whatever transaction is authorized by @a coin_sig is
 * a conflict with @a proof.
 *
 * @param history coin history to check
 * @param coin_sig signature that must not be in @a history
 * @return #GNUNET_OK if @a coin_sig is not in @a history
 */
enum GNUNET_GenericReturnValue
TALER_EXCHANGE_check_coin_signature_conflict (
  const json_t *history,
  const struct TALER_CoinSpendSignatureP *coin_sig);


/**
 * Possible options we can set for the GET coins history request.
 */
enum TALER_EXCHANGE_GetCoinsHistoryOption
{
  /**
   * End of list of options.
   */
  TALER_EXCHANGE_GET_COINS_HISTORY_OPTION_END = 0,

  /**
   * Only return entries with history_offset strictly greater than this value.
   * Defaults to 0 (return all entries).
   */
  TALER_EXCHANGE_GET_COINS_HISTORY_OPTION_START_OFF

};


/**
 * Value for an option for the GET coins history request.
 */
struct TALER_EXCHANGE_GetCoinsHistoryOptionValue
{
  /**
   * Type of the option being set.
   */
  enum TALER_EXCHANGE_GetCoinsHistoryOption option;

  /**
   * Specific option value.
   */
  union
  {
    /**
     * Value if @e option is TALER_EXCHANGE_GET_COINS_HISTORY_OPTION_START_OFF.
     */
    uint64_t start_off;

  } details;

};


/**
 * Handle for an operation to GET /coins/$COIN_PUB/history.
 */
struct TALER_EXCHANGE_GetCoinsHistoryHandle;


/**
 * Set up GET /coins/$COIN_PUB/history operation.
 * Note that you must explicitly start the operation after
 * possibly setting options.
 *
 * @param ctx the context
 * @param url base URL of the exchange
 * @param coin_priv private key of the coin (used for authentication)
 * @return handle to operation
 */
struct TALER_EXCHANGE_GetCoinsHistoryHandle *
TALER_EXCHANGE_get_coins_history_create (
  struct GNUNET_CURL_Context *ctx,
  const char *url,
  const struct TALER_CoinSpendPrivateKeyP *coin_priv);


/**
 * Terminate the list of options.
 *
 * @return the terminating object of struct TALER_EXCHANGE_GetCoinsHistoryOptionValue
 */
#define TALER_EXCHANGE_get_coins_history_option_end_()                   \
        (const struct TALER_EXCHANGE_GetCoinsHistoryOptionValue)         \
        {                                                                 \
          .option = TALER_EXCHANGE_GET_COINS_HISTORY_OPTION_END          \
        }

/**
 * Set starting offset for partial history fetch.
 *
 * @param o offset: only return entries with history_offset > this value
 * @return representation of the option as a struct TALER_EXCHANGE_GetCoinsHistoryOptionValue
 */
#define TALER_EXCHANGE_get_coins_history_option_start_off(o)              \
        (const struct TALER_EXCHANGE_GetCoinsHistoryOptionValue)          \
        {                                                                  \
          .option = TALER_EXCHANGE_GET_COINS_HISTORY_OPTION_START_OFF,    \
          .details.start_off = (o)                                         \
        }


/**
 * Set the requested options for the operation.
 *
 * If any option fails, other options may or may not be applied.
 *
 * @param gcsh 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_EXCHANGE_get_coins_history_set_options_ (
  struct TALER_EXCHANGE_GetCoinsHistoryHandle *gcsh,
  unsigned int num_options,
  const struct TALER_EXCHANGE_GetCoinsHistoryOptionValue *options);


/**
 * Set the requested options for the operation.
 *
 * If any option fails, other options may or may not be applied.
 *
 * It should be used with helpers that create required options, for example:
 *
 * TALER_EXCHANGE_get_coins_history_set_options (
 *   gcsh,
 *   TALER_EXCHANGE_get_coins_history_option_start_off (last_off));
 *
 * @param gcsh the request to set the options for
 * @param ... the list of options, each created by a
 *            TALER_EXCHANGE_get_coins_history_option_NAME(VALUE) helper
 * @return #GNUNET_OK on success,
 *         #GNUNET_NO on failure,
 *         #GNUNET_SYSERR on internal error
 */
#define TALER_EXCHANGE_get_coins_history_set_options(gcsh,...)              \
        TALER_EXCHANGE_get_coins_history_set_options_ (                     \
          gcsh,                                                              \
          TALER_EXCHANGE_COMMON_OPTIONS_ARRAY_MAX_SIZE,                     \
          ((const struct TALER_EXCHANGE_GetCoinsHistoryOptionValue[])       \
           {__VA_ARGS__, TALER_EXCHANGE_get_coins_history_option_end_ () }  \
          ))


/**
 * Response to a GET /coins/$COIN_PUB/history request.
 */
struct TALER_EXCHANGE_GetCoinsHistoryResponse
{
  /**
   * HTTP response data.
   */
  struct TALER_EXCHANGE_HttpResponse hr;

  /**
   * Details depending on @e hr.http_status.
   */
  union
  {
    /**
     * Information returned on #MHD_HTTP_OK.
     */
    struct
    {
      /**
       * Coin transaction history (possibly partial).
       * Not yet validated; combine with other already known history data
       * for this coin and then use TALER_EXCHANGE_parse_coin_history() to
       * validate the complete history and obtain it in binary format.
       */
      const json_t *history;

      /**
       * Hash of the coin denomination's public key.
       */
      struct TALER_DenominationHashP h_denom_pub;

      /**
       * Current coin balance.
       */
      struct TALER_Amount balance;

    } ok;

  } details;

};


#ifndef TALER_EXCHANGE_GET_COINS_HISTORY_RESULT_CLOSURE
/**
 * Type of the closure used by
 * the #TALER_EXCHANGE_GetCoinsHistoryCallback.
 */
#define TALER_EXCHANGE_GET_COINS_HISTORY_RESULT_CLOSURE void
#endif /* TALER_EXCHANGE_GET_COINS_HISTORY_RESULT_CLOSURE */

/**
 * Type of the function that receives the result of a
 * GET /coins/$COIN_PUB/history request.
 *
 * @param cls closure
 * @param result result returned by the HTTP server
 */
typedef void
(*TALER_EXCHANGE_GetCoinsHistoryCallback)(
  TALER_EXCHANGE_GET_COINS_HISTORY_RESULT_CLOSURE *cls,
  const struct TALER_EXCHANGE_GetCoinsHistoryResponse *result);


/**
 * Start GET /coins/$COIN_PUB/history operation.
 *
 * @param[in,out] gcsh operation to start
 * @param cb function to call with the exchange's result
 * @param cb_cls closure for @a cb
 * @return status code, #TALER_EC_NONE on success
 */
enum TALER_ErrorCode
TALER_EXCHANGE_get_coins_history_start (
  struct TALER_EXCHANGE_GetCoinsHistoryHandle *gcsh,
  TALER_EXCHANGE_GetCoinsHistoryCallback cb,
  TALER_EXCHANGE_GET_COINS_HISTORY_RESULT_CLOSURE *cb_cls);


/**
 * Cancel GET /coins/$COIN_PUB/history operation.  This function must not
 * be called by clients after the TALER_EXCHANGE_GetCoinsHistoryCallback has
 * been invoked (as in those cases it'll be called internally by the
 * implementation already).
 *
 * @param[in] gcsh operation to cancel
 */
void
TALER_EXCHANGE_get_coins_history_cancel (
  struct TALER_EXCHANGE_GetCoinsHistoryHandle *gcsh);


#endif /* _TALER_EXCHANGE__GET_COINS_COIN_PUB_HISTORY_H */
