#include <grp.h> 
#include "daemon.h"
#include "fatal.h"
#include "pid.h"
#include "cfg.h"
#include "logger.h"

static int	parent_pid = -1;

#define CHECKED_FIELD(siginfo, field)			(NULL == siginfo ? -1 : siginfo->field)
#define CHECKED_FIELD_TYPE(siginfo, field, type)	(NULL == siginfo ? (type)-1 : siginfo->field)
#define PARENT_PROCESS					(parent_pid == (int)getpid())
#define MAX_STACK_FRAMES 64

extern pid_t *threads;
extern int exiting;

static void	signal_handler(int sig, siginfo_t *siginfo, void *context){
	if (NULL == siginfo)
	{
		ja_log("JASERVER400005",error_message,ERROR_MSG_SIZE,sig,get_signal_name(sig));
	}

	if (NULL == context)
	{
		ja_log("JASERVER400005",error_message,ERROR_MSG_SIZE,sig,get_signal_name(sig));
	}

	switch (sig)
	{
		case SIGTERM:

			ja_log(parent_pid == CHECKED_FIELD(siginfo, si_pid) ? "JASERVER400006" : "JASERVER000011",error_message , ERROR_MSG_SIZE ,
				sig,
				get_signal_name(sig),
				CHECKED_FIELD(siginfo, si_pid),
				CHECKED_FIELD(siginfo, si_uid),
				CHECKED_FIELD(siginfo, si_code));
			
			ja_log("JASERVER000010", error_message, ERROR_MSG_SIZE, threads[0]);
			exiting = 1;
			jaz_on_exit();

			break;
		case SIGSEGV:	
		case SIGILL:
		case SIGFPE:
		case SIGBUS:
			ja_log("JASERVER100002",error_message, ERROR_MSG_SIZE,
				sig, get_signal_name(sig),
				CHECKED_FIELD(siginfo, si_pid),
				CHECKED_FIELD(siginfo, si_uid),
				CHECKED_FIELD(siginfo, si_code));
			print_fatal_info(sig, siginfo, context);
			exiting = 1;
			jaz_on_exit();
			break;

		default:
			ja_log("JASERVER300004", error_message , ERROR_MSG_SIZE,
					sig, get_signal_name(sig),
					CHECKED_FIELD(siginfo, si_pid),
					CHECKED_FIELD(siginfo, si_uid));
	}
}

int	daemon_start(int allow_root)
{
	pid_t			pid;
	struct passwd		*pwd;
	struct sigaction	phan;
	char			user[1024] = "zabbix";

	if (NULL != CONFIG_JA_EXECUTION_USER)
	{
		strncpy(user, CONFIG_JA_EXECUTION_USER, sizeof(user) - 1);
		user[sizeof(user) - 1] = '\0'; // ensure null-termination
	}

	if (0 == allow_root && (0 == getuid() || 0 == getgid()))	/* running as root? */
	{
		pwd = getpwnam(user);

		if (NULL == pwd)
		{
			ja_log("JADAEMON200001", error_message, ERROR_MSG_SIZE, user);
			ja_log("JADAEMON200002" , error_message , ERROR_MSG_SIZE);
			exit(FAIL);
		}
		char folderpath[FILE_PATH_LEN];
    
		const char *lastSlash = strrchr(config.log_file, '/');

		if (lastSlash != NULL){
			size_t directoryLength = lastSlash - config.log_file;
			strncpy(folderpath, config.log_file, directoryLength);
			folderpath[directoryLength] = '\0';
		}
		if(mkdir(folderpath, PERMISSION)){
			if (errno != EEXIST) {
				ja_log("JADAEMON200003",error_message,ERROR_MSG_SIZE,folderpath,errno);
				exit(FAIL);
			}
		}
		// 👇 Change folder ownership BEFORE dropping privileges
        if (chown(folderpath, pwd->pw_uid, pwd->pw_gid) != 0) {
            perror("chown failed");
            exit(FAIL);
        }

		if (-1 == setgid(pwd->pw_gid))
		{
			ja_log("JADAEMON200004",error_message,ERROR_MSG_SIZE, user,errno);
			exit(FAIL);
		}

#ifdef HAVE_FUNCTION_INITGROUPS
		if (-1 == initgroups(user, pwd->pw_gid))
		{
			ja_log("JADAEMON200005",error_message,ERROR_MSG_SIZE, user,errno);
			exit(FAIL);
		}
#endif

		if (-1 == setuid(pwd->pw_uid))
		{
			ja_log("JADAEMON200006",error_message,ERROR_MSG_SIZE, user,errno);
			exit(FAIL);
		}

#ifdef HAVE_FUNCTION_SETEUID
		if (-1 == setegid(pwd->pw_gid) || -1 == seteuid(pwd->pw_uid))
		{
			ja_log("JADAEMON200007",error_message,ERROR_MSG_SIZE, user,errno);
			exit(FAIL);
		}
#endif
	}

	init_logging();
	umask(0002);

	if (0 == JAZ_TASK_FLAG_FOREGROUND)
	{
		if (0 != (pid = jaz_fork()))
			exit(0);

		setsid();

		signal(SIGHUP, SIG_IGN);

		if (0 != (pid = jaz_fork()))
			exit(0);

		if (-1 == chdir("/"))	/* this is to eliminate warning: ignoring return value of chdir */
			assert(0);

		//LOGGING and assign CONFIG_PID_FILE
		//if background and log type system, redirect std to syslog.
		// if ( NULL != CONFIG_LOG_FILE && '\0' != *CONFIG_LOG_FILE && strcmp(CONFIG_LOG_TYPE_STR, ZBX_OPTION_LOGTYPE_FILE) == 0){
		// 	redirect_std(CONFIG_LOG_FILE);
    	// }
	
	}

	if (FAIL == create_pid_file(config.pid_file))
		exit(FAIL);

	atexit(daemon_stop);

	parent_pid = (int)getpid();

	phan.sa_sigaction = signal_handler;
	phan.sa_flags = SA_SIGINFO;
	sigemptyset(&phan.sa_mask);

	sigaction(SIGTERM, &phan, NULL);
	sigaction(SIGSEGV, &phan, NULL);
	sigaction(SIGBUS, &phan, NULL);
	sigaction(SIGILL, &phan, NULL);
	sigaction(SIGFPE, &phan, NULL);

	return JAZ_MAIN_ENTRY();
}

void	daemon_stop()
{
	/* this function is registered using atexit() to be called when we terminate */
	/* there should be nothing like logging or calls to exit() beyond this point */

	if (parent_pid != (int)getpid())
		return;

	drop_pid_file(config.pid_file);

	memset(&config, 0, sizeof(config));
}