/*
** 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 "vps_monitor.h"

#include "dbconfig.h"
#include "zbxmutexs.h"

static zbx_mutex_t	vps_lock = ZBX_MUTEX_NULL;

#define ZBX_VPS_FLUSH_PERIOD	10

/******************************************************************************
 *                                                                            *
 * Purpose: create VPS monitor                                                *
 *                                                                            *
 ******************************************************************************/
int	vps_monitor_create(zbx_vps_monitor_t *monitor, char **error)
{
	if (SUCCEED != zbx_mutex_create(&vps_lock, ZBX_MUTEX_VPS_MONITOR, error))
		return FAIL;

	memset(monitor, 0, sizeof(zbx_vps_monitor_t));

	return SUCCEED;
}

/******************************************************************************
 *                                                                            *
 * Purpose: destroy VPS monitor                                               *
 *                                                                            *
 ******************************************************************************/
void	vps_monitor_destroy(void)
{
	zbx_mutex_destroy(&vps_lock);
}

/******************************************************************************
 *                                                                            *
 * Purpose: initialize VPS monitor                                            *
 *                                                                            *
 * Comments: This function is called before processes are spawned -           *
 *           no locking is needed.                                            *
 *                                                                            *
 ******************************************************************************/
void	zbx_vps_monitor_init(zbx_uint64_t vps_limit, zbx_uint64_t overcommit_limit)
{
	zbx_vps_monitor_t	*monitor = &(get_dc_config())->vps_monitor;

	monitor->last_flush = time(NULL);

	monitor->values_limit = vps_limit * ZBX_VPS_FLUSH_PERIOD;
	monitor->overcommit_limit = overcommit_limit;
	monitor->overcommit = 0;
}

/******************************************************************************
 *                                                                            *
 * Purpose: add number of collected values to the monitor                     *
 *                                                                            *
 ******************************************************************************/
void	zbx_vps_monitor_add_collected(zbx_uint64_t values_num)
{
	zbx_vps_monitor_t	*monitor = &(get_dc_config())->vps_monitor;
	time_t			now;

	if (0 == monitor->values_limit)
		return;

	now = time(NULL);

	zbx_mutex_lock(vps_lock);

	if (ZBX_VPS_FLUSH_PERIOD <= now - monitor->last_flush)
	{
		if (monitor->values_limit > monitor->values_num)
		{
			if (monitor->overcommit > monitor->values_limit - monitor->values_num)
				monitor->overcommit -= monitor->values_limit - monitor->values_num;
			else
				monitor->overcommit = 0;

			monitor->values_num = 0;
		}
		else
		{
			zbx_uint64_t	overcommit_available = monitor->overcommit_limit - monitor->overcommit;

			if (monitor->values_num <= monitor->values_limit + overcommit_available)
			{
				monitor->overcommit += monitor->values_num - monitor->values_limit;
				monitor->values_num = 0;
			}
			else
			{
				monitor->values_num -= monitor->values_limit + overcommit_available;
				monitor->overcommit = monitor->overcommit_limit;
			}
		}

		monitor->last_flush = now;
	}

	monitor->values_num += values_num;

	zbx_mutex_unlock(vps_lock);
}

/******************************************************************************
 *                                                                            *
 * Purpose: add number of written values to the monitor                       *
 *                                                                            *
 * Comments: This function is called before processes are spawned -           *
 *           no locking is needed.                                            *
 *                                                                            *
 ******************************************************************************/
void	zbx_vps_monitor_add_written(zbx_uint64_t values_num)
{
	zbx_vps_monitor_t	*monitor = &(get_dc_config())->vps_monitor;

	zbx_mutex_lock(vps_lock);
	monitor->total_values_num += values_num;
	zbx_mutex_unlock(vps_lock);
}

/******************************************************************************
 *                                                                            *
 * Purpose: check if the vps limit is reached for current time period         *
 *                                                                            *
 * Return value: SUCCEED - limit is reached                                   *
 *               FAIL    - otherwise                                          *
 *                                                                            *
 ******************************************************************************/
int	zbx_vps_monitor_capped(void)
{
	zbx_vps_monitor_t	*monitor = &(get_dc_config())->vps_monitor;

	if (0 == monitor->values_limit || monitor->values_num < monitor->values_limit)
		return FAIL;

	int	ret;

	zbx_mutex_lock(vps_lock);

	zbx_uint64_t	overcommit_available = monitor->overcommit_limit - monitor->overcommit;
	ret = (monitor->values_num <= monitor->values_limit + overcommit_available ? FAIL : SUCCEED);

	zbx_mutex_unlock(vps_lock);

	return ret;
}

/******************************************************************************
 *                                                                            *
 * Purpose: get available overcommit charge                                   *
 *                                                                            *
 ******************************************************************************/
void	zbx_vps_monitor_get_stats(zbx_vps_monitor_stats_t *stats)
{
	zbx_vps_monitor_t	*monitor = &(get_dc_config())->vps_monitor;

	zbx_mutex_lock(vps_lock);
	stats->overcommit = monitor->overcommit;
	stats->written_num = monitor->total_values_num;
	zbx_mutex_unlock(vps_lock);

	/* preconfigured values, cannot change without restarting server */
	stats->values_limit = monitor->values_limit / ZBX_VPS_FLUSH_PERIOD;
	stats->overcommit_limit = monitor->overcommit_limit;
}

/******************************************************************************
 *                                                                            *
 * Purpose: return data collection status string to append to process title   *
 *                                                                            *
 ******************************************************************************/
const char	*zbx_vps_monitor_status(void)
{
	if (SUCCEED == zbx_vps_monitor_capped())
		return ", data collection paused";
	else
		return "";
}