import inspect
from collections.abc import Callable
from functools import wraps
from typing import Any, Concatenate, ParamSpec, TypeVar

DataT = TypeVar('DataT')
P = ParamSpec('P')
R = TypeVar('R')


def make_data_last(
    f: Callable[Concatenate[DataT, P], R],
) -> Callable[Concatenate[DataT, P], R] | Callable[P, Callable[[DataT], R]]:
    sig = inspect.signature(f)
    number_of_positional_params = sum(1 for x in sig.parameters.values() if x.kind == inspect.Parameter.POSITIONAL_ONLY)

    @wraps(f)
    def inner(*args: P.args, **kwargs: P.kwargs) -> R | Callable[[DataT], R]:
        if len(args) == number_of_positional_params:
            return f(*args, **kwargs)  # pyright: ignore[reportUnknownVariableType, reportCallIssue]
        elif len(args) == number_of_positional_params - 1:
            return lambda data: f(data, *args, **kwargs)
        else:  # pragma: no cover
            raise ValueError(f'Unsupported number of arguments: {len(args)}')

    return inner  # pyright: ignore[reportReturnType]


DataT2 = TypeVar('DataT2')


def make_data_last2(
    f: Callable[Concatenate[DataT, DataT2, P], R],
) -> Callable[Concatenate[DataT, DataT2, P], R] | Callable[[DataT, DataT2], R] | Callable[P, Callable[[DataT], R]]:
    number_of_positional_params = sum(
        1 for x in inspect.signature(f).parameters.values() if x.kind == inspect.Parameter.POSITIONAL_ONLY
    )

    @wraps(f)
    def inner(*args: Any, **kwargs: Any) -> R | Callable[[DataT], R] | Callable[[DataT, DataT2], R]:
        if len(args) == number_of_positional_params:
            return f(*args, **kwargs)
        elif len(args) == number_of_positional_params - 1:
            return lambda data: f(data, *args, **kwargs)
        else:
            return lambda data1, data2: f(data1, data2, *args, **kwargs)

    return inner  # pyright: ignore[reportReturnType]
