/*
  This file is part of GNU Taler
  Copyright (C) 2024 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 include/taler_merchant_util.h
 * @brief Interface for common utility functions
 * @author Christian Grothoff
 */
#ifndef TALER_MERCHANT_UTIL_H
#define TALER_MERCHANT_UTIL_H

#include <gnunet/gnunet_common.h>
#include <gnunet/gnunet_util_lib.h>
#include <gnunet/gnunet_json_lib.h>
#include <stdint.h>
#include <taler/taler_util.h>
#include <jansson.h>

/**
 * Fixed-point base for inventory quantities (powers of 10).
 * Six decimal digits are supported to match the maximum unit precision.
 */
#define MERCHANT_UNIT_FRAC_BASE 1000000U
#define MERCHANT_UNIT_FRAC_MAX_DIGITS 6U

/**
 * Return default project data used by Taler merchant.
 */
const struct GNUNET_OS_ProjectData *
TALER_MERCHANT_project_data (void);


/**
 * Channel used to transmit MFA authorization request.
 */
enum TALER_MERCHANT_MFA_Channel
{
  /**
   * No MFA channel.
   */
  TALER_MERCHANT_MFA_CHANNEL_NONE = 0,

  /**
   * SMS ("sms")
   */
  TALER_MERCHANT_MFA_CHANNEL_SMS = 1,

  /**
   * E-mail ("email")
   */
  TALER_MERCHANT_MFA_CHANNEL_EMAIL,

  /**
   * TOTP ("totp"). (Not yet implemented, #10327.)
   */
  TALER_MERCHANT_MFA_CHANNEL_TOTP
};


/**
 * Types of critical operations of a merchant backend that may require
 * multi-factor authorization.
 */
enum TALER_MERCHANT_MFA_CriticalOperation
{
  /**
   * Marker used to indicate that this is NOT a critical operation.
   */
  TALER_MERCHANT_MFA_CO_NONE = 0,

  /**
   * Instance provisioning ("instance_provision").
   */
  TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION,

  /**
   * Bank account configuration or reconfiguration ("account_config").
   */
  TALER_MERCHANT_MFA_CO_ACCOUNT_CONFIGURATION,

  /**
   * Authentication configuration change ("auth_config").
   */
  TALER_MERCHANT_MFA_CO_AUTH_CONFIGURATION,

  /**
   * Instance deletion ("instance_deletion").
   */
  TALER_MERCHANT_MFA_CO_INSTANCE_DELETION,

  /**
   * Authentication token creation ("auth_token_creation").
   */
  TALER_MERCHANT_MFA_CO_AUTH_TOKEN_CREATION


};


/**
 * Convert critical operation enumeration value to string
 * suitable for human readers.
 *
 * @param co input to convert
 * @return operation value as string
 */
const char *
TALER_MERCHANT_MFA_co2s (
  enum TALER_MERCHANT_MFA_CriticalOperation co);


/**
 * Convert critical operation enumeration value to string.
 *
 * @param co input to convert
 * @return operation value as string
 */
const char *
TALER_MERCHANT_MFA_co_to_string (
  enum TALER_MERCHANT_MFA_CriticalOperation co);


/**
 * Convert string to critical operation enumeration value.
 *
 * @param str input to convert
 * @return #TALER_MERCHANT_MFA_CO_NONE on failure
 */
enum TALER_MERCHANT_MFA_CriticalOperation
TALER_MERCHANT_MFA_co_from_string (const char *str);


/**
 * Convert MFA channel enumeration value to string.
 *
 * @param ch input to convert
 * @return operation value as string
 */
const char *
TALER_MERCHANT_MFA_channel_to_string (
  enum TALER_MERCHANT_MFA_Channel ch);


/**
 * Convert string to MFA channel enumeration value.
 *
 * @param str input to convert
 * @return #TALER_MERCHANT_MFA_CHANNEL_NONE on failure
 */
enum TALER_MERCHANT_MFA_Channel
TALER_MERCHANT_MFA_channel_from_string (const char *str);


/**
 * @brief Salted hash of a body for a request that required MFA.
 */
struct TALER_MERCHANT_MFA_BodyHash
{
  /**
   * Hash of the body and salt.
   */
  struct GNUNET_ShortHashCode hash;
};


