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

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

static void handle_execve_error();
static void handle_parent_process(
	int *prev_read_fd,
	t_list *current_command,
	int pipefd[2]
);
static int handle_pipeline(
	t_list *current_command,
	int pipefd[2],
	int *exit_status
);
static inline uint8_t	execute_builtin(
	t_command *command,
	t_minishell *minishell
);
static void	execute_external_command(
	t_command *command,
	t_minishell *minishell
);
static pid_t handle_fork(
	t_list *current_command,
	t_minishell *minishell
);
static uint8_t execute_command(
	t_command *command,
	t_minishell *minishell
);

static inline bool		can_execute_command(t_command *command, t_minishell *minishell);
static inline bool		command_exists(t_command *command, t_minishell *minishell);
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;
	int		exit_status;
	int		pipefd[2];
	pid_t	pid;


	prev_read_fd = -1;
	exit_status = EXIT_SUCCESS;
	current_command = command_list;
	while (current_command != NULL)
	{
		if (handle_pipeline(current_command, pipefd, &exit_status) == EXIT_FAILURE)
			break ;
		pid = handle_fork(current_command, minishell);
		if (pid == -1)
		{
			perror("fork");
			exit_status = EXIT_FAILURE;
			break ;
		}
		if (pid == CHILD_PID)
			handle_child_process(prev_read_fd, current_command, pipefd, minishell);
		handle_parent_process(&prev_read_fd, current_command, pipefd);
		current_command = current_command->next;
	}
	ft_lstclear(&command_list, (void (*)(void *))cmdfree);
	while (wait(&exit_status) > 0)
	{
		if (WIFEXITED(exit_status))
			exit_status = WEXITSTATUS(exit_status);
	}
	return (exit_status);
}

static int handle_pipeline(
	t_list *current_command,
	int pipefd[2],
	int *exit_status
) {
	if (current_command->next) // create pipe if needed
	{
		if (pipe(pipefd) == -1)
		{
			perror("pipe");
			*exit_status = EXIT_FAILURE;
			return (EXIT_FAILURE);
		}
	}
	return (EXIT_SUCCESS);
}

static void handle_parent_process(
	int *prev_read_fd,
	t_list *current_command,
	int pipefd[2]
) {
	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;
}

static pid_t handle_fork(
	t_list *current_command,
	t_minishell *minishell
) {
	pid_t	pid;
	const t_command *command = (t_command *)current_command->content;

	pid = 0;
	if (current_command->next != NULL
		|| !is_builtin(command->path, minishell))
		pid = fork();
	return (pid);
}

void handle_child_process(int prev_read_fd, t_list *current_command, int pipefd[2], t_minishell *minishell)
{
	redirect_pipes(prev_read_fd, current_command, pipefd);
	execute_command((t_command *)current_command->content, minishell);
}

void redirect_pipes(int prev_read_fd, t_list *current_command, int pipefd[2])
{
	redirect_input_pipe(prev_read_fd);
	redirect_output_pipe(current_command, pipefd);
}

void redirect_output_pipe(t_list * current_command, int  pipefd[2])
{
	if (current_command->next)
	{
		dup2(pipefd[WRITE_PIPE], STDOUT_FILENO);
		close(pipefd[READ_PIPE]);
		close(pipefd[WRITE_PIPE]);
	}
}

void redirect_input_pipe(int prev_read_fd)
{
	if (prev_read_fd != -1)
	{
		dup2(prev_read_fd, STDIN_FILENO);
		close(prev_read_fd);
	}
}

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 uint8_t execute_command(
	t_command *command,
	t_minishell *minishell
) {
	if (is_builtin(command->path, minishell))
		return (execute_builtin(command, minishell));
	else
	{
		execute_external_command(command, minishell);
		return (EXIT_FAILURE); //! should never reach here
	}
}

static inline uint8_t	execute_builtin(
	t_command *command,
	t_minishell *minishell
) {
	const t_builtin_func	builtin 
		= ft_hashmap_get(minishell->builtins, command->path);

	return (builtin(*command, minishell));
}

static void	execute_external_command(
	t_command *command,
	t_minishell *minishell
) {
	char	**envp;

	envp = get_envp(minishell);
	if (envp == NULL)
	{
		perror("get_envp");
		exit(EXIT_FAILURE);
	}
	execve(command->path, command->argv, envp);
	handle_execve_error();
}

static void handle_execve_error() {
	perror("execve");
	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");
// }