/* ************************************************************************** */
/*                                                                            */
/*                                                        :::      ::::::::   */
/*   executor.c                                         :+:      :+:    :+:   */
/*                                                    +:+ +:+         +:+     */
/*   By: sede-san <sede-san@student.42madrid.com    +#+  +:+       +#+        */
/*                                                +#+#+#+#+#+   +#+           */
/*   Created: 2025/10/28 13:03:44 by sede-san          #+#    #+#             */
/*   Updated: 2026/02/08 03:42:11 by sede-san         ###   ########.fr       */
/*                                                                            */
/* ************************************************************************** */

#include "executor.h"
#include "builtins.h"
#include <errno.h>

static inline bool		can_execute_command(t_command *command, t_minishell *minishell);
static inline bool		command_exists(t_command *command, t_minishell *minishell);
static u_int8_t		execute_command(t_command *command, t_minishell *minishell);
static u_int8_t		execute_command(t_command *command, t_minishell *minishell);
// static inline bool		is_piped(t_command *command);
static void			cmdfree(t_command *command);
static void			cmdfree_argv(char **argv);

# define WRITE_PIPE 1
# define READ_PIPE 0
# define CHILD_PID 0

u_int8_t	execute(
	t_list *command_list,
	t_minishell *minishell
) {
	t_list		*current_command;
	int			prev_read_fd;
	pid_t		pid;
	u_int8_t	exit_status;
	int			child_exit_status;

	prev_read_fd = -1;
	exit_status = EXIT_SUCCESS;
	current_command = command_list;
	while (current_command != NULL)
	{
		t_command *command = (t_command *)current_command->content;
		int pipefd[2];

		/* Create pipe ONLY if there is a next command */
		if (current_command->next)
		{
			if (pipe(pipefd) == -1)
			{
				perror("pipe");
                exit_status = EXIT_FAILURE;
                break ;
			}
		}

        // create fork
		pid = 0;
		if (current_command->next != NULL
            || !is_builtin(command->path, minishell))
		    pid = fork();
        // handle fork error
		if (pid == -1)
		{
			perror("fork");
			return (EXIT_FAILURE);
		}

        // handle child process
		if (pid == CHILD_PID)
		{
			/* If we have input from previous pipe */
			if (prev_read_fd != -1)
			{
				dup2(prev_read_fd, STDIN_FILENO);
				close(prev_read_fd);
			}
			/* If we pipe output to next command */
			if (current_command->next)
			{
				dup2(pipefd[WRITE_PIPE], STDOUT_FILENO);
				close(pipefd[READ_PIPE]);
				close(pipefd[WRITE_PIPE]);
			}
			execute_command(command, minishell); // child process exits here
		}

        // handle parent process
        waitpid(pid, &child_exit_status, 0); // wait for child to finish
		if (prev_read_fd != -1)
			close(prev_read_fd);

		if (current_command->next)
		{
			close(pipefd[WRITE_PIPE]);          /* parent does not write */
			prev_read_fd = pipefd[READ_PIPE];  /* pass read end forward */
		}
		else
			prev_read_fd = -1;

        // continue executing
		current_command = current_command->next;
	}
	ft_lstclear(&command_list, (void (*)(void *))cmdfree);
    exit_status = child_exit_status;
	return (exit_status);
}

static inline bool	can_execute_command(
	t_command *command,
	t_minishell *minishell
) {
	if (!is_builtin(command->path, minishell)
		&& access(command->path, X_OK) != EXIT_SUCCESS)
		return (false);
	return (true);
}

static inline bool	command_exists(
	t_command *command,
	t_minishell *minishell
) {
	if (!is_builtin(command->path, minishell)
		&& access(command->path, F_OK) != EXIT_SUCCESS)
		return (false);
	return (true);
}

static u_int8_t execute_command(
	t_command *command,
	t_minishell *minishell
)
{
	char			**envp;
	t_builtin_func	builtin;

	if (is_builtin(command->path, minishell))
	{
		builtin = ft_hashmap_get(minishell->builtins, command->path);
		return (builtin(*command, minishell));
	}
	envp = get_envp(minishell);
	execve(command->path, command->argv, envp);
    // handle error if execve fails
	perror("execve");
	free_envp(envp);
	exit(EXIT_FAILURE);
}

// static inline bool	is_piped(
// 	t_command *command
// ) {
// 	return (command->piped_to != NULL || command->piped_from != NULL);
// }

// static u_int8_t handle_child_process(
// 	t_command *command,
// 	t_minishell *minishell
// ) {
// 	if (!redirect_pipes(command))
// 		exit(EXIT_FAILURE);
// 	execute_command(command, minishell);
// 	exit(EXIT_FAILURE);
// }

// static int redirect_pipe(
// 	int from,
// 	int to
// ) {
// 	if (dup2(from, to) == -1)
// 		return (-1);
// 	close(from);
// 	return (0);
// }

// static int redirect_pipes(
// 	t_command *command
// ) {
// 	if (command->piped_from &&
// 		redirect_pipe(command->piped_from->outfile, STDIN_FILENO) == -1)
// 	{
// 		perror("dup2");
// 		return (0);
// 	}
// 	if (command->piped_to &&
// 		redirect_pipe(command->outfile, STDOUT_FILENO) == -1)
// 	{
// 		perror("dup2");
// 		return (0);
// 	}
// 	return (1);
// }

// static void	show_error(
// 	t_command *command,
// 	t_minishell *minishell
// ) {
// 	if (!command_exists(command, minishell))
// 	{
// 		ft_eprintf("minishell: %s: command not found\n", command->argv[0]);
// 		minishell->exit_status = 127;
// 	}
// 	else if (!can_execute_command(command, minishell))
// 	{
// 		ft_eprintf("minishell: %s: %s\n", command->path, strerror(errno));
// 		minishell->exit_status = errno;
// 	}
// }

static void	cmdfree(
	t_command *command
) {
	if (command == NULL)
		return ;
	cmdfree_argv(command->argv);
	free(command->path);
	free(command);
}

static void	cmdfree_argv(
	char **argv
) {
	size_t	i;

	if (argv == NULL)
		return ;
	i = 0;
	while (argv[i] != NULL)
	{
		free(argv[i]);
		i++;
	}
	free(argv);
}

// static void debug_print_command_info(
// 	t_command *command
// ) {
// 	size_t	i;

// 	if (command == NULL)
// 	{
// 		printf("Command is NULL\n");
// 		return ;
// 	}
// 	printf("Command info:\n");
// 	printf("  Path: %s\n", command->path);
// 	printf("  Argc: %d\n", command->argc);
// 	printf("  Argv:\n");
// 	for (i = 0; i < (size_t)command->argc; i++)
// 		printf("    arg[%zu]: %s\n", i, command->argv[i]);
// 	printf("  Infile FD: %d\n", command->infile);
// 	printf("  Outfile FD: %d\n", command->outfile);
// 	printf("--------------------------\n");
// }