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

from .decorator import make_data_last

T = TypeVar('T')
U = TypeVar('U')


@overload
def find_index(data: Iterable[T], predicate: Callable[[T], bool], /, default: U = -1) -> int | U: ...


@overload
def find_index(predicate: Callable[[T], bool], /, default: U = -1) -> Callable[[Iterable[T]], int | U]: ...


@make_data_last
def find_index(iterable: Iterable[T], predicate: Callable[[T], bool], /, default: U = -1) -> int | U:
    """
    Returns the index of the first element of the iterable that satisfies the predicate.

    If no element does returns default provided (default default value is -1).

    Parameters
    ----------
    iterable : Iterable[T]
        Input iterable (positional-only).
    predicate: Callable[[T], bool]
        Predicate to check the elements of the iterable (positional-only).
    default: U
        Default value to return if no element satisfies the predicate (keyword-only, optional).

    Returns
    -------
    int | U
        Index of the first element of the iterable that satisfies the predicate or the default value.

    Examples
    --------
    Data first:
    >>> R.find_index([1, 3, 4, 6], R.is_even)
    2
    >>> R.find_index([1, 3, 4, 6], lambda x: x % 2 == 0)
    2
    >>> R.find_index([1, 3, 4, 6], lambda x: 1 < x < 4)
    1
    >>> R.find_index([1, 3, 4, 6], lambda x: x > 8)
    -1
    >>> R.find_index([1, 3, 4, 6], lambda x: x > 8, default=69)
    69

    Data last:
    >>> R.find_index(R.is_even)([1, 3, 4, 6])
    2
    >>> R.pipe([1, 2, 3, 4, 3, 2, 1], R.find_index(R.is_even))
    1
    >>> R.pipe([1, 3, 5], R.find_index(R.is_even, default=10))
    10

    """
    for i, item in enumerate(iterable):
        if predicate(item):
            return i
    return default
