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

from remedapy.decorator import make_data_last
from remedapy.is_sequence import is_sequence

T = TypeVar('T')


@overload
def split_at(iterable: Iterable[T], index: int, /) -> tuple[list[T], list[T]]: ...


@overload
def split_at(index: int, /) -> Callable[[Iterable[T]], tuple[list[T], list[T]]]: ...


@make_data_last
def split_at(
    iterable: Iterable[T],
    index: int,
    /,
) -> tuple[list[T], list[T]]:
    r"""
    Splits the given iterable at the given index.

    Parameters
    ----------
    iterable: Iterable[T]
        Iterable to split (positional-only).
    index: int
        Index to split at (positional-only).

    Returns
    -------
    tuple[list[T], list[T]]
        Tuple of two sequences.

    Examples
    --------
    Data first:
    >>> R.split_at([1, 2, 3], 1)
    ([1], [2, 3])
    >>> R.split_at(range(1, 4), 1)
    ([1], [2, 3])
    >>> R.split_at((x for x in range(1, 4)), 1)
    ([1], [2, 3])
    >>> R.split_at([1, 2, 3, 4, 5], -1)
    ([1, 2, 3, 4], [5])

    Data last:
    >>> R.split_at(1)([1, 2, 3])
    ([1], [2, 3])
    >>> R.split_at(-1)([1, 2, 3, 4, 5])
    ([1, 2, 3, 4], [5])

    """
    if index < 0:
        if is_sequence(iterable):
            n_ = index % len(iterable)
            data_list = iterable
        else:
            data_list = list(iterable)
            n_ = index % len(data_list)
        iterable_ = iter(data_list)
    else:
        iterable_ = iter(iterable)
        n_ = index
    result: tuple[list[T], list[T]] = ([], [])
    for i, v in enumerate(iterable_):
        if i == n_:
            result[1].append(v)
            break
        result[0].append(v)
    result[1].extend(iterable_)
    return result
