/*
  This file is part of TALER
  Copyright (C) 2021-2023 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 taler-merchant-httpd_helper.h
 * @brief helpers for shared logic
 * @author Florian Dold
 * @author Benedikt Mueller
 * @author Christian Grothoff
 */
#ifndef TALER_MERCHANT_HTTPD_HELPER_H
#define TALER_MERCHANT_HTTPD_HELPER_H

#define TMH_MAX_FRACTIONAL_PRECISION_LEVEL 6


#include "taler-merchant-httpd.h"

/**
 * check @a accounts for well-formedness
 *
 * @param accounts JSON array of merchant accounts (presumably)
 * @return true if they are all valid accounts
 */
bool
TMH_accounts_array_valid (const json_t *accounts);


/**
 * Check if @a taxes is an array of valid Taxes in the sense of
 * Taler's API definition.
 *
 * @param taxes array to check
 * @return true if @a taxes is an array and all
 *         entries are valid Taxes.
 */
bool
TMH_taxes_array_valid (const json_t *taxes);


/**
 * Check if @a location is a valid Location object in the sense of Taler's API
 * definition.
 *
 * @param location object to check
 * @return true if @a location is an object
 *         representing a Location.
 */
bool
TMH_location_object_valid (const json_t *location);


/**
 * Check if @a products is an array of valid Product(s) in the sense of
 * Taler's API definition.
 *
 * @param products array to check
 * @return true if @a products is an array and all
 *         entries are valid Products.
 */
bool
TMH_products_array_valid (const json_t *products);


/**
 * Parse decimal quantity expressed as string for request handling.
 *
 * @param value string to parse
 * @param[out] integer_part result integer component
 * @param[out] fractional_part result fractional component (0..MERCHANT_UNIT_FRAC_BASE-1)
 * @return #GNUNET_OK on success, #GNUNET_SYSERR on validation failure
 */
enum GNUNET_GenericReturnValue
TMH_parse_fractional_string (const char *value,
                             int64_t *integer_part,
                             uint32_t *fractional_part);

/**
 * Kind of fixed-decimal value the helpers operate on.
 * Primarily distinguishes how special sentinel values (such as "-1"
 * meaning infinity for stock) must be encoded.
 */
enum TMH_ValueKind
{
  TMH_VK_QUANTITY,  /* -1 is illegal                    */
  TMH_VK_STOCK      /* -1 means "infinity"              */
};

/**
 * Extract a fixed-decimal number that may be supplied either
 *   - as pure integer  (e.g. "total_stock"), or
 *   - as decimal text (e.g. "unit_total_stock").
 *
 * Rules:
 *  - If both forms are missing          -> error.
 *  - If both are present                -> they must match and the decimal must have no fraction.
 *  - For kind == TMH_VK_STOCK the integer value -1 represents infinity.
 *
 * @param kind             See #TMH_ValueKind
 * @param allow_fractional False: any fractional part is rejected
 * @param int_missing      True if client omitted the integer field
 * @param int_raw          Raw integer (undefined if @a int_missing is true)
 * @param str_missing      True if client omitted the string field
 * @param str_raw          Raw UTF-8 string (undefined if @a str_missing is true)
 * @param[out] int_out     Canonicalised integer part
 * @param[out] frac_out    Canonicalised fractional part
 * @param[out] error_param Set to offending field name on failure
 * @param int_field        Integer field name (for error reporting)
 * @param str_field        String field name (for error reporting)
 * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
 */
enum GNUNET_GenericReturnValue
TMH_process_quantity_inputs (enum TMH_ValueKind kind,
                             bool allow_fractional,
                             bool int_missing,
                             int64_t int_raw,
                             bool str_missing,
                             const char *str_raw,
                             uint64_t *int_out,
                             uint32_t *frac_out,
                             const char **error_param);

/**
 * Lookup the defaults for @a unit within @a mi and fall back to sane
 * values (disallow fractional quantities, zero precision) if no data
 * is available.
 *
 * @param mi merchant instance whose defaults should be consulted (must not be NULL)
 * @param unit textual unit name (must not be NULL or empty)
 * @param allow_fractional updated with whether fractional quantities are allowed (must not be NULL)
 * @param precision_level updated with the supported precision (must not be NULL)
 */
