from collections.abc import Callable, Sequence
from typing import TYPE_CHECKING, TypeVar, overload

import remedapy as R

from .decorator import make_data_last

if TYPE_CHECKING:
    from _typeshed import SupportsRichComparison

T = TypeVar('T')


@overload
def sorted_index_by(data: Sequence['T'], item: 'T', fn: Callable[['T'], 'SupportsRichComparison'], /) -> int: ...


@overload
def sorted_index_by(item: 'T', fn: Callable[['T'], 'SupportsRichComparison'], /) -> Callable[[Sequence['T']], int]: ...


@make_data_last
def sorted_index_by(data: Sequence['T'], item: 'T', function: Callable[['T'], 'SupportsRichComparison'], /) -> int:
    """
    Given a sorted sequence and a comparison function and an item, returns the index.

    The index is the first position where the item should be inserted to keep the sequence sorted.

    "sorted" means that elements are sorted by the function.

    The result is also the number of elements smaller that the item when compared with the function.

    Parameters
    ----------
    data: Sequence[T]
        The data.
    item: T
        The item to insert.
    function: Callable[[T], SupportsRichComparison]
        The comparison function.

    Returns
    -------
    int
        The index. Will be non-negative.

    See Also
    --------
    sorted_last_index_by

    Examples
    --------
    Data first:
    >>> R.sorted_index_by([{'age': 20}, {'age': 22}], {'age': 21}, R.prop('age'))
    1
    >>> R.sorted_index_by([{'age': 20}, {'age': 21}], {'age': 21}, R.prop('age'))
    1
    >>> R.sorted_index_by([{'age': 20}, {'age': 22}], {'age': 24}, R.prop('age'))
    2

    Data last:
    >>> R.sorted_index_by({'age': 21}, R.prop('age'))([{'age': 20}, {'age': 22}])
    1

    """
    return R.sorted_index_with(data, R.piped(function, R.lt(function(item))))
