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

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


@overload
def get(data: dict[Key, T], key: Key, default: U = None) -> T | U: ...


@overload
def get(data: Sequence[T], key: int, default: U = None) -> T | U: ...


@overload
def get(key: int, /, default: U = None) -> Callable[[dict[int, T] | Sequence[T]], T | U]: ...


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


def get(*args: Any, default: Any = None) -> Any | None | Callable[[Any], Any]:  # pyright: ignore[reportInconsistentOverload]
    """
    Gets value under key from a dict, or under index from a sequence.

    Accepts optional default.

    Parameters
    ----------
    thing: dict[Key, T] | Sequenct[T]
        Dict or sequence to get value from.
    key: Key | int
        Key to get value from.
    default: U
        Default value to return if key is not found.

    Returns
    -------
    T | U
        Value from the dict or sequence, or default if key is not found.

    Examples
    --------
    Data first:
    >>> R.get([], 0) is None
    True
    >>> R.get([1], 0)
    1
    >>> R.get([1], 2, 5)
    5
    >>> R.get({'a': 3}, 'a', 5)
    3
    >>> R.get({'a': 3}, 'aa', 5)
    5
    >>> R.get({'a': 3}, 'aa', 'ab')
    'ab'

    Data last:
    >>> R.get(1)([9, 8])
    8

    """
    if len(args) == 2:
        data, key = args
    elif len(args) == 1:
        return lambda x: get(x, args[0], default)
    elif len(args) == 3:
        data, key, default = args
    else:  # pragma: no cover
        raise ValueError('get() takes 1, 2 or 3 arguments')
    try:
        return data[key]
    except (KeyError, IndexError):
        return default
