from typing import Callable
import inspect
import functools


def _process_input(wrapped_function: Callable):  # coverage: ignore
    """
    Allow `run_test` to take a single positional argument that is a
    `list` or `tuple` in lieu of using multiple positional/keyword
    arguments as usual.  If `len` of this argument returns `3`, then
    it assumes that `kwargs` is an empty `dict` and that the expected
    result/outcome is the last item.
    """

    def decorator(wrapped_function: Callable):
        wrapped_signature = inspect.signature(wrapped_function)

        @functools.wraps(wrapped_function)
        def wrapper(*args, **kwargs):
            arguments = wrapped_signature.bind(*args, **kwargs).arguments
            if (
                len(args) == 1
                and len(kwargs) == 0
                and isinstance(args[0], (list, tuple))
            ):
                inputs = args[0]
                if len(inputs) not in (3, 4):
                    raise RuntimeError(f"{args} is an invalid input to run_test.")
                new_kwargs = {"func": inputs[0], "args": inputs[1]}
                new_kwargs["kwargs"] = inputs[2] if len(inputs) == 4 else {}
                new_kwargs["expected_outcome"] = (
                    inputs[3] if len(inputs) == 4 else inputs[2]
                )
            else:
                new_kwargs = {argname: argval for argname, argval in arguments.items()}
            return wrapped_function(**new_kwargs)

        return wrapper

    return decorator(wrapped_function)