import inspect
from typing import Any


def create_dictionary_of_aggregation_methods(
    dependent_variables: list[str],
    independent_variable: str,
    aggregation_functions: list[str],
) -> dict[str, str]:
    """
    Creates a dictionary mapping each variable (dependent and independent) to its specified aggregation function.

    This function is designed to facilitate the aggregation of data by dynamically creating a mapping of variables
    to their corresponding aggregation functions. This is particularly useful in data analysis and preprocessing
    where different variables may require different methods of aggregation.

    Parameters
    ----------
    dependent_variables : list[str]
        A list of strings representing the names of the dependent variables.
    independent_variable : str
        A string representing the name of the independent variable.
    aggregation_functions : list[str]
        A list of strings representing the aggregation functions to be applied to each variable. The order of functions
        in this list should correspond to the order of variables in `dependent_variables`
        followed by the `independent_variable`.

    Returns
    -------
    dict[str, str]
        A dictionary where keys are variable names (both dependent and independent) and values are the corresponding
        aggregation functions as strings.

    Raises
    ------
    ValueError
        If the total number of variables (dependent + independent)
        does not match the number of provided aggregation functions.

    Example
    -------
    >>> create_dictionary_of_aggregation_methods(['var1', 'var2'], 'var3', ['sum', 'mean', 'count'])
    {'var1': 'sum', 'var2': 'mean', 'var3': 'count'}
    """
    variables: list[str] = dependent_variables + [independent_variable]

    if len(variables) != len(aggregation_functions):
        raise ValueError(
            "The number of variables must match the number of aggregation functions."
        )

    return {
        variable: aggregation_function
        for variable, aggregation_function in zip(variables, aggregation_functions)
    }


def get_data_name(data: Any) -> str or None:
    """
    Retrieves the variable name of the input data as it appears in the caller's scope.

    This function uses introspection to look back into the caller's local variables and find the name of the variable
    that references the data passed to this function. This can be useful for debugging or when dynamically generating
    output based on variable names.

    Parameters
    ----------
    data : any
        The data object whose variable name is to be found.

    Returns
    -------
    str or None
        The name of the variable as a string if found; otherwise, None.

    Example
    -------
    >>> my_data = [1, 2, 3]
    >>> get_data_name(my_data)
    'my_data'
    """
    callers_local_vars = inspect.currentframe().f_back.f_locals.items()
    data_name = [
        variable_name for variable_name, variable_value in callers_local_vars if variable_value is data
    ]
    if data_name:
        return data_name[0]
