/*
 * Copyright 2023 Vsevolod Stakhov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @file scan_result.h
 * Scan result holder
 */

#ifndef RSPAMD_SCAN_RESULT_H
#define RSPAMD_SCAN_RESULT_H

#include "config.h"
#include "rspamd_symcache.h"
#include "task.h"

#ifdef __cplusplus
extern "C" {
#endif

struct rspamd_task;
struct rspamd_settings;
struct rspamd_classifier_config;

struct rspamd_symbol_option {
	gchar *option;
	gsize optlen;
	struct rspamd_symbol_option *prev, *next;
};

enum rspamd_symbol_result_flags {
	RSPAMD_SYMBOL_RESULT_NORMAL = 0,
	RSPAMD_SYMBOL_RESULT_IGNORED = (1 << 0)
};

struct kh_rspamd_options_hash_s;

/**
 * Rspamd symbol
 */
struct rspamd_symbol_result {
	double score;                             /**< symbol's score							*/
	struct kh_rspamd_options_hash_s *options; /**< list of symbol's options				*/
	struct rspamd_symbol_option *opts_head;   /**< head of linked list of options			*/
	const gchar *name;
	struct rspamd_symbol *sym; /**< symbol configuration					*/
	gssize opts_len;           /**< total size of all options (negative if truncated option is added) */
	guint nshots;
	int flags;
	struct rspamd_symbol_result *next; /**< for shadow results */
};


#define RSPAMD_PASSTHROUGH_NORMAL 1
#define RSPAMD_PASSTHROUGH_LOW 0
#define RSPAMD_PASSTHROUGH_HIGH 2
#define RSPAMD_PASSTHROUGH_CRITICAL 3

#define RSPAMD_PASSTHROUGH_LEAST (1u << 0u)
#define RSPAMD_PASSTHROUGH_NO_SMTP_MESSAGE (1u << 1u)
#define RSPAMD_PASSTHROUGH_PROCESS_ALL (1u << 2u)

struct rspamd_passthrough_result {
	struct rspamd_action *action;
	guint priority;
	guint flags;
	double target_score;
	const gchar *message;
	const gchar *module;
	struct rspamd_passthrough_result *prev, *next;
};


enum rspamd_action_config_flags {
	RSPAMD_ACTION_RESULT_DEFAULT = 0,
	RSPAMD_ACTION_RESULT_NO_THRESHOLD = (1u << 0u),
	RSPAMD_ACTION_RESULT_DISABLED = (1u << 1u),
};
struct rspamd_action_config {
	gdouble cur_limit;
	int flags;
	struct rspamd_action *action;
};

struct kh_rspamd_symbols_hash_s;
struct kh_rspamd_symbols_group_hash_s;


struct rspamd_scan_result {
	double score;       /**< total score							*/
	double grow_factor; /**< current grow factor					*/
	struct rspamd_passthrough_result *passthrough_result;
	double positive_score;
	double negative_score;
	struct kh_rspamd_symbols_hash_s *symbols;          /**< symbols of metric						*/
	struct kh_rspamd_symbols_group_hash_s *sym_groups; /**< groups of symbols						*/
	struct rspamd_action_config *actions_config;
	const gchar *name;        /**< for named results, NULL is the default result */
	struct rspamd_task *task; /**< back reference */
	gint symbol_cbref;        /**< lua function that defines if a symbol can be inserted, -1 if unused */
	guint nactions;
	guint npositive;
	guint nnegative;
	guint nresults;                         /**< all results: positive, negative, passthrough etc */
	guint nresults_postfilters;             /**< how many results are there before postfilters stage */
	struct rspamd_scan_result *prev, *next; /**< double linked list of results */
};

/**
 * Create or return existing result for the specified metric name
 * @param task task object
 * @return metric result or NULL if metric `name` has not been found
 */
struct rspamd_scan_result *rspamd_create_metric_result(struct rspamd_task *task,
													   const gchar *name, gint lua_sym_cbref);

/**
 * Find result with a specific name (NULL means the default result)
 * @param task
 * @param name
 * @return
 */
struct rspamd_scan_result *rspamd_find_metric_result(struct rspamd_task *task,
													 const gchar *name);

/**
 * Adds a new passthrough result to a task
 * @param task
 * @param action
 * @param priority
 * @param target_score
 * @param message
 * @param module
 */
bool rspamd_add_passthrough_result(struct rspamd_task *task,
								   struct rspamd_action *action, guint priority,
								   double target_score, const gchar *message,
								   const gchar *module, guint flags,
								   struct rspamd_scan_result *scan_result);

enum rspamd_symbol_insert_flags {
	RSPAMD_SYMBOL_INSERT_DEFAULT = 0,
	RSPAMD_SYMBOL_INSERT_SINGLE = (1 << 0),
	RSPAMD_SYMBOL_INSERT_ENFORCE = (1 << 1),
};

/**
 * Insert a result to task
 * @param task worker's task that present message from user
 * @param metric_name metric's name to which we need to insert result
 * @param symbol symbol to insert
 * @param weight numeric weight for symbol
 * @param opts list of symbol's options
 */
struct rspamd_symbol_result *rspamd_task_insert_result_full(struct rspamd_task *task,
															const gchar *symbol,
															double weight,
															const gchar *opts,
															enum rspamd_symbol_insert_flags flags,
															struct rspamd_scan_result *result);

#define rspamd_task_insert_result_single(task, symbol, weight, opts) \
	rspamd_task_insert_result_full((task), (symbol), (weight), (opts), RSPAMD_SYMBOL_INSERT_SINGLE, NULL)
#define rspamd_task_insert_result(task, symbol, weight, opts) \
	rspamd_task_insert_result_full((task), (symbol), (weight), (opts), RSPAMD_SYMBOL_INSERT_DEFAULT, NULL)

/**
 * Removes a symbol from a specific symbol result
 * @param task
 * @param symbol
 * @param result
 * @return
 */
struct rspamd_symbol_result *rspamd_task_remove_symbol_result(
	struct rspamd_task *task,
	const gchar *symbol,
	struct rspamd_scan_result *result);
/**
 * Adds new option to symbol
 * @param task
 * @param s
 * @param opt
 */
gboolean rspamd_task_add_result_option(struct rspamd_task *task,
									   struct rspamd_symbol_result *s,
									   const gchar *opt,
									   gsize vlen);

/**
 * Finds symbol result
 * @param task
 * @param sym
 * @return
 */
struct rspamd_symbol_result *
rspamd_task_find_symbol_result(struct rspamd_task *task, const char *sym,
							   struct rspamd_scan_result *result);

/**
 * Compatibility function to iterate on symbols hash
 * @param task
 * @param func
 * @param ud
 */
void rspamd_task_symbol_result_foreach(struct rspamd_task *task,
									   struct rspamd_scan_result *result,
									   GHFunc func,
									   gpointer ud);

/**
 * Default consolidation function for metric, it get all symbols and multiply symbol
 * weight by some factor that is specified in config. Default factor is 1.
 * @param task worker's task that present message from user
 * @param metric_name name of metric
 * @return result metric weight
 */
double rspamd_factor_consolidation_func(struct rspamd_task *task,
										const gchar *metric_name,
										const gchar *unused);


/**
 * Check thresholds and return action for a task
 * @param task
 * @return
 */
struct rspamd_action *rspamd_check_action_metric(struct rspamd_task *task,
												 struct rspamd_passthrough_result **ppr,
												 struct rspamd_scan_result *scan_result);

struct rspamd_action_config *rspamd_find_action_config_for_action(struct rspamd_scan_result *scan_result,
																  struct rspamd_action *act);

#ifdef __cplusplus
}
#endif

#endif
