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

/* process cmdline options and args, verify that state is set correctly */

#include "test.h"
#include <state.h>

/* test state_parse_args() with bad option (-z) */
static int test_state_bad_option(char *opt)
{
	char *argv[] = { "lcagent", opt, NULL };
	int argc = sizeof argv / sizeof argv[0] - 1;
	state_t state = {0};
	int rc;

	errno = 0;
	rc = state_parse_args(&state, argc, argv);
	test_assert(errno == EINVAL, "bad option (%s): EINVAL", opt);
	test_assert(rc == -1, "bad option (%s): return -1", opt);

	return test_status;
}

/* Everything after -- is treated as an argument, not an option */
static int test_state_endoptions(void)
{
	char *argv[] = { "lcagent", "--", "whoami", "--verbose", NULL };
	int argc = sizeof argv / sizeof argv[0] - 1;
	state_t state = {0};
	int rc;

	rc = state_parse_args(&state, argc, argv);
	test_assert(rc == 0, "-- ends option parsing rc = %i", rc);
	test_assert(!STATE(&state, STATE_VERBOSE), "flag NOT set");

	return test_status;
}

static int test_state_long(char *opt, unsigned int flag)
{
	char flaarg[32] = "--";
	char *argv[] = { "lcagent", flaarg, NULL };
	int argc = sizeof argv / sizeof argv[0] - 1;
	state_t state = {0};
	int rc;

	strcpy(flaarg + 2, opt);
	rc = state_parse_args(&state, argc, argv);
	test_assert(rc == 0, "%s, rc = %i", flaarg, rc);
	test_assert(STATE(&state, flag), "flag set");

	return test_status;
}

static int test_state_short(char opt, unsigned int flag)
{
	char flaarg[] = "-?";
	char *argv[] = { "lcagent", flaarg, NULL };
	int argc = sizeof argv / sizeof argv[0] - 1;
	state_t state = {0};
	int rc;

	flaarg[1] = opt;
	rc = state_parse_args(&state, argc, argv);
	test_assert(rc == 0, "%s", flaarg);
	test_assert(STATE(&state, flag), "flag set");

	return test_status;
}

/* too many arguments (> 256) */
static int test_state_args_E2BIG(void)
{
	state_t state = {0};
	char *argv[sizeof state.optmask * CHAR_BIT + 2] = {0};
	int argc = sizeof argv / sizeof argv[0] - 1;
	int rc;

	argv[0] = PACKAGE_NAME;
	for (int i = 1; i < argc; i++) argv[i] = "arg";

	errno = 0;
	rc = state_parse_args(&state, argc, argv);
	if (!test_assert(errno == E2BIG, "errno = E2BIG")) return test_status;
	if (!test_assert(rc == -1, "%s", __func__)) return test_status;

	return test_status;
}

/* test leftover args are appended to state with mask */
static int test_state_args(void)
{
	char hexkey[] = "305c464b3c1839a0101a95c4a31c0022e93d861ee2d5084d5be3bb2a862b0c11e05c5a9902682b5f14ee39f68123e5cfc1dcb6a82175162377ebc24e3fb0a91a";
	char *argv[] = { PACKAGE_NAME, "sign", "-v", hexkey, NULL };
	int argc = sizeof argv / sizeof argv[0] - 1;
	state_t state = {0};
	int rc;
	rc = state_parse_args(&state, argc, argv);
	test_assert(rc == 0, "%s", __func__);

	/* check argc */
	test_assert(state.argc == argc, "state.argc == %i", state.argc);

	/* check argv */
	for (int i = 0; i < argc; i++) {
		test_expect(argv[i], state.argv[i]);
	}

	/* check optmask */
	test_assert(!isset(state.optmask, 0), "optmask: bit 0 not set");
	test_assert(!isset(state.optmask, 1), "optmask: bit 1 not set");
	test_assert(!isset(state.optmask, 2), "optmask: bit 2 not set");
	test_assert( isset(state.optmask, 3), "optmask: bit 3 set");

	return test_status;
}

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

	test_name(name);

#define X(a, b, c) if (b == OPT_TYPE_FLAG && test_state_short(a, c)) return test_status;
	STATE_OPTIONS_SHORT
#undef X
#define X(a, b, c) if (b == OPT_TYPE_FLAG && test_state_long(a, c)) return test_status;
	STATE_OPTIONS_LONG
#undef X

	if (test_state_bad_option("-z")) return test_status;
	if (test_state_bad_option("--badoption")) return test_status;
	if (test_state_endoptions()) return test_status;
	if (test_state_args_E2BIG()) return test_status;
	if (test_state_args()) return test_status;

	return test_status;
}