/**
 * @brief Salt used when computing a `struct TALER_MERCHANT_MFA_BodyHash`
 */
struct TALER_MERCHANT_MFA_BodySalt
{
  /**
   * Salt.
   */
  uint64_t salt[128 / 64];
};


/**
 * Hash the given request @a body with the given @a salt to
 * produce @a h_body for MFA checks.
 *
 * @param body HTTP request body, NULL if body was empty
 * @param salt salt to use
 * @param h_body resulting hash
 */
void
TALER_MERCHANT_mfa_body_hash (
  const json_t *body,
  const struct TALER_MERCHANT_MFA_BodySalt *salt,
  struct TALER_MERCHANT_MFA_BodyHash *h_body);


/**
 * Possible versions of the contract terms.
 */
enum TALER_MERCHANT_ContractVersion
{

  /**
   * Version 0
   */
  TALER_MERCHANT_CONTRACT_VERSION_0 = 0,

  /**
   * Version 1
   */
  TALER_MERCHANT_CONTRACT_VERSION_1 = 1
};

/**
 * Possible token kinds.
 */
enum TALER_MERCHANT_ContractTokenKind
{
  /**
   * Token kind invalid
   */
  TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID = 0,

  /**
   * Subscription token kind
   */
  TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION = 1,

  /**
   * Discount token kind
   */
  TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT = 2,
};

/**
 * Possible input types for the contract terms.
 */
enum TALER_MERCHANT_ContractInputType
{

  /**
   * Input type invalid
   */
  TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID = 0,

#if FUTURE
  /**
   * Input type coin
   */
  TALER_MERCHANT_CONTRACT_INPUT_TYPE_COIN = 1,
#endif
  /**
   * Input type token
   */
  TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN = 2
};

/**
 * Contract input (part of the v1 contract terms).
 */
struct TALER_MERCHANT_ContractInput
{
  /**
  * Type of the input.
  */
  enum TALER_MERCHANT_ContractInputType type;

  union
  {
#if FUTURE
    /**
     * Coin-based input (ration). (Future work, only here for reference)
     */
    struct
    {
      /**
      * Price to be paid.
      */
      struct TALER_Amount price;

      /**
      * Base URL of the ration authority.
      */
      const char *ration_authority_url;
    } coin;
#endif

    /**
     * Token-based input.
     */
    struct
    {
      /**
       * Slug of the token family to be used.
       */
      const char *token_family_slug;

      /**
       * Number of tokens of this type required. Defaults to one if the
       * field is not provided.
       */
      unsigned int count;
    } token;
  } details;
};

/**
 * Possible output types for the contract terms.
 */
enum TALER_MERCHANT_ContractOutputType
{

  /**
   * Invalid output type
   */
  TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID = 0,

  /**
   * Output type token
   */
  TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN = 1,

  /**
   * Output type donation-receipt
   */
  TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT = 2,
#if FUTURE
  /**
   * Output type coin
   */
  TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_COIN = 3
#endif

};

/**
 * Contract output (part of the v1 contract terms).
 */
struct TALER_MERCHANT_ContractOutput
{
  /**
   * Type of the output.
   */
  enum TALER_MERCHANT_ContractOutputType type;

  union
  {
#if FUTURE
    /**
     * Coin-based output.
     */
    struct
    {
      /**
       * Coins that will be yielded. This excludes any applicable withdraw fees.
       */
      struct TALER_Amount brutto_yield;

      /**
       * Base URL of the exchange that will issue the coins.
       *
       * NOTE: Once implemented, check if we need to allocate this here or if
       * we again reference the JSON as we do in other places.
       */
      char *exchange_url;

    } coin;
#endif
    /**
     * DONAU-receipt output.
     */
    struct
    {
      /**
       * Amount of the donation. (optional)
       */
      struct TALER_Amount amount;

      /**
       * Base URLs of the donation authorities that will issue the tax receipt.
       */
      char **donau_urls;

      /**
       * Length of the @e donau_urls array.
       */
      unsigned int donau_urls_len;

    } donation_receipt;

    /**
     * Token-based output.
     */
    struct
    {
      /**
       * Slug of the token family to be issued.
       * Note: this is a pointer into the JSON of the
       * respective contract/request and not owned here.
       */
      const char *token_family_slug;

