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

from remedapy.decorator import make_data_last

T = TypeVar('T')


@overload
def reduce(data: Iterable[T], callbackfn: Callable[[T, T], T], /) -> T: ...


@overload
def reduce(callbackfn: Callable[[T, T], T], /) -> Callable[[Iterable[T]], T]: ...


@make_data_last
def reduce(
    iterable: Iterable[T],
    callbackfn: Callable[[T, T], T],
    /,
) -> T:
    """
    Applies the given function to each element of the iterable in order passing previous result to the function.

    Parameters
    ----------
    iterable: Iterable[T]
        The iterable (positional-only).
    callbackfn: Callable[[T, T], T]
        The reducer function (positional-only).

    Returns
    -------
    T
        The reduced value.

    Examples
    --------
    Data first:
    >>> R.reduce([1, 2, 3, 4, 5], lambda a, x: a + x)
    15

    Data last:
    >>> R.reduce(R.add)([1, 2, 3, 4, 5])
    15
    >>> R.pipe([1, 2, 3, 4, 5], R.reduce(R.add))
    15

    """
    it = iter(iterable)
    initial_value = next(it)
    for item in it:
        initial_value = callbackfn(initial_value, item)
    return initial_value
