/*
** Copyright (C) 2001-2025 Zabbix SIA
**
** This program 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, version 3.
**
** This program 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 this program.
** If not, see <https://www.gnu.org/licenses/>.
**/

#include "embed_xml.h"

#include "zbxvariant.h"
#include "embed.h"
#include "zbxxml.h"

/******************************************************************************
 *                                                                            *
 * Purpose: XML constructor                                                   *
 *                                                                            *
 ******************************************************************************/
static duk_ret_t	es_xml_ctor(duk_context *ctx)
{
	if (!duk_is_constructor_call(ctx))
		return DUK_RET_TYPE_ERROR;

	duk_push_this(ctx);

	duk_set_finalizer(ctx, -1);

	return 0;
}

/******************************************************************************
 *                                                                            *
 * Purpose: XML.query method                                                  *
 *                                                                            *
 ******************************************************************************/
static duk_ret_t	es_xml_query(duk_context *ctx)
{
	int		err_index = -1;
	char		*err = NULL;
	zbx_variant_t	value;
	zbx_es_env_t	*env;

	if (NULL == (env = zbx_es_get_env(ctx)))
		return duk_error(ctx, DUK_RET_TYPE_ERROR, "cannot access internal environment");

	ZBX_ES_CHECK_TIMEOUT(ctx, env);

	zbx_variant_set_str(&value, zbx_strdup(NULL, duk_safe_to_string(ctx, 0)));

	if (FAIL == zbx_query_xpath(&value, duk_safe_to_string(ctx, 1), &err))
	{
		err_index = duk_push_error_object(ctx, DUK_RET_EVAL_ERROR, err);
		goto out;
	}
	es_push_result_string(ctx, value.data.str, strlen(value.data.str));
out:
	zbx_variant_clear(&value);
	zbx_free(err);

	if (-1 != err_index)
		return duk_throw(ctx);

	return 1;
}

/******************************************************************************
 *                                                                            *
 * Purpose: XML.fromJson method                                               *
 *                                                                            *
 ******************************************************************************/
static duk_ret_t	es_xml_from_json(duk_context *ctx)
{
	int		err_index = -1;
	char		*str = NULL, *error = NULL;
	zbx_es_env_t	*env;

	if (NULL == (env = zbx_es_get_env(ctx)))
		return duk_error(ctx, DUK_RET_TYPE_ERROR, "cannot access internal environment");

	ZBX_ES_CHECK_TIMEOUT(ctx, env);

	if (FAIL == zbx_json_to_xml((char *)duk_safe_to_string(ctx, 0), &str, &error))
	{
		err_index = duk_push_error_object(ctx, DUK_RET_EVAL_ERROR, error);
		goto out;
	}
	es_push_result_string(ctx, str, strlen(str));
out:
	zbx_free(str);
	zbx_free(error);

	if (-1 != err_index)
		return duk_throw(ctx);

	return 1;
}

/******************************************************************************
 *                                                                            *
 * Purpose: XML.toJson method                                                 *
 *                                                                            *
 ******************************************************************************/
static duk_ret_t	es_xml_to_json(duk_context *ctx)
{
	int		err_index = -1;
	char		*str = NULL, *error = NULL;
	zbx_es_env_t	*env;

	if (NULL == (env = zbx_es_get_env(ctx)))
		return duk_error(ctx, DUK_RET_TYPE_ERROR, "cannot access internal environment");

	ZBX_ES_CHECK_TIMEOUT(ctx, env);

	if (FAIL == zbx_xml_to_json((char *)duk_safe_to_string(ctx, 0), &str, &error))
	{
		err_index = duk_push_error_object(ctx, DUK_RET_EVAL_ERROR, error);
		goto out;
	}
	es_push_result_string(ctx, str, strlen(str));
out:
	zbx_free(str);
	zbx_free(error);

	if (-1 != err_index)
		return duk_throw(ctx);

	return 1;
}

static const duk_function_list_entry	xml_methods[] = {
	{"query", es_xml_query, 2},
	{"fromJson", es_xml_from_json, 1},
	{"toJson", es_xml_to_json, 1},
	{NULL, NULL, 0}
};

static int	es_xml_create_object(duk_context *ctx)
{
	duk_push_c_function(ctx, es_xml_ctor, 0);
	duk_push_object(ctx);

	duk_put_function_list(ctx, -1, xml_methods);

	if (1 != duk_put_prop_string(ctx, -2, "prototype"))
		return FAIL;

	duk_new(ctx, 0);

	if (1 != duk_put_global_string(ctx, "XML"))
		return FAIL;

	return SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Purpose: init XML object                                                   *
 *                                                                            *
 ******************************************************************************/
int	zbx_es_init_xml(zbx_es_t *es, char **error)
{
	if (0 != setjmp(es->env->loc))
	{
		*error = zbx_strdup(*error, es->env->error);
		return FAIL;
	}

	if (FAIL == es_xml_create_object(es->env->ctx))
	{
		*error = zbx_strdup(*error, duk_safe_to_string(es->env->ctx, -1));
		duk_pop(es->env->ctx);
		return FAIL;
	}

	return SUCCEED;
}