      /**
       * Index of the public key in the @a token_family_slug's token family
       * ``keys`` array that this output token will have.
       */
      unsigned int key_index;

      /**
       * Number of tokens of this type required. Defaults to one if the
       * field is not provided.
       */
      unsigned int count;

      /**
       * Determines when the output token should be valid.
       * Optional, set to zero for not specified (then we
       * use the current time).
       */
      struct GNUNET_TIME_Timestamp valid_at;

    } token;

  } details;

};

/**
 * Contract choice (part of the v1 contract terms).
 */
struct TALER_MERCHANT_ContractChoice
{

  /**
   * Amount to be paid for this choice.
   */
  struct TALER_Amount amount;

  /**
   * Human readable description of the semantics of the choice within the
   * contract to be shown to the user at payment.
   */
  char *description;

  /**
   * Map from IETF BCP 47 language tags to localized description.
   */
  json_t *description_i18n;

  /**
   * Maximum fee the merchant is willing to pay for this choice.
   * Set to an invalid amount to use instance defaults (zero or STEFAN).
   */
  struct TALER_Amount max_fee;

  /**
   * List of inputs the wallet must provision (all of them) to satisfy the
   * conditions for the contract.
   */
  struct TALER_MERCHANT_ContractInput *inputs;

  /**
   * Length of the @e inputs array.
   */
  unsigned int inputs_len;

  /**
   * List of outputs the merchant promises to yield (all of them) once
   * the contract is paid.
   */
  struct TALER_MERCHANT_ContractOutput *outputs;

  /**
   * Length of the @e outputs array.
   */
  unsigned int outputs_len;
};

/**
 * Public key and corresponding metadata for a token family.
 */
struct TALER_MERCHANT_ContractTokenFamilyKey
{
  /**
   * Public key.
   */
  struct TALER_TokenIssuePublicKey pub;

  /**
   * Start time of the token family duration.
   */
  struct GNUNET_TIME_Timestamp valid_after;

  /**
   * Tokens signed by this key will be valid until this time.
   */
  struct GNUNET_TIME_Timestamp valid_before;
};


/**
 * Represents a family of tokens issued by merchants that can be used in contracts.
 */
struct TALER_MERCHANT_ContractTokenFamily
{
  /**
   * Slug of the token family.
   */
  char *slug;

  /**
   * Human-readable name of the token family.
   */
  char *name;

  /**
   * Human-readable description of the semantics of the tokens issued by
   * this token family.
   */
  char *description;

  /**
   * Map from IETF BCP 47 language tags to localized description.
   */
  json_t *description_i18n;

  /**
   * Relevant public keys of this token family for the given contract.
   */
  struct TALER_MERCHANT_ContractTokenFamilyKey *keys;

  /**
   * Length of the @e keys array.
   */
  unsigned int keys_len;

  /**
   * Must a wallet understand this token type to process contracts that
   * consume or yield it?
   */
  bool critical;

  /**
   * Kind of the token family.
   */
  enum TALER_MERCHANT_ContractTokenKind kind;

  /**
   * Kind-specific information about the token.
   */
  union
  {
    /**
     * Subscription token.
     */
    struct
    {
      /**
       * Array of domain names where this subscription can be safely used
       * (e.g. the issuer warrants that these sites will re-issue tokens of
       * this type if the respective contract says so). May contain "*" for
       * any domain or subdomain.
       */
      char **trusted_domains;

      /**
       * Length of the @e trusted_domains array.
       */
      unsigned int trusted_domains_len;
    } subscription;

    /**
    * Discount token.
    */
    struct
    {
      /**
       * Array of domain names where this discount token is intended to be
       * used. May contain "*" for any domain or subdomain. Users should be
       * warned about sites proposing to consume discount tokens of this
       * type that are not in this list that the merchant is accepting a
       * coupon from a competitor and thus may be attaching different
       * semantics (like get 20% discount for my competitors 30% discount
       * token).
       */
      char **expected_domains;

      /**
       * Length of the @e expected_domains array.
       */
      unsigned int expected_domains_len;

    } discount;
  } details;
};


/**
 * Struct to hold contract terms.
 */
struct TALER_MERCHANT_Contract
{
  /**
   * URL where the same contract could be ordered again (if available).
   */
  char *public_reorder_url;

  /**
   * Our order ID.
   */
  char *order_id;

