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

from remedapy.decorator import make_data_last

T = TypeVar('T')
R = TypeVar('R')


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


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


# TODO: index and whole arr?


@make_data_last
def map_with_feedback(
    iterable: Iterable[T],
    function: Callable[[R, T], R],
    initial_value: R,
    /,
) -> Iterable[R]:
    """
    Maps iterable with a function accepting an accumulated values.

    Parameters
    ----------
    iterable: Iterable[T]
        The iterable to map.
    function: Callable[[R, T], R],
        The function to apply to each element.
    initial_value: R
        The first value for the acculumator.

    Yields
    ------
    R
        Mapped elements.

    See Also
    --------
    fold
    map
    reduce

    Examples
    --------
    Data first:
    >>> list(R.map_with_feedback([1, 2, 3, 4, 5], lambda prev, x: prev + x, 100))
    [101, 103, 106, 110, 115]

    Data last:
    >>> R.pipe([1, 2, 3, 4, 5], R.map_with_feedback(R.add, 100), list)
    [101, 103, 106, 110, 115]

    """
    current = initial_value
    for x in iterable:
        current = function(current, x)
        yield current
