/*
 * 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.
 */
#include "lua_common.h"
#include "expression.h"
#include "src/libserver/composites/composites.h"

/*
 * This is implementation of lua routines to handle config file params
 */

/* Process a single item in 'metrics' table */
static void
lua_process_metric(lua_State *L, const gchar *name, struct rspamd_config *cfg)
{
	gchar *symbol;
	const gchar *desc = NULL;
	gdouble *score;
	struct rspamd_symbol *s;

	/* Now iterate through module table */
	for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
		/* key - -2, value - -1 */
		symbol = rspamd_mempool_strdup(cfg->cfg_pool, luaL_checkstring(L, -2));
		if (symbol != NULL) {
			if (lua_istable(L, -1)) {
				/* We got a table, so extract individual attributes */
				lua_pushstring(L, "weight");
				lua_gettable(L, -2);
				if (lua_isnumber(L, -1)) {
					score = rspamd_mempool_alloc(cfg->cfg_pool, sizeof(double));
					*score = lua_tonumber(L, -1);
				}
				else {
					msg_warn_config("cannot get weight of symbol: %s", symbol);
					continue;
				}
				lua_pop(L, 1);
				lua_pushstring(L, "description");
				lua_gettable(L, -2);
				if (lua_isstring(L, -1)) {
					desc = lua_tostring(L, -1);
				}
				lua_pop(L, 1);
			}
			else if (lua_isnumber(L, -1)) {
				/* Just got weight */
				score = rspamd_mempool_alloc(cfg->cfg_pool, sizeof(double));
				*score = lua_tonumber(L, -1);
			}
			else {
				msg_warn_config("cannot get weight of symbol: %s", symbol);
				continue;
			}
			/* Insert symbol */
			if ((s =
					 g_hash_table_lookup(cfg->symbols, symbol)) != NULL) {
				msg_info_config("replacing weight for symbol %s: %.2f -> %.2f",
								symbol,
								*s->weight_ptr,
								*score);
				s->weight_ptr = score;
			}
			else {
				s = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(*s));
				s->name = symbol;
				s->weight_ptr = score;
				g_hash_table_insert(cfg->symbols, symbol, s);
			}

			if (desc) {
				s->description = rspamd_mempool_strdup(cfg->cfg_pool, desc);
			}
		}
	}
}

/* Do post load initialization based on lua */
void rspamd_lua_post_load_config(struct rspamd_config *cfg)
{
	lua_State *L = cfg->lua_state;
	const gchar *name;
	ucl_object_t *obj;
	gsize keylen, i;

	/* First check all module options that may be overridden in 'config' global */
	lua_getglobal(L, "config");

	if (lua_istable(L, -1)) {
		/* Iterate to get all keys */
		GPtrArray *names = g_ptr_array_new_full(rspamd_lua_table_size(L, -1),
												g_free);

		for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 2)) {
			gchar *tmp;
			lua_pushvalue(L, -2);
			name = luaL_checklstring(L, -1, &keylen);

			if (name && lua_istable(L, -2)) {
				tmp = g_malloc(keylen + 1);
				rspamd_strlcpy(tmp, name, keylen + 1);
				g_ptr_array_add(names, tmp);
			}
		}

		PTR_ARRAY_FOREACH(names, i, name)
		{
			lua_getfield(L, -1, name);

			if (lua_istable(L, -1)) {
				obj = ucl_object_lua_import(L, lua_gettop(L));

				if (obj != NULL) {
					ucl_object_sort_keys(obj, UCL_SORT_KEYS_DEFAULT);
					ucl_object_insert_key_merged(cfg->cfg_ucl_obj,
												 obj,
												 name,
												 strlen(name),
												 true);
				}
			}
		}

		g_ptr_array_free(names, TRUE);
	}

	/* Check metrics settings */
	lua_getglobal(L, "metrics");

	if (lua_istable(L, -1)) {
		/* Iterate */
		for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
			/* 'key' is at index -2 and 'value' is at index -1 */
			/* Key must be a string and value must be a table */
			name = luaL_checkstring(L, -2);
			if (name != NULL && lua_istable(L, -1)) {
				lua_process_metric(L, name, cfg);
			}
		}
	}

	lua_settop(L, 0);

	rspamd_lua_start_gc(cfg);
}