  /**
   * Merchant base URL.
   */
  char *merchant_base_url;

  /**
   * Merchant information.
   */
  struct
  {
    /**
     * Legal name of the instance
     */
    char *name;

    /**
     * Merchant's site url
     */
    char *website;

    /**
     * Email contact for customers
     */
    char *email;

    /**
     * merchant's logo data uri
     */
    char *logo;

    /**
     * Merchant address
     */
    json_t *address;

    /**
     * Jurisdiction of the business
     */
    json_t *jurisdiction;

  } merchant;

  /**
   * Summary of the contract.
   */
  char *summary;

  /**
   * Internationalized summary.
   */
  json_t *summary_i18n;

  /**
   * URL that will show that the contract was successful
   * after it has been paid for.
   */
  char *fulfillment_url;

  /**
   * Message shown to the customer after paying for the contract.
   * Either fulfillment_url or fulfillment_message must be specified.
   */
  char *fulfillment_message;

  /**
   * Map from IETF BCP 47 language tags to localized fulfillment messages.
   */
  json_t *fulfillment_message_i18n;

  /**
   * Array of products that are part of the purchase.
   */
  json_t *products;

  /**
   * Timestamp of the contract.
   */
  struct GNUNET_TIME_Timestamp timestamp;

  /**
   * Deadline for refunds.
   */
  struct GNUNET_TIME_Timestamp refund_deadline;

  /**
   * Specifies for how long the wallet should try to get an
   * automatic refund for the purchase.
   */
  struct GNUNET_TIME_Relative auto_refund;

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

  /**
   * Wire transfer deadline.
   */
  struct GNUNET_TIME_Timestamp wire_deadline;

  /**
   * Delivery date.
   */
  struct GNUNET_TIME_Timestamp delivery_date;


  /**
   * Merchant public key.
   */
  struct TALER_MerchantPublicKeyP merchant_pub;

  /**
   * The hash of the merchant instance's wire details.
   * TODO: appropriate type
   */
  struct TALER_MerchantWireHashP h_wire;

  /**
   * Wire transfer method identifier for the wire method associated with
   h_wire.
   */
  char *wire_method;

  /**
   * Exchanges that the merchant accepts even if it does not accept any auditors that audit them.
   * TODO: appropriate type
   */
  json_t *exchanges;

  /**
   * Delivery location.
   */
  json_t *delivery_location;

  /**
   * Nonce generated by the wallet and echoed by the merchant
   * in this field when the proposal is generated.
   */
  char *nonce;

  /**
   * Extra data that is only interpreted by the merchant frontend.
   */
  json_t *extra;

  /**
   * Minimum age the buyer must have (in years).
   */
  uint8_t minimum_age;

  /**
   * Specified version of the contract.
   */
  enum TALER_MERCHANT_ContractVersion version;

  /**
   * Details depending on the @e version.
   */
  union
  {

    /**
     * Details for v0 contracts.
     */
    struct
    {

      /**
       * Price to be paid for the transaction. Could be 0. The price is in addition
       * to other instruments, such as rations and tokens.
       * The exchange will subtract deposit fees from that amount
       * before transferring it to the merchant.
       */
      struct TALER_Amount brutto;

      /**
      * Maximum fee as given by the client request.
      */
      struct TALER_Amount max_fee;

    } v0;

    /**
     * Details for v1 contracts.
     */
    struct
    {

      /**
       * Array of possible specific contracts the wallet/customer may choose
       * from by selecting the respective index when signing the deposit
       * confirmation.
       */
      struct TALER_MERCHANT_ContractChoice *choices;

      /**
       * Length of the @e choices array.
       */
      unsigned int choices_len;

      /**
       * Array of token authorities.
       */
      struct TALER_MERCHANT_ContractTokenFamily *token_authorities;

      /**
       * Length of the @e token_authorities array.
       */
      unsigned int token_authorities_len;

    } v1;

  } details;

};


/**
 * Parse JSON contract terms in @a input.
 *
 * @param[in] input JSON object containing contract terms
 * @param nonce_optional whether `nonce' field is optional
 * @return parsed contract terms; NULL if @a input is malformed
 */
struct TALER_MERCHANT_Contract *
TALER_MERCHANT_contract_parse (json_t *input,
                               bool nonce_optional);


