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

from remedapy.decorator import make_data_last

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


@overload
def pull_object(data: Iterable[T], key_fn: Callable[[T], K], v_fn: Callable[[T], V], /) -> dict[K, V]: ...


@overload
def pull_object(key_fn: Callable[[T], K], v_fn: Callable[[T], V], /) -> Callable[[Iterable[T]], dict[K, V]]: ...


@make_data_last
def pull_object(data: Iterable[T], key_fn: Callable[[T], K], v_fn: Callable[[T], V], /) -> dict[K, V]:
    """
    Given an iterable, key function, and value function; returns a dict.

    Parameters
    ----------
    data: Iterable[T]
        The iterable to pull objects from.
    key_fn: Callable[[T], K]
        The key function to apply to the objects.
    v_fn: Callable[[T], V]
        The value function to apply to the objects.

    Returns
    -------
    dict[K, V]
        The dict from mapped values from the iterable.

    Examples
    --------
    Data first:
    >>> R.pull_object(
    ...     [{'name': 'john', 'email': 'john@remedajs.com'}, {'name': 'jane', 'email': 'jane@remedajs.com'}],
    ...     R.prop('name'),
    ...     R.prop('email')
    ... )
    {'john': 'john@remedajs.com', 'jane': 'jane@remedajs.com'}

    Data last:
    >>> R.pipe(
    ...     [{'name': 'john', 'email': 'john@remedajs.com'}, {'name': 'jane', 'email': 'jane@remedajs.com'}],
    ...     R.pull_object(R.prop('name'), R.prop('email'))
    ... )
    {'john': 'john@remedajs.com', 'jane': 'jane@remedajs.com'}

    """
    return {key_fn(x): v_fn(x) for x in data}
