/* ************************************************************************** */
/*                                                                            */
/*                                                        :::      ::::::::   */
/*   environment.c                                      :+:      :+:    :+:   */
/*                                                    +:+ +:+         +:+     */
/*   By: sede-san <sede-san@student.42madrid.com    +#+  +:+       +#+        */
/*                                                +#+#+#+#+#+   +#+           */
/*   Created: 2025/12/01 09:12:39 by sede-san          #+#    #+#             */
/*   Updated: 2026/02/14 13:30:34 by sede-san         ###   ########.fr       */
/*                                                                            */
/* ************************************************************************** */

#include "minishell.h"
#include "core.h"
#include "variables.h"
#include "errors.h"

/**
 * @brief Retrieves the value of an environment variable from the shell's
 *        environment hashmap.
 *
 * This function searches for the specified environment variable name in the
 * minishell's environment variable hashmap and returns its associated value.
 *
 * @param name      The name of the environment variable to retrieve.
 * @param minishell Pointer to the minishell object.
 *
 * @return The value of the environment variable if found, NULL if not found
 */
char	*get_env(
	const char *name,
	t_minishell *minishell
)
{
	return (ft_hashmap_get(minishell->variables.environment, name));
}

/**
 * @brief Sets an environment variable in the minishell's environment hashmap.
 *
 * This function adds a new environment variable or updates an existing one
 * in the minishell's environment hashmap. If the variable already exists,
 * the old value is freed to prevent memory leaks. If the variable is new,
 * a duplicate of the key name is created for storage.
 *
 * @param name      The name of the environment variable to set
 * @param value     The value to assign to the environment variable
 * @param minishell Pointer to the minishell object.
 */
void	set_env(
	const char *name,
	char *value,
	t_minishell *minishell
)
{
	t_hashmap		*environment;
	char			*key;
	char			*val;
	char			*old_value;
	bool			owns_key;

	environment = minishell->variables.environment;
	owns_key = false;
	key = (char *)name;
	if (name != NULL && !ft_hashmap_contains_key(environment, name))
	{
		key = ft_strdup(name);
		if (key == NULL)
			return (minishell->exit = true, malloc_error());
		owns_key = true;
	}
	if (value == NULL)
		val = ft_strdup("");
	else
		val = ft_strdup(value);
	if (val == NULL)
	{
		if (owns_key)
			free(key);
		return (minishell->exit = true, malloc_error());
	}
	old_value = ft_hashmap_put(environment, key, val);
	if (old_value != NULL)
		free(old_value);
}

/**
 * @brief Removes an environment variable by name.
 * 
 * @param name      The name of the environment variable to remove.
 * @param minishell Pointer to the minishell structure.
 * 
 * @note If the environment variable exists, it will be removed from the hashmap
 *       and its associated value will be freed.
 */
void	unset_env(
	const char *name,
	t_minishell *minishell
)
{
	t_hashmap	*environment;
	char		*val;
	
	environment = minishell->variables.environment;
	val = ft_hashmap_remove(environment, (void *)name);
	if (val != NULL)
		free(val);
}

/**
 * @brief Converts the environment variables hashmap to an envp array format.
 *
 * This function extracts all environment variables from the minishell's
 * environment hashmap and converts them into a NULL-terminated array of
 * strings in the format "KEY=VALUE".
 *
 * @param minishell Pointer to the minishell structure containing the environment
 *            variables hashmap.
 *
 * @return A dynamically allocated array of strings representing environment
 *         variables in "KEY=VALUE" format, terminated by NULL. Returns NULL
 *         if memory allocation fails. The caller is responsible for freeing
 *         the returned array and its individual string elements using
 *         the `free_envp()` function.
 *
 * @note The function allocates memory for both the array and individual
 *       strings using malloc and ft_strnjoin respectively.
 * @note The returned array size is environment->size + 1 to accommodate
 *       the NULL terminator.
 */
char	**get_envp(
	t_minishell *minishell
)
{
	char		**envp;
	t_list		*env_list;
	t_list		*env;
	t_map_entry	*entry;
	size_t		i;

	env_list = ft_hashmap_entries(minishell->variables.environment);
	envp = (char **)malloc(
			(minishell->variables.environment->size + 1) * sizeof(char *)
			);
	if (envp != NULL)
	{
		i = 0;
		env = env_list;
		while (env != NULL)
		{
			entry = env->content;
			envp[i++] = ft_strnjoin(3, entry->key, "=", entry->value);
			env = env->next;
		}
		envp[i] = NULL;
	}
	ft_lstclear_nodes(&env_list);
	return (envp);
}

/**
 * @brief Parses and stores environment variables from envp array into a hashmap
 *
 * This function iterates through the environment variables array (envp) and
 * splits each variable string on the '=' delimiter to separate the variable
 * name from its value. Each name-value pair is then stored in the minishell's
 * environment hashmap for later retrieval.
 *
 * @param envp Array of environment variable strings in "NAME=value" format
 * @param minishell  Pointer to the minishell structure containing the environment
 *             hashmap
 *
 * @note The function assumes envp strings are in the standard format
 *       "NAME=value"
 */
void	set_envp(
	char **envp,
	t_minishell *minishell
)
{
	t_hashmap	**environment;
	char		*equal;
	char		*key;
	char		*value;
	char		*new_shlvl;
	int			shlvl;
	size_t		key_len;

	if (minishell == NULL || envp == NULL)
		return ;
	environment = &minishell->variables.environment;
	*environment = ft_hashmap_new(32, ft_hashmap_hashstr, ft_hashmap_strcmp);
	if (*environment == NULL)
		return ;
	while (*envp != NULL)
	{
		equal = ft_strchr(*envp, '=');
		if (equal == NULL)
		{
			envp++;
			continue ;
		}
		key_len = (size_t)(equal - *envp);
		key = ft_substr(*envp, 0, key_len);
		value = ft_strdup(equal + 1);
		if (key == NULL || value == NULL)
		{
			free(key);
			free(value);
			minishell->exit = true;
			return (malloc_error());
		}
		set_env(key, value, minishell);
		free(key);
		free(value);
		envp++;
	}
	value = get_env("SHLVL", minishell);
	shlvl = 0;
	if (value != NULL)
		shlvl = ft_atoi(value);
	if (shlvl < 0)
		shlvl = 0;
	new_shlvl = ft_itoa(shlvl + 1);
	if (new_shlvl == NULL)
		return (minishell->exit = true, malloc_error());
	set_env("SHLVL", new_shlvl, minishell);
	free(new_shlvl);
}
