import typing
import collections


def route(url: str, methods: typing.Iterable[str] = ("GET", "HEAD"), **kwargs):
    """
    A companion function to the RouteGroup class. This follows :meth:`.Blueprint.route` in 
    terms of arguments, and marks a function as a route inside the class.
    
    This will return the original function, with some attributes attached:
    
        - ``in_group``: Marks the function as in the route group.
        - ``rg_delegate``: Internal. The type of function inside the group this is.
        - ``route_kwargs``: Keyword arguments to provide to ``wrap_route``.
        - ``route_url``: The routing URL to provide to ``add_route``.
        - ``route_methods``: The methods for the route.
        - ``route_hooks``: A defaultdict of route-specific hooks.
        
    Additionally, the following methods are added.
    
        - ``hook``: A decorator that adds a hook of type ``type_``.
        - ``before_request``: A decorator that adds a ``pre`` hook.
        - ``after_request``: A decorator that adds a ``post`` hook.
    
    .. versionadded:: 2.1.1
    
    .. versionchanged:: 2.1.3
    
        Added the ability to add route-specific hooks.
        
    .. versionchanged:: 2.2.0
        
        Now accepts an already edited function as the function to decorate - this will add a new \
        routing url and method pair to the :attr:`.Route.routes`.

    .. versionchanged:: 2.2.2

        Default methods changed to GET and HEAD.
    
    :param url: The routing URL of the route.
    :param methods: An iterable of methods for the route.
    """

    def inner(func):
        # add the required attrs which are used on a scan later
        func.in_group = True
        func.rg_delegate = "route"
        func.route_kwargs = kwargs

        # try and append to the routes
        # failing that, create a new list
        try:
            func.routes.append((url, methods))
        except AttributeError:
            func.routes = [(url, methods)]

        if not hasattr(func, "route_hooks"):
            func.route_hooks = collections.defaultdict(lambda: [])

            # helper for route-specific hooks.
            def hook(type_: str):
                def _inner2(hookfunc):
                    func.route_hooks[type_].append(hookfunc)
                    return hookfunc

                return _inner2

            func.hook = hook
            func.before_request = hook("pre")
            func.after_request = hook("post")

        return func

    return inner