/*
  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/post-aml-OFFICER_PUB-decision.h
 * @brief C interface for POST /aml/$OFFICER_PUB/decision
 * @author Christian Grothoff
 */
#ifndef _TALER_EXCHANGE__POST_AML_OFFICER_PUB_DECISION_H
#define _TALER_EXCHANGE__POST_AML_OFFICER_PUB_DECISION_H

#include <taler/exchange/common.h>


/**
 * Information about a (custom) measure.
 */
struct TALER_EXCHANGE_MeasureInformation
{
  /**
   * Name of the measure.
   */
  const char *measure_name;

  /**
   * Name of the check.
   */
  const char *check_name;

  /**
   * Name of the AML program, can be NULL if @e check_name
   * is for a check of type "INFO".
   */
  const char *prog_name;

  /**
   * Context for the check, can be NULL.
   */
  const json_t *context;

  /**
   * Operation type this measure relates to,
   * can be #TALER_KYCLOGIC_KYC_TRIGGER_NONE if unknown.
   */
  enum TALER_KYCLOGIC_KycTriggerEvent operation_type;

};


/**
 * Rule that applies for an account, specifies the
 * trigger and measures to apply.
 */
struct TALER_EXCHANGE_AccountRule
{

  /**
   * Timeframe over which the @e threshold is computed.
   */
  struct GNUNET_TIME_Relative timeframe;

  /**
   * The maximum amount transacted within the given @e timeframe for the
   * specified @e operation_type.
   */
  struct TALER_Amount threshold;

  /**
   * Array of names of measures to apply.
   * Names listed can be original measures or
   * custom measures from the AmlOutcome.
   */
  const char **measures;

  /**
   * Length of the @e measures array.
   */
  unsigned int num_measures;

  /**
   * If multiple rules apply to the same account
   * at the same time, the number with the highest
   * rule determines which set of measures will
   * be activated and thus become visible for the
   * user.
   */
  uint32_t display_priority;

  /**
   * Operation type for which the restriction applies.
   */
  enum TALER_KYCLOGIC_KycTriggerEvent operation_type;

  /**
   * True if crossing this limit is categorically not
   * allowed. The @e measures array will be ignored
   * in this case.
   */
  bool verboten;

  /**
   * True if the rule (specifically, operation_type,
   * threshold, timeframe) and the general nature of
   * the measures (verboten or approval required)
   * should be exposed to the client.
   * Defaults to "false" if not set.
   */
  bool exposed;

  /**
   * Optional human-readable name for this rule.
   * May be NULL.
   */
  const char *rule_name;

  /**
   * True if all the measures will eventually need to
   * be satisfied, false if any of the measures should
   * do.  Primarily used by the SPA to indicate how
   * the measures apply when showing them to the user;
   * in the end, AML programs will decide after each
   * measure what to do next.
   * Default (if missing) is false.
   */
  bool is_and_combinator;

};


/**
 * Possible options we can set for the POST /aml/$OFFICER_PUB/decision request.
 */
enum TALER_EXCHANGE_PostAmlDecisionOption
{
  /**
   * End of list of options.
   */
  TALER_EXCHANGE_POST_AML_DECISION_OPTION_END = 0,

  /**
   * Full payto URI of the account the decision is about.
   * Needed only if the exchange does not yet know the account.
   */
  TALER_EXCHANGE_POST_AML_DECISION_OPTION_PAYTO_URI,

  /**
   * Space-separated list of measures to trigger immediately;
   * "+" prefixed for AND combination.
   */
  TALER_EXCHANGE_POST_AML_DECISION_OPTION_NEW_MEASURES,

  /**
   * JSON object with account properties to set.
   */
  TALER_EXCHANGE_POST_AML_DECISION_OPTION_PROPERTIES,

  /**
   * Array of event names to trigger.
   */
  TALER_EXCHANGE_POST_AML_DECISION_OPTION_EVENTS,

  /**
   * JSON object with KYC attributes to set for the account.
   */
  TALER_EXCHANGE_POST_AML_DECISION_OPTION_ATTRIBUTES,

  /**
   * Expiration time for the KYC attributes.
   * Must be set if attributes are set.
   */
  TALER_EXCHANGE_POST_AML_DECISION_OPTION_ATTRIBUTES_EXPIRATION

};


/**
 * Value for an option we can set for the POST /aml/$OFFICER_PUB/decision request.
 */
struct TALER_EXCHANGE_PostAmlDecisionOptionValue
{
  /**
   * Type of the option being set.
   */
  enum TALER_EXCHANGE_PostAmlDecisionOption option;

  /**
   * Specific option value.
   */
  union
  {
    /**
     * Value if @e option is
     * #TALER_EXCHANGE_POST_AML_DECISION_OPTION_PAYTO_URI.
     */
    struct TALER_FullPayto payto_uri;

    /**
     * Value if @e option is
     * #TALER_EXCHANGE_POST_AML_DECISION_OPTION_NEW_MEASURES.
     * May be NULL for none.
     */
    const char *new_measures;

