from typing import Optional
from typing import Callable
from typing import Tuple
from typing import Any
from typing import Dict
import functools
import warnings


def deprecated_parameter(
        *,
        deadline: str,
        fix: str,
        func_name: Optional[str] = None,
        parameter_desc: str,
        match: Callable[[Tuple[Any, ...], Dict[str, Any]], bool],
        rewrite: Optional[
            Callable[[Tuple[Any, ...], Dict[str, Any]],
                     Tuple[Tuple[Any, ...], Dict[str, Any]]]] = None,
) -> Callable[[Callable], Callable]:
    """Marks a function parameter as deprecated.

    Also handles rewriting the deprecated parameter into the new signature.

    Args:
        deadline: The version where the parameter will be deleted (e.g. "v0.7").
        fix: A complete sentence describing what the user should be using
            instead of this particular function (e.g. "Use cos instead.")
        func_name: How to refer to the function.
            Defaults to `func.__qualname__`.
        parameter_desc: The name and type of the parameter being deprecated,
            e.g. "janky_count" or "janky_count keyword" or
            "positional janky_count".
        match: A lambda that takes args, kwargs and determines if the
            deprecated parameter is present or not. This determines whether or
            not the deprecation warning is printed, and also whether or not
            rewrite is called.
        rewrite: Returns new args/kwargs that don't use the deprecated
            parameter. Defaults to making no changes.

    Returns:
        A decorator that decorates functions with a parameter deprecation
            warning.
    """

    def decorator(func: Callable) -> Callable:

        @functools.wraps(func)
        def decorated_func(*args, **kwargs) -> Any:
            if match(args, kwargs):
                if rewrite is not None:
                    args, kwargs = rewrite(args, kwargs)

                qualname = (func.__qualname__
                            if func_name is None else func_name)
                warnings.warn(
                    f'The {parameter_desc} parameter of {qualname} was '
                    f'used but is deprecated.\n'
                    f'It will be removed in cirq {deadline}.\n'
                    f'{fix}\n',
                    DeprecationWarning,
                    stacklevel=2)

            return func(*args, **kwargs)

        return decorated_func

    return decorator