void
TMH_quantity_defaults_from_unit (const struct TMH_MerchantInstance *mi,
                                 const char *unit,
                                 bool *allow_fractional,
                                 uint32_t *precision_level);

/**
 * Query the database for precision defaults tied to @a unit within
 * @a mi.  Returns #GNUNET_OK even if no unit information exists, in
 * which case the out-parameters remain at their implicit defaults.
 *
 * @param mi merchant instance whose unit table is inspected (must not be NULL)
 * @param unit textual unit name (must not be NULL or empty)
 * @param allow_fractional updated with whether fractional quantities are allowed (must not be NULL)
 * @param precision_level updated with the supported precision (must not be NULL)
 * @return #GNUNET_OK on success, #GNUNET_SYSERR on database failure
 */
enum GNUNET_GenericReturnValue
TMH_unit_defaults_for_instance (const struct TMH_MerchantInstance *mi,
                                const char *unit,
                                bool *allow_fractional,
                                uint32_t *precision_level);

/**
 * Format a fixed-decimal pair into canonical string representation.
 * Recognises INT64_MAX / INT32_MAX as the "-1" sentinel used for
 * infinite stock if @a kind equals #TMH_VK_STOCK.
 *
 * @param kind specifies whether sentinel values are permitted
 * @param integer integer portion
 * @param fractional fractional portion (0..MERCHANT_UNIT_FRAC_BASE-1 or sentinel)
 * @param buffer output buffer
 * @param buffer_length length of @a buffer
 */
void
TMH_format_fractional_string (enum TMH_ValueKind kind,
                              uint64_t integer,
                              uint32_t fractional,
                              size_t buffer_length,
                              char buffer[static buffer_length]);

/**
 * Check if @a image_data_url is a valid image
 * data URL. Does not validate the actual payload,
 * only the syntax and that it properly claims to
 * be an image.
 *
 * @param image_data_url string to check
 * @return true if @a image_data_url is a data
 *         URL with an "image/" mime-type
 */
bool
TMH_image_data_url_valid (const char *image_data_url);


/**
 * Check if @a template_contract is a valid template_contract  object in the sense of Taler's API
 * definition.
 *
 * @param template_contract object to check
 * @return true if @a template_location is an object
 *         representing a template_location.
 */
bool
TMH_template_contract_valid (const json_t *template_contract);


/**
 * Setup new wire method for the given @ payto_uri.
 *
 * @param payto_uri already validated payto URI
 * @param credit_facade_url where to download credit information for this account (can be NULL)
 * @param credit_facade_credentials credentials for the @a credit_facade_url
 * @return new wire method object, never fails
 */
struct TMH_WireMethod *
TMH_setup_wire_account (
  struct TALER_FullPayto payto_uri,
  const char *credit_facade_url,
  const json_t *credit_facade_credentials);


/**
 * Test if JSON spec @a account for a wire method is equal to the given @a wm.
 *
 * @param account JSON spec for a merchant account
 * @param wm known wire method
 * @return #GNUNET_YES if both specifications are equal
 *  #GNUNET_NO if the specifications are for
 *      the same account but differ in the credit facade
 *  #GNUNET_SYSERR if the specs are for different accounts
 *     or if @a account is malformed
 */
enum GNUNET_GenericReturnValue
TMH_cmp_wire_account (
  const json_t *account,
  const struct TMH_WireMethod *wm);


/**
 * Check that the provided authentication configuration
 * is valid.
 *
 * @param connection connection to use for returning errors
 * @param jauth JSON with authentication data
 * @param[out] auth_token set to the authentication token
 * @return #GNUNET_OK on success,
 *   #GNUNET_NO if an error was returned on @a connection
 *   #GNUNET_SYSERR if we failed to return an error on @a connection
 */
enum GNUNET_GenericReturnValue
TMH_check_auth_config (struct MHD_Connection *connection,
                       const json_t *jauth,
                       const char **auth_token);


/**
 * Generate binary UUID from client-provided UUID-string.
 *
 * @param uuids string intpu
 * @param[out] uuid set to binary UUID
 */
void
TMH_uuid_from_string (const char *uuids,
                      struct GNUNET_Uuid *uuid);


