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

from .decorator import make_data_last

if TYPE_CHECKING:
    from _typeshed import SupportsRichComparison

T = TypeVar('T')


@overload
def first_by(it: Iterable[T], fn: Callable[[T], 'SupportsRichComparison'], /) -> T: ...


@overload
def first_by(fn: Callable[[T], 'SupportsRichComparison'], /) -> Callable[[Iterable[T]], T]: ...


@make_data_last
def first_by(iterable: Iterable[T], function: Callable[[T], 'SupportsRichComparison'], /) -> T:
    """
    Returns the elements that would be first if the iterable were sorted by the function.

    They are yielded in the order they appear in the iterable.

    Parameters
    ----------
    iterable : Iterable[T]
        Input iterable (positional-only).
    function: Callable[[T], SupportsGtLt]
        Function to "sort" the iterable by (positional-only).

    Returns
    -------
    T
        The first element of the iterable if sorted by the function provided.

    Examples
    --------
    Data first:
    >>> R.first_by([1, 2, 3], R.identity())
    1
    >>> R.first_by([{'a': 'a'}, {'a': 'aa'}, {'a': 'aaa'}], R.piped(R.prop('a'), R.default_to(''), R.length))
    {'a': 'a'}

    Data last:
    >>> R.pipe([3, 2, 1], R.first_by(R.identity()))
    1
    >>> R.pipe([{'a': 'a'}, {'a': 'aa'}, {'a': 'aaa'}], R.first_by(R.piped(R.prop('a'), R.default_to(''), R.length)))
    {'a': 'a'}

    """
    iterable = iter(iterable)
    current = next(iterable)
    current_mapped = function(current)
    for i in iterable:
        i_mapped = function(i)
        if i_mapped < current_mapped:  # pyright: ignore[reportOperatorIssue]
            current = i
            current_mapped = i_mapped
    return current
