import json
import warnings

from coreapi.document import Object, Link

from rest_framework import exceptions
from rest_framework.permissions import AllowAny
from rest_framework.renderers import CoreJSONRenderer
from rest_framework.response import Response
from rest_framework.schemas import SchemaGenerator, AutoSchema as DRFAuthSchema
from rest_framework.views import APIView

from rest_framework_swagger import renderers


class AutoSchema(DRFAuthSchema):

    def get_link(self, path, method, base_url):
        link = super(AutoSchema, self).get_link(path, method, base_url)
        try:
            serializer = self.view.get_serializer()
        except Exception:
            serializer = None
            warnings.warn('{}.get_serializer() raised an exception during '
                          'schema generation. Serializer fields will not be '
                          'generated for {} {}.'
                          .format(self.view.__class__.__name__, method, path))

        link.__dict__['deprecated'] = getattr(self.view, 'deprecated', False)

        # auto-generate a topic/tag for the serializer based on its model
        if hasattr(self.view, 'swagger_topic'):
            link.__dict__['topic'] = str(self.view.swagger_topic).title()
        elif serializer and hasattr(serializer, 'Meta'):
            link.__dict__['topic'] = str(
                serializer.Meta.model._meta.verbose_name_plural
            ).title()
        elif hasattr(self.view, 'model'):
            link.__dict__['topic'] = str(self.view.model._meta.verbose_name_plural).title()
        else:
            warnings.warn('Could not determine a Swagger tag for path {}'.format(path))
        return link

    def get_description(self, path, method):
        self.view._request = self.view.request
        setattr(self.view.request, 'swagger_method', method)
        description = super(AutoSchema, self).get_description(path, method)
        return description


class SwaggerSchemaView(APIView):
    _ignore_model_permissions = True
    exclude_from_schema = True
    permission_classes = [AllowAny]
    renderer_classes = [
        CoreJSONRenderer,
        renderers.OpenAPIRenderer,
        renderers.SwaggerUIRenderer
    ]

    def get(self, request):
        generator = SchemaGenerator(
            title='Ansible Tower API',
            patterns=None,
            urlconf=None
        )
        schema = generator.get_schema(request=request)
        # python core-api doesn't support the deprecation yet, so track it
        # ourselves and return it in a response header
        _deprecated = []

        # By default, DRF OpenAPI serialization places all endpoints in
        # a single node based on their root path (/api).  Instead, we want to
        # group them by topic/tag so that they're categorized in the rendered
        # output
        document = schema._data.pop('api')
        for path, node in document.items():
            if isinstance(node, Object):
                for action in node.values():
                    topic = getattr(action, 'topic', None)
                    if topic:
                        schema._data.setdefault(topic, Object())
                        schema._data[topic]._data[path] = node

                    if isinstance(action, Object):
                        for link in action.links.values():
                            if link.deprecated:
                                _deprecated.append(link.url)
            elif isinstance(node, Link):
                topic = getattr(node, 'topic', None)
                if topic:
                    schema._data.setdefault(topic, Object())
                    schema._data[topic]._data[path] = node

        if not schema:
            raise exceptions.ValidationError(
                'The schema generator did not return a schema Document'
            )

        return Response(
            schema,
            headers={'X-Deprecated-Paths': json.dumps(_deprecated)}
        )