/**
 * Initializes a buffer with
 * the ``http[s]://$HOST/[$PATH/][instances/$INSTANCE/]``
 * string using $HOST and $PATH from @a connection.
 *
 * @param[in] connection connection to base the construction on
 * @param instance instance to set, NULL for none
 * @param[out] buf buffer to initialize
 * @return #GNUNET_OK on success
 */
enum GNUNET_GenericReturnValue
TMH_base_url_by_connection (struct MHD_Connection *connection,
                            const char *instance,
                            struct GNUNET_Buffer *buf);


/**
 * Initializes a buffer with
 * the ``taler[+http]://$METHOD/$HOST/[instances/$INSTANCE/]``
 * string using $HOST from @a connection.
 *
 * @param[in] connection connection to base the construction on
 * @param method taler-URI method to inject
 * @param instance instance to set, NULL for none
 * @param[out] buf buffer to initialize
 * @return #GNUNET_OK on success
 */
enum GNUNET_GenericReturnValue
TMH_taler_uri_by_connection (struct MHD_Connection *connection,
                             const char *method,
                             const char *instance,
                             struct GNUNET_Buffer *buf);


/**
 * Create a taler://pay/ URI for the given @a con and @a order_id
 * and @a session_id and @a instance_id.
 *
 * @param con HTTP connection
 * @param order_id the order id
 * @param session_id session, may be NULL
 * @param instance_id instance, may be "default"
 * @param claim_token claim token for the order, may be NULL
 * @return corresponding taler://pay/ URI, or NULL on missing "host"
 */
char *
TMH_make_taler_pay_uri (struct MHD_Connection *con,
                        const char *order_id,
                        const char *session_id,
                        const char *instance_id,
                        struct TALER_ClaimTokenP *claim_token);

/**
 * Create a http(s) URL for the given @a con and @a order_id
 * and @a instance_id to display the /orders/{order_id} page.
 *
 * @param con HTTP connection
 * @param order_id the order id
 * @param session_id session, may be NULL
 * @param instance_id instance, may be "default"
 * @param claim_token claim token for the order, may be NULL
 * @param h_contract contract hash for authentication, may be NULL
 * @return corresponding http(s):// URL, or NULL on missing "host"
 */
char *
TMH_make_order_status_url (struct MHD_Connection *con,
                           const char *order_id,
                           const char *session_id,
                           const char *instance_id,
                           struct TALER_ClaimTokenP *claim_token,
                           struct TALER_PrivateContractHashP *h_contract);


/**
 * Put data from an exchange's HTTP response into
 * a JSON reply
 *
 * @param hr a `TALER_EXCHANGE_HttpResponse`
 */
#define TMH_pack_exchange_reply(hr) \
        GNUNET_JSON_pack_uint64 ("exchange_code", (hr)->ec),                \
        GNUNET_JSON_pack_uint64 ("exchange_http_status", (hr)->http_status), \
        GNUNET_JSON_pack_uint64 ("exchange_ec", (hr)->ec), /* LEGACY */  \
        GNUNET_JSON_pack_uint64 ("exchange_hc", (hr)->http_status), /* LEGACY */ \
        GNUNET_JSON_pack_allow_null ( \
          GNUNET_JSON_pack_object_incref ("exchange_reply", (json_t *) (hr)-> \
                                          reply))


/**
 * TMH_trigger_webhook is a function that need to be use when someone
 * pay. Merchant need to have a notification.
 *
 * @param instance that we need to send the webhook as a notification
 * @param event of the webhook
 * @param args argument of the function
 */
enum GNUNET_DB_QueryStatus
TMH_trigger_webhook (const char *instance,
                     const char *action,
                     const json_t *args);


/**
 * Return JSON array with all of the exchange accounts
 * that support the given @a wire_method.
 *
 * @param master_pub master public key to match exchange by
 * @param wire_method NULL for any
 * @return JSON array with information about all matching accounts
 */
json_t *
TMH_exchange_accounts_by_method (
  const struct TALER_MasterPublicKeyP *master_pub,
  const char *wire_method);

/**
 * Check validity of login @a token for the given @a instance_id.
 *
 * @param token the login token given in the request
 * @param instance_id the instance the login is to be checked against
 * @param[out] as set to scope of the token if it is valid
 * @return TALER_EC_NONE on success
 */
enum TALER_ErrorCode
TMH_check_token (const char *token,
                 const char *instance_id,
                 enum TMH_AuthScope *as);

#endif
