/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * debug.c - for debug and logging.
 *
 * Copyright (C) 2025 Genexis AB.
 *
 * Author: anjan.chanda@iopsys.eu
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdarg.h>
#include <string.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>

#include <easy/easy.h>

#include "wifimngr_opts.h"
#include "debug.h"

extern const char *PROG_NAME;

static int loglevel;
static uint32_t features;
static const char *logfile;
static FILE *outfile;
static int ffd = -1;
static int ofd = -1;
static bool syslogging;
static bool logfile_isfifo;
static const int syslog_level[] = { LOG_ERR, LOG_WARNING, LOG_INFO, LOG_DEBUG };

uint32_t logfeature_to_enum(const char *s)
{
	if (!strncmp(s, "cmd", 3))
		return BIT(LOG_CMD);

	if (!strncmp(s, "event", 5))
		return BIT(LOG_EVENT);

	if (!strncmp(s, "default", 7))
		return BIT(LOG_DEFAULT);

	if (!strncmp(s, "all", 3))
		return LOG_FEATURE_ALL;

	return 0;
}

const char *logfeature_to_string(uint32_t e)
{
	switch (e) {
	case LOG_CMD:     return "cmd";
	case LOG_EVENT:   return "event";
	case LOG_DEFAULT: return "default";
	}

	return "";
}

void restart_logging(void *args)
{
	struct wifimngr_cmdline_opts *opts = args;

	syslogging = opts->syslogging;
	logfile = opts->logfile;
	logfile_isfifo = opts->logfile_isfifo;
	loglevel = opts->loglevel;
	features = opts->features;

	if (syslogging)
		openlog(PROG_NAME, 0, LOG_DAEMON);

	if (!logfile) {
		outfile = stderr;
		ofd = fileno(stderr);
		return;
	}

	if (logfile_isfifo) {
		struct stat st;
		int rfd;

		if (stat(logfile, &st))
			unlink(logfile);

		mkfifo(logfile, 0600);
		if (stat(logfile, &st) == -1 || !S_ISFIFO(st.st_mode))
			return;

		rfd = open(logfile, O_RDONLY | O_NONBLOCK);
		if (rfd) {
			ffd = open(logfile, O_WRONLY | O_NONBLOCK);
			close(rfd);
		}
	} else {
		ofd = open(logfile, O_CREAT | O_WRONLY | O_APPEND | O_NONBLOCK);
	}
}

void stop_logging(void)
{
	if (syslogging)
		closelog();

	if (outfile)
		fclose(outfile);

	if (ofd > 0) {
		close(ofd);
		ofd = -1;
	}

	if (ffd > 0) {
		close(ffd);
		unlink(logfile);
	}
}

static void log_timestamp(int fd)
{
	time_t now = time(NULL);
	struct tm *tm_now = localtime(&now);
	const char *tm_fmt = "[%d-%02d-%02d %02d:%02d:%02d] ";

	dprintf(fd, tm_fmt,
		tm_now->tm_year + 1900,
		tm_now->tm_mon + 1,
		tm_now->tm_mday,
		tm_now->tm_hour,
		tm_now->tm_min,
		tm_now->tm_sec);
}

void log_message(int feature, int level, const char *fmt, ...)
{
	va_list args;
	int fd = -1;

	if (!(BIT(feature) & features))
		return;

	if (level > loglevel)
		return;

	va_start(args, fmt);
	if (syslogging && level >= 0)
		vsyslog(syslog_level[level > 3 ? 3 : level], fmt, args);

	if (logfile_isfifo && ffd > 0)
		fd = ffd;
	else if (ofd >= 0)
		fd = ofd;

	if (fd != -1) {
		log_timestamp(fd);
		dprintf(fd, "[%d]: ", getpid());
		vdprintf(fd, fmt, args);
	}

	va_end(args);
}