#include "sandbox.h"

#include "entry.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pwd.h>
#include <unistd.h>

Command COMMANDS[] = {
	{CommandHelp, "help", "[command]", "Prints this help message.",
	 "TODO: Add details."},
	{CommandVersion, "version", NULL, "Prints the version of the program.",
	 "TODO: Add details."},
};

int main(int argc, char* argv[]) {
	struct passwd* pw = getpwnam(SANDBOX_USER);
	if (pw == NULL) {
		Log(LOG_LEVEL_ERROR, "Failed to get the 'sandbox' user (%s).", strerror(errno));
		return EXIT_FAILURE;
	}

	// Check if the current user is root or the 'sandbox' user
	if (getuid() != 0 && getuid() != pw->pw_uid) {
		Log(LOG_LEVEL_ERROR, "You must be root or the 'sandbox' user to use this program.");
		return EXIT_FAILURE;
	}

	// Try and switch to the 'sandbox' user if we are root
	if (geteuid() == 0) {
		if (setregid(pw->pw_gid, pw->pw_gid) == -1) {
			Log(LOG_LEVEL_ERROR, "Failed to set the real and effective group ID to the 'sandbox' user (%s).", strerror(errno));
			return EXIT_FAILURE;
		}

		if (setreuid(pw->pw_uid, pw->pw_uid) == -1) {
			Log(LOG_LEVEL_ERROR, "Failed to set the real and effective user ID to the 'sandbox' user (%s).", strerror(errno));
			return EXIT_FAILURE;
		}
	}

	// If there are no arguments, print the help message
	if (argc == 1)
		return CommandHelp(0, NULL);

	const char* input = argv[1];
	size_t input_length = strlen(input);

	const Command* command = NULL;
	for (size_t i = 0; i < sizeof(COMMANDS) / sizeof(COMMANDS[0]); i++) {
		if (COMMANDS[i].name == NULL)
			continue;

		if (input_length > strlen(COMMANDS[i].name))
			continue;

		if (strncmp(input, COMMANDS[i].name, input_length) == 0) {
			if (command != NULL) {
				Log(LOG_LEVEL_ERROR, "Ambiguous command '%s'.", input);
				return EXIT_FAILURE;
			}

			command = &COMMANDS[i];
		}
	}

	if (command == NULL) {
		Log(LOG_LEVEL_ERROR, "Unknown command '%s'.", input);
		return EXIT_FAILURE;
	}

	return command->handler(argc - 2, argv + 2);
}

int CommandHelp(int argc, char* argv[]) {
	if (argc == 0) {
		fprintf(stdout, "Usage: sandbox <command> [arguments] [options]\n");
		fprintf(stdout, "\n");
		fprintf(stdout, "Commands:\n");
		for (size_t i = 0; i < sizeof(COMMANDS) / sizeof(COMMANDS[0]); i++) {
			if (COMMANDS[i].name == NULL) {
				fprintf(stdout, "\n");
				continue;
			}

			fprintf(stdout, "    %s", COMMANDS[i].name);
			if (COMMANDS[i].arguments != NULL)
				fprintf(stdout, " %s", COMMANDS[i].arguments);
			fprintf(stdout, " - %s\n", COMMANDS[i].description);
		}

		fprintf(stdout, "\nFor more information, run 'sandbox help <command>'.\n");

		return EXIT_SUCCESS;
	} else if (argc == 1) {
		const char* input = argv[0];

		for (size_t i = 0; i < sizeof(COMMANDS) / sizeof(COMMANDS[0]); i++) {
			if (COMMANDS[i].name == NULL)
				continue;

			if (strcmp(input, COMMANDS[i].name) == 0) {
				fprintf(stdout, "Usage: sandbox %s", COMMANDS[i].name);
				if (COMMANDS[i].arguments != NULL)
					fprintf(stdout, " %s", COMMANDS[i].arguments);
				fprintf(stdout, "\n");
				fprintf(stdout, "\n");
				fprintf(stdout, "%s\n", COMMANDS[i].description);
				fprintf(stdout, "\n");
				fprintf(stdout, "%s\n", COMMANDS[i].details);

				return EXIT_SUCCESS;
			}
		}

		Log(LOG_LEVEL_ERROR, "Unknown command '%s'.", input);
		return EXIT_FAILURE;
	} else {
		Log(LOG_LEVEL_ERROR, "Too many arguments supplied to 'help'.");
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}

int CommandVersion(int argc, char* argv[]) {
	fprintf(stdout, "Sandbox utility v%s\n", VERSION);
	return EXIT_SUCCESS;
}