# -*- coding: utf-8 -*-
"""Tornado plugin. Includes a path helper that allows you to pass an urlspec (path-handler pair)
object to `add_path`.
::

    from pprint import pprint

    from tornado.web import RequestHandler

    class HelloHandler(RequestHandler):
        def get(self):
            '''Get a greeting endpoint.
            ---
            description: Get a greeting
            responses:
                200:
                    description: A greeting to the client
                    schema:
                        $ref: '#/definitions/Greeting'
            '''
            self.write("hello")

    urlspec = (r'/hello', HelloHandler)
    spec.add_path(urlspec=urlspec)
    pprint(spec.to_dict()['paths'])
    # {'/hello': {'get': {'description': 'Get a greeting',
    #                     'responses': {200: {'description': 'A greeting to the '
    #                                                     'client',
    #                                         'schema': {'$ref': '#/definitions/Greeting'}}}}}}

"""
from __future__ import absolute_import
import inspect
import sys
from tornado.web import URLSpec

from apispec import Path
from apispec import utils
from apispec.exceptions import APISpecError


def setup(spec):
    """Setup for the plugin."""
    spec.register_path_helper(path_from_urlspec)

def path_from_urlspec(spec, urlspec, operations, **kwargs):
    """Path helper that allows passing a Tornado URLSpec or tuple."""
    if not isinstance(urlspec, URLSpec):
        urlspec = URLSpec(*urlspec)
    if operations is None:
        operations = {}
        for operation in _operations_from_methods(urlspec.handler_class):
            operations.update(operation)
    if not operations:
        raise APISpecError(
            'Could not find endpoint for urlspec {0}'.format(urlspec))
    params_method = getattr(urlspec.handler_class, list(operations.keys())[0])
    path = tornadopath2swagger(urlspec, params_method)
    extensions = _extensions_from_handler(urlspec.handler_class)
    operations.update(extensions)
    return Path(path=path, operations=operations)

def _operations_from_methods(handler_class):
    """Generator of operations described in handler's http methods

    :param handler_class:
    :type handler_class: RequestHandler descendant
    """
    for httpmethod in utils.PATH_KEYS:
        method = getattr(handler_class, httpmethod)
        operation_data = utils.load_yaml_from_docstring(method.__doc__)
        if operation_data:
            operation = {httpmethod: operation_data}
            yield operation

def tornadopath2swagger(urlspec, method):
    """Convert Tornado URLSpec to OpenAPI-compliant path.

    :param urlspec:
    :type urlspec: URLSpec
    :param method: Handler http method
    :type method: function
    """
    if sys.version_info >= (3, 3):
        args = list(inspect.signature(method).parameters.keys())[1:]
    else:
        if getattr(method, '__tornado_coroutine__', False):
            method = method.__wrapped__
        args = inspect.getargspec(method).args[1:]
    params = tuple('{{{}}}'.format(arg) for arg in args)
    try:
        path_tpl = urlspec.matcher._path
    except AttributeError:  # tornado<4.5
        path_tpl = urlspec._path
    path = (path_tpl % params)
    if path.count('/') > 1:
        path = path.rstrip('/?*')
    return path

def _extensions_from_handler(handler_class):
    """Returns extensions dict from handler docstring

    :param handler_class:
    :type handler_class: RequestHandler descendant
    """
    extensions = utils.load_yaml_from_docstring(handler_class.__doc__) or {}
    return extensions
