#include "minishell.h"

static int exec_external(t_command *cmd, t_shell *sh)
{
	char **envp = env_to_envp(sh);
	if (!envp)
		return 1;
	execve(cmd->path, cmd->argv, envp);
	perror(cmd->path);
	env_free_envp(envp);
	return 126;
}

static int run_command_child(t_command *cmd, t_shell *sh)
{
	int saved_in, saved_out;

	if (apply_redirections(cmd, &saved_in, &saved_out) != 0)
		return 1;
	if (is_builtin(cmd->argv[0]))
		return exec_builtin(cmd, sh);
	return exec_external(cmd, sh);
}

static int run_command_parent_builtin(t_command *cmd, t_shell *sh)
{
	int saved_in, saved_out;
	int status;

	if (apply_redirections(cmd, &saved_in, &saved_out) != 0)
		return 1;
	status = exec_builtin(cmd, sh);
	restore_redirections(saved_in, saved_out);
	return status;
}

static int setup_pipes(int idx, int count, int pipefd[2])
{
	if (idx + 1 >= count)
		return 0;
	if (pipe(pipefd) == -1)
		return 1;
	return 0;
}

static void setup_child_fds(int idx, int count, int prev_read, int pipefd[2])
{
	if (prev_read != -1)
	{
		dup2(prev_read, STDIN_FILENO);
		close(prev_read);
	}
	if (idx + 1 < count)
	{
		dup2(pipefd[1], STDOUT_FILENO);
		close(pipefd[0]);
		close(pipefd[1]);
	}
}

static void close_parent_fds(int idx, int count, int *prev_read, int pipefd[2])
{
	if (*prev_read != -1)
		close(*prev_read);
	if (idx + 1 < count)
	{
		close(pipefd[1]);
		*prev_read = pipefd[0];
	}
	else
		*prev_read = -1;
}

int execute_pipeline(t_pipeline *p, t_shell *sh)
{
	int i;
	int prev_read = -1;
	pid_t *pids;
	int status = 0;

	if (!p || p->count == 0)
		return 0;
	if (prepare_heredocs(p, sh) != 0)
		return sh->exit_status;
	for (size_t k = 0; k < p->count; k++)
	{
		if (!p->cmds[k]->argv || !p->cmds[k]->argv[0])
			return 1;
		free(p->cmds[k]->path);
		p->cmds[k]->path = resolve_path(p->cmds[k]->argv[0], sh);
	}
	if (p->count == 1 && is_builtin(p->cmds[0]->argv[0]))
		return run_command_parent_builtin(p->cmds[0], sh);
	pids = (pid_t *)calloc(p->count, sizeof(pid_t));
	if (!pids)
		return 1;
	for (i = 0; i < (int)p->count; i++)
	{
		int pipefd[2] = {-1, -1};
		if (setup_pipes(i, (int)p->count, pipefd))
			break;
		pids[i] = fork();
		if (pids[i] == 0)
		{
			ms_set_child_signals();
			setup_child_fds(i, (int)p->count, prev_read, pipefd);
			if (!p->cmds[i]->path)
			{
				fprintf(stderr, "minishell: %s: command not found\n", p->cmds[i]->argv[0]);
				exit(127);
			}
			status = run_command_child(p->cmds[i], sh);
			exit(status);
		}
		close_parent_fds(i, (int)p->count, &prev_read, pipefd);
	}
	for (i = 0; i < (int)p->count; i++)
	{
		int wstatus = 0;
		if (pids[i] > 0)
		{
			waitpid(pids[i], &wstatus, 0);
			if (i == (int)p->count - 1)
			{
				if (WIFEXITED(wstatus))
					status = WEXITSTATUS(wstatus);
				else if (WIFSIGNALED(wstatus))
					status = 128 + WTERMSIG(wstatus);
			}
		}
	}
	free(pids);
	return status;
}
