/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */
/* Copyright (c) 2025 Brett A C Sheffield <bacs@librecast.net> */

/*
 * create fakehome
 * write config
 * start lcagent
 * lcagent send, verify command output
 * change config
 * lcagent reload
 * lcagent send, verify command output
 */

#include "test.h"
#include "testnet.h"
#include <agent.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>

#define SPEEDLIMIT "1048576"
#define CHANNEL "0000-0029"

static char ifname[IFNAMSIZ];
static unsigned int ifx;

static int test_config(int n)
{
	state_t state = {0};
	char testfile[] = "0000-0029-XXXXXX/success";
	char channel_name[] = CHANNEL;
	char *argv[] = { PACKAGE_NAME, "send", "-v", "--loopback", "-i", ifname, channel_name, NULL };
	char *home = getenv("HOME");
	int argc = sizeof argv / sizeof argv[0] - 1;
	int rc;

	test_log("\n%s\n", __func__);

	rc = snprintf(testfile, sizeof testfile, "%s/success", home);
	if (!test_assert(rc == sizeof testfile - 1, "build testfile string")) return test_status;

	rc = agent(&state, argc, argv);
	test_assert(rc == EXIT_SUCCESS, "%s() lcagent send returned %i", __func__, rc);

	/* wait for command to execute */
	if (RUNNING_ON_VALGRIND) sleep(1);
	else usleep(10000);

	/* check file was written */
	struct stat sb;
	rc = stat(testfile, &sb);
	test_assert(rc == n, "stat %s", testfile);
	return test_status;
}

static int test_write_config(int n)
{
	char configfile[] = "0000-0029-XXXXXX/.lcagentrc";
	char *cmd = (!n) ? "/bin/false" : "touch success";
	char *home = getenv("HOME");
	FILE *f;
	int rc;

	rc = snprintf(configfile, sizeof configfile, "%s/.lcagentrc", home);
	if (!test_assert(rc == sizeof configfile - 1, "build configfile string")) return test_status;

	f = fopen(configfile, "w+");
	if (!test_assert(f != NULL, "creating config %s", configfile)) return test_status;

	fprintf(f, "logfile lastlog.log\n");
	fprintf(f, "debug\n");
	fprintf(f, "channel '%s'\n", CHANNEL);
	fprintf(f, "    command '%s'\n", cmd);
	fclose(f);

	return test_status;
}

static int test_reload_server(void)
{
	state_t state = {0};
	char *argv[] = { PACKAGE_NAME, "reload", "-v", NULL };
	int argc = sizeof argv / sizeof argv[0] - 1;
	int rc = agent(&state, argc, argv);
	test_assert(rc == EXIT_SUCCESS, "lcagent reload returned %i", rc);
	return test_status;
}

static int test_stop_server(void)
{
	state_t state = {0};
	char *argv[] = { PACKAGE_NAME, "stop", "-v", NULL };
	int argc = sizeof argv / sizeof argv[0] - 1;
	int rc = agent(&state, argc, argv);
	test_assert(rc == EXIT_SUCCESS, "lcagent stop returned %i", rc);
	return test_status;
}

static int test_start_server(void)
{
	state_t state = {0};
	char *argv[] = { PACKAGE_NAME, "start", "-v", "-i", ifname, NULL };
	int argc = sizeof argv / sizeof argv[0] - 1;
	int rc = agent(&state, argc, argv);
	test_assert(rc == EXIT_SUCCESS, "lcagent start returned %i", rc);
	return test_status;
}

static int create_keys_and_tokens(void)
{
	state_t state = {0};
	int rc;
	{
		/* generate keys */
		char *argv[] = { PACKAGE_NAME, "whoami", NULL };
		int argc = sizeof argv / sizeof argv[0] - 1;
		rc = agent(&state, argc, argv);
		test_assert(rc == EXIT_SUCCESS, "agent() returned %i", rc);
	}
	/* NB: skipping token generation — use self-signed */
	{
		/* add trusted key (self-signed) */
		char *signer_key = state.defaults.keyring.phex;
		char *argv[] = { PACKAGE_NAME, "key", "add", signer_key, NULL };
		int argc = sizeof argv / sizeof argv[0] - 1;
		rc = agent(&state, argc, argv);
		test_assert(rc == EXIT_SUCCESS, "agent() returned %i", rc);
	}
	return test_status;
}

static int create_fake_home(void)
{
	char fakehome[] = "0000-0029-XXXXXX";
	if (!test_assert(mkdtemp(fakehome) != NULL, "mkdtemp()")) {
		perror("mkdtemp");
		return test_status;
	}
	setenv("HOME", fakehome, 1);
	return test_status;
}

int main(void)
{
	char name[] = "lcagent reload";

	test_name(name);
	test_require_net(TEST_NET_BASIC);

	ifx = get_multicast_if();
	if (!ifx) return (test_status = TEST_WARN);
	if (!test_assert(if_indextoname(ifx, ifname) != NULL, "if_indextoname()"))
		return test_status;

	/* initialize */
	if (create_fake_home()) return test_status;
	if (create_keys_and_tokens()) return test_status;
	if (test_write_config(0)) return test_status;
	if (test_start_server()) return test_status;

	/* run tests */
	if (test_config(-1)) goto stop_server;
	if (test_write_config(1)) goto stop_server;
	if (test_reload_server()) return test_status;
	if (test_config(0)) goto stop_server;

	test_log("\n");
stop_server:
	test_stop_server();

	return test_status;
}
