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

from .decorator import make_data_last

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

real_zip = zip


@overload
def zip(first: Iterable[T], second: Iterable[U], /, *, strict: bool = False) -> Iterable[tuple[T, U]]: ...


@overload
def zip(second: Iterable[U], /, *, strict: bool = False) -> Callable[[Iterable[T]], Iterable[tuple[T, U]]]: ...


@make_data_last
def zip(first: Iterable[T], second: Iterable[U], /, *, strict: bool = False) -> Iterable[tuple[T, U]]:
    """
    Yields pairs of elements from the two iterables.

    Alias for zip(first, second, strict=strict).

    Parameters
    ----------
    first: Iterable[T]
        First iterable (positional-only).
    second: Iterable[U]
        Second iterable (positional-only).
    strict: bool
        Whether to raise StopIteration if the iterables are of different lengths (keyword-only, optional).

    Yields
    ------
    tuple[T, U]
        Pairs of elements from the two iterables.

    Examples
    --------
    Data first:
    >>> list(R.zip([1, 2], ['a', 'b']))
    [(1, 'a'), (2, 'b')]

    Data last:
    >>> list(R.zip(['a', 'b'])([1, 2]))
    [(1, 'a'), (2, 'b')]

    """
    yield from real_zip(first, second, strict=strict)
