/* ************************************************************************** */
/*                                                                            */
/*                                                        :::      ::::::::   */
/*   executor.c                                         :+:      :+:    :+:   */
/*                                                    +:+ +:+         +:+     */
/*   By: sede-san <sede-san@student.42madrid.com    +#+  +:+       +#+        */
/*                                                +#+#+#+#+#+   +#+           */
/*   Created: 2025/10/28 13:03:44 by sede-san          #+#    #+#             */
/*   Updated: 2025/10/29 20:37:09 by sede-san         ###   ########.fr       */
/*                                                                            */
/* ************************************************************************** */

#include "executor.h"

static char		*solve_path(char *cmd_name, t_minishell *msh);
static u_int8_t	path_is_solved(char *cmd_name);
static void		handle_child(t_command *cmd, t_minishell *msh);
static void		handle_parent(pid_t child_pid, t_command *cmd, t_minishell *msh);

u_int8_t	execute(
	t_command cmd,
	t_minishell *msh
){
	pid_t	child_pid;

	cmd.path = solve_path(cmd.argv[0], msh);
	if (!cmd.path)
	{
		// command not found
		ft_eprintf("minishell: %s: command not found\n", cmd.argv[0]);
		return (msh->exit_status = 127, msh->exit_status);
	}
	if (access(cmd.path, X_OK) != EXIT_SUCCESS)
	{
		// permission denied
		ft_eputstr("minishell: ");
		perror(cmd.path);
		return (msh->exit_status = 126, msh->exit_status);
	}
	child_pid = fork();
	if (child_pid == -1)
		perror("minishell");
	else if (child_pid == 0)
		handle_child(&cmd, msh);
	else
		handle_parent(child_pid, &cmd, msh);
	return (msh->exit_status);
}

static char	*solve_path(
	char *cmd_name,
	t_minishell *msh
){
	char	*cmd_path;
	char	**path;
	size_t	i;

	if (path_is_solved(cmd_name))
		// return a copy to avoid double free on parent
		return (ft_strdup(cmd_name));
	//TODO substitute getenv call for own getenv
	path = ft_split(getenv("PATH"), COLON);
	if (!path)
		return (NULL);
	(void)msh;
	cmd_path = NULL;
	i = -1;
	while (!cmd_path && path[++i])
	{
		cmd_path = ft_strnjoin(3, path[i], "/", cmd_name);
		if (!cmd_path)
			return (NULL);
		/**
		 * If a command exists but user has no execution permission
		 * the command is shown as non existant instead of showing the
		 * last ocurrence found
		 *
		 * TLDR:	bash shows 'Permission denied'
		 * 			and minishell 'command not found'
		 *
		 * TEST:	execute an existing command without permission to do so
		 */
		if (access(cmd_path, X_OK) != EXIT_SUCCESS)
			ft_free((void **)&cmd_path);
	}
	ft_free_split((char **)path);
	if (!cmd_path)
		return (NULL);
	return (cmd_path);
}

static u_int8_t	path_is_solved(
	char *cmd_name
){
	return (ft_strncmp(cmd_name, "/", 1) == 0
		|| (cmd_name[1] && ft_strncmp(cmd_name, "./", 2) == 0)
		|| (cmd_name[2] && ft_strncmp(cmd_name, "../", 3) == 0)
	);
}

static void	handle_child(
	t_command *cmd,
	t_minishell *msh
){
	execve(cmd->path, cmd->argv, msh->variables.environment);
}

static void	handle_parent(
	pid_t child_pid,
	t_command *cmd,
	t_minishell *msh
){
	if (waitpid(child_pid, (int *)&msh->exit_status, 0) == EXIT_SUCCESS)
	{
		// handle success
	}
	else
	{
		// handle error
	}
	ft_free((void **)&cmd->path);
	ft_free_split(cmd->argv);
}
