from collections.abc import Callable
from typing import Any, Concatenate, ParamSpec, TypeVar, overload

R = TypeVar('R')
P = ParamSpec('P')
T = TypeVar('T')
A = TypeVar('A')
B = TypeVar('B')
C = TypeVar('C')
D = TypeVar('D')
E = TypeVar('E')
F = TypeVar('F')
G = TypeVar('G')
H = TypeVar('H')
I = TypeVar('I')
J = TypeVar('J')
K = TypeVar('K')
L = TypeVar('L')
M = TypeVar('M')
N = TypeVar('N')
O = TypeVar('O')


@overload
def partial(fn: Callable[Concatenate[A, P], R], argA: A, /) -> Callable[P, R]: ...


@overload
def partial(fn: Callable[Concatenate[A, B, P], R], argA: A, argB: B, /) -> Callable[P, R]: ...


@overload
def partial(fn: Callable[Concatenate[A, B, C, P], R], argA: A, argB: B, argC: C, /) -> Callable[P, R]: ...


@overload
def partial(fn: Callable[Concatenate[A, B, C, D, P], R], argA: A, argB: B, argC: C, argD: D, /) -> Callable[P, R]: ...


@overload
def partial(
    fn: Callable[Concatenate[A, B, C, D, E, P], R],
    argA: A,
    argB: B,
    argC: C,
    argD: D,
    argE: E,
    /,
) -> Callable[P, R]: ...


@overload
def partial(
    fn: Callable[Concatenate[A, B, C, D, E, F, P], R],
    argA: A,
    argB: B,
    argC: C,
    argD: D,
    argE: E,
    argF: F,
    /,
) -> Callable[P, R]: ...


@overload
def partial(
    fn: Callable[Concatenate[A, B, C, D, E, F, G, P], R],
    argA: A,
    argB: B,
    argC: C,
    argD: D,
    argE: E,
    argF: F,
    argG: G,
    /,
) -> Callable[P, R]: ...


@overload
def partial(
    fn: Callable[Concatenate[A, B, C, D, E, F, G, H, P], R],
    argA: A,
    argB: B,
    argC: C,
    argD: D,
    argE: E,
    argF: F,
    argG: G,
    argH: H,
    /,
) -> Callable[P, R]: ...


@overload
def partial(
    fn: Callable[Concatenate[A, B, C, D, E, F, G, H, I, P], R],
    argA: A,
    argB: B,
    argC: C,
    argD: D,
    argE: E,
    argF: F,
    argG: G,
    argH: H,
    argI: I,
    /,
) -> Callable[P, R]: ...


@overload
def partial(
    fn: Callable[Concatenate[A, B, C, D, E, F, G, H, I, J, P], R],
    argA: A,
    argB: B,
    argC: C,
    argD: D,
    argE: E,
    argF: F,
    argG: G,
    argH: H,
    argI: I,
    argJ: J,
    /,
) -> Callable[P, R]: ...


@overload
def partial(
    fn: Callable[Concatenate[A, B, C, D, E, F, G, H, I, J, K, P], R],
    argA: A,
    argB: B,
    argC: C,
    argD: D,
    argE: E,
    argF: F,
    argG: G,
    argH: H,
    argI: I,
    argJ: J,
    argK: K,
    /,
) -> Callable[P, R]: ...


@overload
def partial(
    fn: Callable[Concatenate[A, B, C, D, E, F, G, H, I, J, K, L, P], R],
    argA: A,
    argB: B,
    argC: C,
    argD: D,
    argE: E,
    argF: F,
    argG: G,
    argH: H,
    argI: I,
    argJ: J,
    argK: K,
    argL: L,
    /,
) -> Callable[P, R]: ...


@overload
def partial(
    fn: Callable[Concatenate[A, B, C, D, E, F, G, H, I, J, K, L, M, P], R],
    argA: A,
    argB: B,
    argC: C,
    argD: D,
    argE: E,
    argF: F,
    argG: G,
    argH: H,
    argI: I,
    argJ: J,
    argK: K,
    argL: L,
    argM: M,
    /,
) -> Callable[P, R]: ...


@overload
def partial(
    fn: Callable[Concatenate[A, B, C, D, E, F, G, H, I, J, K, L, M, N, P], R],
    argA: A,
    argB: B,
    argC: C,
    argD: D,
    argE: E,
    argF: F,
    argG: G,
    argH: H,
    argI: I,
    argJ: J,
    argK: K,
    argL: L,
    argM: M,
    argN: N,
    /,
) -> Callable[P, R]: ...


@overload
def partial(
    fn: Callable[Concatenate[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P], R],
    argA: A,
    argB: B,
    argC: C,
    argD: D,
    argE: E,
    argF: F,
    argG: G,
    argH: H,
    argI: I,
    argJ: J,
    argK: K,
    argL: L,
    argM: M,
    argN: N,
    argO: O,
    /,
) -> Callable[P, R]: ...


def partial(function: Callable[..., T], *args: Any) -> Callable[..., T]:
    """
    Returns a closure with args pre-set.

    Parameters
    ----------
    function : Callable[..., T]
        The function to partialize (positional-only).
    *args : Any
        The arguments to pre-set (positional-only).

    Returns
    -------
    Callable[..., T]
        The resulting closure.

    Examples
    --------
    Data first:
    >>> def sum3(a: int, b: int, c: int) -> int:
    ...     return a + b + c
    >>> p = R.partial(sum3, 1, 2)
    >>> p(3)
    6
    >>> p2 = R.partial(sum3, 1)
    >>> p2(2, 3)
    6

    """

    def returned(*args2: Any, **kwargs: Any) -> T:
        return function(*args, *args2, **kwargs)

    return returned