/**
 * Provide specification to parse an JSON contract input type.
 * The value is provided as a descriptive string.
 *
 * @param name name of the JSON member with the contract type
 * @param[out] cit where to store the contract input type
 * @return spec for parsing a contract input type
 */
struct GNUNET_JSON_Specification
TALER_MERCHANT_json_spec_cit (const char *name,
                              enum TALER_MERCHANT_ContractInputType *cit);


/**
 * Parse JSON contract terms choice input.
 *
 * @param[in] root JSON object containing choice input
 * @param[out] input parsed choice input, NULL if @a input is malformed
 * @param index index of choice input in inputs array
 * @param order whether @a input is contained in order or contract terms
 * @return #GNUNET_SYSERR if @a input is malformed; #GNUNET_OK otherwise
 */
enum GNUNET_GenericReturnValue
TALER_MERCHANT_parse_choice_input (
  json_t *root,
  struct TALER_MERCHANT_ContractInput *input,
  size_t index,
  bool order);


/**
 * Provide specification to parse an JSON contract output type.
 * The value is provided as a descriptive string.
 *
 * @param name name of the JSON member with the contract type
 * @param[out] cot where to store the contract output type
 * @return spec for parsing a contract output type
 */
struct GNUNET_JSON_Specification
TALER_MERCHANT_json_spec_cot (const char *name,
                              enum TALER_MERCHANT_ContractOutputType *cot);


/**
 * Parse JSON contract terms choice output.
 *
 * @param[in] root JSON object containing choice output
 * @param[out] output parsed choice output, NULL if @a output is malformed
 * @param index index of choice output in outputs array
 * @param order whether @a output is contained in order or contract terms
 * @return #GNUNET_SYSERR if @a output is malformed; #GNUNET_OK otherwise
 */
enum GNUNET_GenericReturnValue
TALER_MERCHANT_parse_choice_output (
  json_t *root,
  struct TALER_MERCHANT_ContractOutput *output,
  size_t index,
  bool order);


/**
 * Serialize contract terms into JSON object.
 *
 * @param[in] input contract terms to serialize
 * @param nonce_optional whether `nonce' field is optional
 * @return JSON representation of @a input; NULL on error
 */
json_t *
TALER_MERCHANT_contract_serialize (
  const struct TALER_MERCHANT_Contract *input,
  bool nonce_optional);


/**
 * Get JSON representation of contract choice.
 *
 * @param[in] choice contract choice to serialize
 * @param order whether @a choice is contained in order or contract terms
 * @return JSON representation of @a choice; NULL on error
 */
json_t *
TALER_MERCHANT_json_from_contract_choice (
  const struct TALER_MERCHANT_ContractChoice *choice,
  bool order);


/**
 * Get JSON representation of contract token family.
 *
 * @param[in] family contract token family to serialize
 * @return JSON representation of @a family; NULL on error
 */
json_t *
TALER_MERCHANT_json_from_token_family (
  const struct TALER_MERCHANT_ContractTokenFamily *family);


/**
 * Find token family in contract terms from slug and validity date.
 *
 * @param slug slug of the token family
 * @param valid_after validity start of the token family
 * @param[in] families array of token families in the contract terms
 * @param families_len length of @a families array
 * @param[out] family matching token family; NULL if no result
 * @param[out] key key of matching token family; NULL if no result
 * @return #GNUNET_SYSERR if no matching family found; #GNUNET_OK otherwise
 */
enum GNUNET_GenericReturnValue
TALER_MERCHANT_find_token_family_key (
  const char *slug,
  struct GNUNET_TIME_Timestamp valid_after,
  const struct TALER_MERCHANT_ContractTokenFamily *families,
  unsigned int families_len,
  struct TALER_MERCHANT_ContractTokenFamily *family,
  struct TALER_MERCHANT_ContractTokenFamilyKey *key);


/**
 * Free all the fields in the given @a choice, but not @a choice itself, since
 * it is normally part of an array.
 *
 * @param[in] choice contract terms choice to free
 */
void
TALER_MERCHANT_contract_choice_free (
  struct TALER_MERCHANT_ContractChoice *choice);


/**
 * Free the @a contract and all fields in it.
 *
 * @param[in] contract contract to free
 */
void
TALER_MERCHANT_contract_free (struct TALER_MERCHANT_Contract *contract);

#endif