    /**
     * Value if @e option is
     * #TALER_EXCHANGE_POST_AML_DECISION_OPTION_PROPERTIES.
     * JSON object with account properties.
     */
    const json_t *properties;

    /**
     * Value if @e option is
     * #TALER_EXCHANGE_POST_AML_DECISION_OPTION_EVENTS.
     */
    struct
    {
      /**
       * Length of the @e events array.
       */
      unsigned int num_events;

      /**
       * Array of event names to trigger.
       */
      const char **events;
    } events;

    /**
     * Value if @e option is
     * #TALER_EXCHANGE_POST_AML_DECISION_OPTION_ATTRIBUTES.
     * JSON object with KYC attributes.
     */
    const json_t *attributes;

    /**
     * Value if @e option is
     * #TALER_EXCHANGE_POST_AML_DECISION_OPTION_ATTRIBUTES_EXPIRATION.
     */
    struct GNUNET_TIME_Timestamp attributes_expiration;

  } details;

};


/**
 * Handle for an operation to POST /aml/$OFFICER_PUB/decision.
 */
struct TALER_EXCHANGE_PostAmlDecisionHandle;


/**
 * Set up POST /aml/$OFFICER_PUB/decision operation.
 * Note that you must explicitly start the operation after setup.
 *
 * @param ctx the context
 * @param url HTTP base URL for the exchange
 * @param h_payto payto URI hash of the account the decision is about
 * @param decision_time when was the decision made
 * @param successor_measure measure to activate after @a expiration_time if no rule applied
 * @param expiration_time when do the new rules expire
 * @param num_rules length of the @a rules array
 * @param rules new rules for the account
 * @param num_measures length of the @a measures array
 * @param measures possible custom measures
 * @param keep_investigating true to keep the investigation open
 * @param justification human-readable justification
 * @param officer_priv private key of the deciding AML officer
 * @return handle to operation, NULL on error
 */
struct TALER_EXCHANGE_PostAmlDecisionHandle *
TALER_EXCHANGE_post_aml_decision_create (
  struct GNUNET_CURL_Context *ctx,
  const char *url,
  const struct TALER_NormalizedPaytoHashP *h_payto,
  struct GNUNET_TIME_Timestamp decision_time,
  const char *successor_measure,
  struct GNUNET_TIME_Timestamp expiration_time,
  unsigned int num_rules,
  const struct TALER_EXCHANGE_AccountRule rules[static num_rules],
  unsigned int num_measures,
  const struct TALER_EXCHANGE_MeasureInformation measures[static num_measures],
  bool keep_investigating,
  const char *justification,
  const struct TALER_AmlOfficerPrivateKeyP *officer_priv);


/**
 * Terminate the list of options.
 *
 * @return the terminating object of struct TALER_EXCHANGE_PostAmlDecisionOptionValue
 */
#define TALER_EXCHANGE_post_aml_decision_option_end_()              \
        (const struct TALER_EXCHANGE_PostAmlDecisionOptionValue)    \
        {                                                           \
          .option = TALER_EXCHANGE_POST_AML_DECISION_OPTION_END    \
        }

/**
 * Set the payto URI of the account (needed if exchange does not yet know it).
 *
 * @param p the full payto URI
 * @return representation of the option as a struct TALER_EXCHANGE_PostAmlDecisionOptionValue
 */
#define TALER_EXCHANGE_post_aml_decision_option_payto_uri(p)              \
        (const struct TALER_EXCHANGE_PostAmlDecisionOptionValue)           \
        {                                                                  \
          .option = TALER_EXCHANGE_POST_AML_DECISION_OPTION_PAYTO_URI,    \
          .details.payto_uri = (p)                                         \
        }

/**
 * Set the new measures to trigger immediately.
 *
 * @param m space-separated list of measures, "+" prefixed for AND combination,
 *          or NULL for none
 * @return representation of the option as a struct TALER_EXCHANGE_PostAmlDecisionOptionValue
 */
#define TALER_EXCHANGE_post_aml_decision_option_new_measures(m)            \
        (const struct TALER_EXCHANGE_PostAmlDecisionOptionValue)            \
        {                                                                   \
          .option = TALER_EXCHANGE_POST_AML_DECISION_OPTION_NEW_MEASURES,  \
          .details.new_measures = (m)                                       \
        }

/**
 * Set the account properties.
 *
 * @param p JSON object with account properties
 * @return representation of the option as a struct TALER_EXCHANGE_PostAmlDecisionOptionValue
 */
#define TALER_EXCHANGE_post_aml_decision_option_properties(p)              \
        (const struct TALER_EXCHANGE_PostAmlDecisionOptionValue)            \
        {                                                                   \
          .option = TALER_EXCHANGE_POST_AML_DECISION_OPTION_PROPERTIES,    \
          .details.properties = (p)                                         \
        }

/**
 * Set the events to trigger.
 *
 * @param n number of events
 * @param e array of event name strings
 * @return representation of the option as a struct TALER_EXCHANGE_PostAmlDecisionOptionValue
 */
