#include "minishell.h"

static int builtin_echo(t_command *cmd, t_shell *sh);
static int builtin_cd(t_command *cmd, t_shell *sh);
static int builtin_pwd(t_command *cmd, t_shell *sh);
static int builtin_env(t_command *cmd, t_shell *sh);
static int builtin_export(t_command *cmd, t_shell *sh);
static int builtin_unset(t_command *cmd, t_shell *sh);
static int builtin_exit(t_command *cmd, t_shell *sh);

int is_builtin(const char *name)
{
	if (!name)
		return 0;
	return (strcmp(name, "echo") == 0 || strcmp(name, "cd") == 0
		|| strcmp(name, "pwd") == 0 || strcmp(name, "env") == 0
		|| strcmp(name, "export") == 0 || strcmp(name, "unset") == 0
		|| strcmp(name, "exit") == 0);
}

int exec_builtin(t_command *cmd, t_shell *sh)
{
	if (strcmp(cmd->argv[0], "echo") == 0)
		return builtin_echo(cmd, sh);
	if (strcmp(cmd->argv[0], "cd") == 0)
		return builtin_cd(cmd, sh);
	if (strcmp(cmd->argv[0], "pwd") == 0)
		return builtin_pwd(cmd, sh);
	if (strcmp(cmd->argv[0], "env") == 0)
		return builtin_env(cmd, sh);
	if (strcmp(cmd->argv[0], "export") == 0)
		return builtin_export(cmd, sh);
	if (strcmp(cmd->argv[0], "unset") == 0)
		return builtin_unset(cmd, sh);
	if (strcmp(cmd->argv[0], "exit") == 0)
		return builtin_exit(cmd, sh);
	return 1;
}

static int builtin_echo(t_command *cmd, t_shell *sh)
{
	int i = 1;
	int newline = 1;

	(void)sh;
	while (cmd->argv[i] && cmd->argv[i][0] == '-' && cmd->argv[i][1] == 'n')
	{
		int j = 2;
		while (cmd->argv[i][j] == 'n')
			j++;
		if (cmd->argv[i][j] != '\0')
			break;
		newline = 0;
		i++;
	}
	while (cmd->argv[i])
	{
		printf("%s", cmd->argv[i]);
		if (cmd->argv[i + 1])
			printf(" ");
		i++;
	}
	if (newline)
		printf("\n");
	return 0;
}

static int builtin_pwd(t_command *cmd, t_shell *sh)
{
	char buf[4096];
	(void)cmd;
	(void)sh;
	if (getcwd(buf, sizeof(buf)))
		printf("%s\n", buf);
	return 0;
}

static int builtin_cd(t_command *cmd, t_shell *sh)
{
	char *path;
	char cwd[4096];

	if (cmd->argc > 2)
	{
		fprintf(stderr, "minishell: cd: too many arguments\n");
		return 1;
	}
	if (cmd->argc == 1)
		path = env_get(sh, "HOME");
	else
		path = cmd->argv[1];
	if (!path)
	{
		fprintf(stderr, "minishell: cd: HOME not set\n");
		return 1;
	}
	if (getcwd(cwd, sizeof(cwd)))
		env_set(sh, "OLDPWD", cwd);
	if (chdir(path) != 0)
	{
		perror("minishell: cd");
		return 1;
	}
	if (getcwd(cwd, sizeof(cwd)))
		env_set(sh, "PWD", cwd);
	return 0;
}

static int builtin_env(t_command *cmd, t_shell *sh)
{
	if (cmd->argc > 1)
	{
		fprintf(stderr, "minishell: env: too many arguments\n");
		return 1;
	}
	env_print(sh);
	return 0;
}

static int valid_identifier(const char *s)
{
	int i = 0;
	if (!s || !ms_is_alpha((unsigned char)s[0]))
		return 0;
	while (s[i])
	{
		if (!ms_is_alnum((unsigned char)s[i]))
			return 0;
		i++;
	}
	return 1;
}

static int builtin_export(t_command *cmd, t_shell *sh)
{
	int i = 1;
	if (cmd->argc == 1)
	{
		env_print(sh);
		return 0;
	}
	while (cmd->argv[i])
	{
		char *eq = strchr(cmd->argv[i], '=');
		if (eq)
		{
			char *key = ms_strndup(cmd->argv[i], (size_t)(eq - cmd->argv[i]));
			char *val = ms_strdup(eq + 1);
			if (!valid_identifier(key))
				fprintf(stderr, "minishell: export: `%s': not a valid identifier\n", cmd->argv[i]);
			else
				env_set(sh, key, val);
			free(key);
			free(val);
		}
		else
		{
			if (!valid_identifier(cmd->argv[i]))
				fprintf(stderr, "minishell: export: `%s': not a valid identifier\n", cmd->argv[i]);
			else
				env_set(sh, cmd->argv[i], "");
		}
		i++;
	}
	return 0;
}

static int builtin_unset(t_command *cmd, t_shell *sh)
{
	int i = 1;
	while (cmd->argv[i])
	{
		env_unset(sh, cmd->argv[i]);
		i++;
	}
	return 0;
}

static int builtin_exit(t_command *cmd, t_shell *sh)
{
	long code = sh->exit_status;
	int i = 0;

	if (sh->interactive)
		printf("exit\n");
	if (cmd->argc == 1)
	{
		sh->exit_requested = 1;
		sh->exit_status = (int)(code & 0xFF);
		return sh->exit_status;
	}
	if (cmd->argc > 2)
	{
		fprintf(stderr, "minishell: exit: too many arguments\n");
		return 1;
	}
	if (cmd->argv[1][i] == '+' || cmd->argv[1][i] == '-')
		i++;
	if (cmd->argv[1][i] == '\0')
	{
		fprintf(stderr, "minishell: exit: %s: numeric argument required\n", cmd->argv[1]);
		sh->exit_requested = 1;
		sh->exit_status = 2;
		return 2;
	}
	while (cmd->argv[1][i])
	{
		if (!ms_is_digit((unsigned char)cmd->argv[1][i]))
		{
			fprintf(stderr, "minishell: exit: %s: numeric argument required\n", cmd->argv[1]);
			sh->exit_requested = 1;
			sh->exit_status = 2;
			return 2;
		}
		i++;
	}
	code = strtol(cmd->argv[1], NULL, 10);
	sh->exit_requested = 1;
	sh->exit_status = (int)(code & 0xFF);
	return sh->exit_status;
}
