# Standard library imports
import sys
import warnings

# Local imports
from uplink import decorators
from uplink.converters import keys, interfaces

__all__ = ["json", "from_json", "schema"]


class ReturnType(object):
    def __init__(self, decorator, type_):
        self._decorator = decorator
        self._type_ = type_

    @property
    def type(self):
        return self._type_

    @staticmethod
    def with_decorator(old, decorator):
        old_type = None if old is None else old.type
        return ReturnType(decorator, decorator.return_type or old_type)

    def with_strategy(self, strategy):
        return CallableReturnType(self._decorator, self._type_, strategy)

    def is_applicable(self, decorator):
        return self._decorator is decorator


class CallableReturnType(ReturnType):
    def __init__(self, decorator, type_, strategy):
        super(CallableReturnType, self).__init__(decorator, type_)
        self._strategy = strategy

    def __call__(self, *args, **kwargs):
        return self._strategy(*args, **kwargs)


class _ReturnsBase(decorators.MethodAnnotation):
    @property
    def return_type(self):  # pragma: no cover
        raise NotImplementedError

    def _make_strategy(self, converter):  # pragma: no cover
        pass

    def _modify_request_definition(self, definition, kwargs):
        super(_ReturnsBase, self)._modify_request_definition(definition, kwargs)
        definition.return_type = ReturnType.with_decorator(
            definition.return_type, self
        )

    def _get_converter(self, request_builder, return_type):  # pragma: no cover
        return request_builder.get_converter(
            keys.CONVERT_FROM_RESPONSE_BODY, return_type.type
        )

    def modify_request(self, request_builder):
        return_type = request_builder.return_type
        if not return_type.is_applicable(self):
            return

        converter = self._get_converter(request_builder, return_type)
        if converter is None:
            return

        # Found a converter that can handle the return type.
        request_builder.return_type = return_type.with_strategy(
            self._make_strategy(converter)
        )


class JsonStrategy(object):
    # TODO: Consider moving this under json decorator
    # TODO: Support JSON Pointer (https://tools.ietf.org/html/rfc6901)

    def __init__(self, converter, key=()):
        self._converter = converter

        if not isinstance(key, (list, tuple)):
            key = (key,)
        self._key = key

    def __call__(self, response):
        content = response.json()
        for name in self._key:
            content = content[name]
        content = self._converter(content)
        return content


# noinspection PyPep8Naming
class json(_ReturnsBase):
    """
    Specifies that the decorated consumer method should return a JSON
    object.

    .. code-block:: python