#define TALER_EXCHANGE_post_aml_decision_option_events(n,e)              \
        (const struct TALER_EXCHANGE_PostAmlDecisionOptionValue)          \
        {                                                                 \
          .option = TALER_EXCHANGE_POST_AML_DECISION_OPTION_EVENTS,      \
          .details.events.num_events = (n),                               \
          .details.events.events = (e)                                    \
        }


/**
 * Set the KYC attributes for the account.
 *
 * @param a JSON object with KYC attributes
 * @return representation of the option as a struct TALER_EXCHANGE_PostAmlDecisionOptionValue
 */
#define TALER_EXCHANGE_post_aml_decision_option_attributes(a)                \
        (const struct TALER_EXCHANGE_PostAmlDecisionOptionValue)              \
        {                                                                     \
          .option = TALER_EXCHANGE_POST_AML_DECISION_OPTION_ATTRIBUTES,      \
          .details.attributes = (a)                                           \
        }

/**
 * Set the expiration time for KYC attributes.
 *
 * @param t the expiration timestamp
 * @return representation of the option as a struct TALER_EXCHANGE_PostAmlDecisionOptionValue
 */
#define TALER_EXCHANGE_post_aml_decision_option_attributes_expiration(t)              \
        (const struct TALER_EXCHANGE_PostAmlDecisionOptionValue)                       \
        {                                                                              \
          .option = \
            TALER_EXCHANGE_POST_AML_DECISION_OPTION_ATTRIBUTES_EXPIRATION,    \
          .details.attributes_expiration = (t)                                         \
        }


/**
 * Set the requested options for the operation.
 *
 * If any option fails, other options may or may not be applied.
 *
 * @param padh 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_post_aml_decision_set_options_ (
  struct TALER_EXCHANGE_PostAmlDecisionHandle *padh,
  unsigned int num_options,
  const struct TALER_EXCHANGE_PostAmlDecisionOptionValue options[]);


/**
 * Set the requested options for the operation.
 *
 * If any option fails, other options may or may not be applied.
 *
 * @param padh the request to set the options for
 * @param ... the list of the options, each option must be created
 *            by helpers TALER_EXCHANGE_post_aml_decision_option_NAME(VALUE)
 * @return #GNUNET_OK on success,
 *         #GNUNET_NO on failure,
 *         #GNUNET_SYSERR on internal error
 */
#define TALER_EXCHANGE_post_aml_decision_set_options(padh,...)          \
        TALER_EXCHANGE_post_aml_decision_set_options_ (                 \
          padh,                                                         \
          TALER_EXCHANGE_COMMON_OPTIONS_ARRAY_MAX_SIZE,                 \
          ((const struct TALER_EXCHANGE_PostAmlDecisionOptionValue[])   \
           {__VA_ARGS__,                                                \
            TALER_EXCHANGE_post_aml_decision_option_end_ ()}            \
          ))


/**
 * Response when making an AML decision.
 */
struct TALER_EXCHANGE_PostAmlDecisionResponse
{
  /**
   * HTTP response data.
   */
  struct TALER_EXCHANGE_HttpResponse hr;
};


#ifndef TALER_EXCHANGE_POST_AML_DECISION_RESULT_CLOSURE
/**
 * Type of the closure used by
 * the #TALER_EXCHANGE_PostAmlDecisionCallback.
 */
#define TALER_EXCHANGE_POST_AML_DECISION_RESULT_CLOSURE void
#endif /* TALER_EXCHANGE_POST_AML_DECISION_RESULT_CLOSURE */

/**
 * Type of the function that receives the result of a
 * POST /aml/$OFFICER_PUB/decision request.
 *
 * @param cls closure
 * @param result result returned by the HTTP server
 */
typedef void
(*TALER_EXCHANGE_PostAmlDecisionCallback)(
  TALER_EXCHANGE_POST_AML_DECISION_RESULT_CLOSURE *cls,
  const struct TALER_EXCHANGE_PostAmlDecisionResponse *result);


/**
 * Start POST /aml/$OFFICER_PUB/decision operation.
 *
 * @param[in,out] padh 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_post_aml_decision_start (
  struct TALER_EXCHANGE_PostAmlDecisionHandle *padh,
  TALER_EXCHANGE_PostAmlDecisionCallback cb,
  TALER_EXCHANGE_POST_AML_DECISION_RESULT_CLOSURE *cb_cls);


/**
 * Cancel POST /aml/$OFFICER_PUB/decision operation.  This function must not
 * be called by clients after the TALER_EXCHANGE_PostAmlDecisionCallback has
 * been invoked (as in those cases it'll be called internally by the
 * implementation already).
 *
 * @param[in] padh operation to cancel
 */
void
TALER_EXCHANGE_post_aml_decision_cancel (
  struct TALER_EXCHANGE_PostAmlDecisionHandle *padh);


#endif /* _TALER_EXCHANGE__POST_AML_OFFICER_PUB_DECISION_H */
