from collections.abc import Callable, Collection, Sequence
from typing import Any, TypeVar, overload

Key = str | int
T = TypeVar('T')


@overload
def prop(data: dict[Key, T], /, key: Key) -> T | None: ...


@overload
def prop(data: Sequence[T], /, key: int) -> T | None: ...


@overload
def prop(key: str, /) -> Callable[[dict[str, T]], T | None]: ...


@overload
def prop(key: int, /) -> Callable[[Sequence[T]], T | None]: ...


@overload
def prop(data: dict[Key, Any], /, *keys: Key) -> Any | None: ...


@overload
def prop(key1: str, /, *keys: int | str) -> Callable[[dict[str, T]], T | None]: ...


@overload
def prop(data: Collection[Any], /, *keys: int | str) -> Any | None: ...


@overload
def prop(key1: int, /, *keys: int | str) -> Callable[[Sequence[T]], T | None]: ...


def prop_helper(data: Any, /, *keys: int | str) -> Any | None:
    if len(keys) == 0:
        return data
    try:
        new_data = data[keys[0]]
    except KeyError:
        return None
    except IndexError:
        return None
    return prop_helper(new_data, *keys[1:])


def prop(data: Any, /, *keys: int | str) -> Any | None:  # pyright: ignore[reportInconsistentOverload]
    """
    Given a dict, list, or tuple, returns the value at the given key or index.

    Supports getting a nested value by passing multiple keys.

    Parameters
    ----------
    data: dict[K, V] | Sequence[T]
        The dict to get the value from.
    keys: Iterable[K]
        The keys to get the value from.

    Returns
    -------
    Any
        The value at the given key or index.

    Examples
    --------
    Data first:
    >>> R.prop({'foo': {'bar': 'baz'}}, 'foo')
    {'bar': 'baz'}
    >>> R.prop({'foo': {'bar': 'baz'}}, 'foo', 'bar')
    'baz'
    >>> R.prop(['cat', 'dog'], 1)
    'dog'

    Data last:
    >>> R.pipe({'foo': {'bar': 'baz'}}, R.prop('foo'))
    {'bar': 'baz'}
    >>> R.pipe({'foo': {'bar': 'baz'}}, R.prop('foo', 'bar'))
    'baz'
    >>> R.pipe(['cat', 'dog'], R.prop(1))
    'dog'

    """
    if not isinstance(data, (dict, list, tuple)):
        return lambda x: prop_helper(x, data, *keys)  # pyright: ignore[reportUnknownVariableType, reportUnknownLambdaType]
    return prop_helper(data, *keys)  # pyright: ignore[reportArgumentType]
