from collections.abc import Callable, Iterable
from typing import TypeVar, overload

from remedapy.decorator import make_data_last

from .apply import apply

T = TypeVar('T')
V = TypeVar('V')


@overload
def from_keys(data: Iterable[T], fn: Callable[[T], V] | Callable[[], V], /) -> dict[T, V]: ...


@overload
def from_keys(fn: Callable[[T], V] | Callable[[], V], /) -> Callable[[Iterable[T]], dict[T, V]]: ...


@make_data_last
def from_keys(
    data: Iterable[T],
    function: Callable[[T], V] | Callable[[], V],
    /,
) -> dict[T, V]:
    """
    Given an iterable and a function, returns a dict.

    Keys are elements from the iterable, values are results of applying the function to the keys.

    Parameters
    ----------
    data: Iterable[T]
        Iterable of keys (positional-only).

    function: Callable[[T], V] | Callable[[], V]
        Function to apply to the keys (positional-only).

    Returns
    -------
    dict[T, V]
        Dict with keys from the iterable and values from the function.

    See Also
    --------
    from_entries

    Examples
    --------
    Data first:
    >>> R.from_keys(['cat', 'dog'], R.length())
    {'cat': 3, 'dog': 3}
    >>> R.from_keys([1, 2], R.add(1))
    {1: 2, 2: 3}
    >>> R.from_keys(['cat', 'dog'], R.constant('uwu'))
    {'cat': 'uwu', 'dog': 'uwu'}

    Data last:
    >>> R.pipe(['cat', 'dog'], R.from_keys(R.length()))
    {'cat': 3, 'dog': 3}
    >>> R.pipe([1, 2], R.from_keys(R.add(1)))
    {1: 2, 2: 3}

    """
    return {x: apply(x, function) for x in data}
