# -*- coding: utf-8 -*-
from copy import deepcopy

from flask import url_for, request

from distribution import app
from distribution.utils import string_to_utc_date, DEFAULT
from distribution.models.taxonomy import TaxonomyTopic


class FeedItemSerializer(object):
    """
    Class used to contain all logic needed to create and display
    a feed item. Use this class before adding more logic to the
    template file.

    """
    name = 'rss'

    def __init__(self, item, fields):
        self._item = item

    @property
    def data(self):
        return self._item.data

    @property
    def id(self):
        return self._item.key

    @property
    def publication_datetime(self):
        return string_to_utc_date(self.data['publication_datetime'])

    @property
    def created_at(self):
        return string_to_utc_date(self.data['meta']['created_at'])

    @property
    def last_modified_datetime(self):
        return string_to_utc_date(self.data.get('last_modified_datetime'))

    @property
    def taxonomy_topic_uuids(self):
        try:
            return self._topics
        except AttributeError:
            pass

        taxonomy_topic_uuids = []
        for key in app.config['TAXONOMY_TOPIC_TYPES']:
            taxonomy_topic_uuids += self.data[key]
        self._topics = TaxonomyTopic.manager.mget(taxonomy_topic_uuids)

        return self._topics

    @property
    def categories(self):
        try:
            return self._categories
        except AttributeError:
            pass

        keywords = [k.strip() for k in self.data['keywords']]
        topics = [topic.data['name'].strip() for topic in
                  self.taxonomy_topic_uuids]

        self._categories = filter(None, keywords + topics)
        return self._categories

    @property
    def photos(self):
        try:
            return self._photos
        except AttributeError:
            pass

        photos_by_uri = {}
        try:
            for gallery in self.data['photo_galleries']:
                for photo in gallery:
                    photos_by_uri.setdefault(photo['uri'], photo)

            for photo in self.data['images']:
                photos_by_uri.setdefault(photo['uri'], photo)

        except Exception:
            app.logger.error('Article with id: {} has wrong photo_galleries '
                             'or images schema.'.format(self.id))

        self._photos = photos_by_uri.values()
        return self._photos

    @property
    def creator(self):
        try:
            return self.data['contributors'][0]['name']
        except (IndexError, KeyError):
            return None

    @property
    def contributors(self):
        try:
            return [c['name'] for c in self.data['contributors'][1:]]
        except (AttributeError, KeyError, TypeError):
            return []

    @property
    def description_simple(self):
        try:
            body = self.data['alt_bodies']['text']['body']
            return body if body is not None else self.abstract
        except KeyError:
            return self.abstract

    @property
    def abstract(self):
        return self.data['abstract'] or self.data['title']


class JSONFeedItemSerializer(object):
    """
    Serializer class that converts a FeedItem list to a list
    of raw dictionaries

    """
    name = 'json'

    @staticmethod
    def serialize(items, fields):
        result = []

        for item in items:
            if JSONFeedItemSerializer.name not in item.excluded_serializers:
                data = deepcopy(item.data)
                if data['content_type'] == 'gallery':
                    for image in data['images']:
                        image['_links'] = {
                            'self': '/api/v1/item/{}/'.format(image['id'])
                        }

                data['pointer'] = {
                    'id': data.pop('id'),
                    'content_type': data.pop('content_type'),
                    'uri': data.pop('uri', None),
                }
                if 'meta' in data and 'created_at' in data['meta']:
                    data['created_at'] = data['meta']['created_at']

                for key in app.config['TAXONOMY_TOPIC_TYPES']:
                    if key in data:
                        topics = TaxonomyTopic.manager.mget(data[key])
                        data[key] = [{'uuid': t.data['uuid'], 'name': t.data['name']}
                                     for t in topics]
                data['localizations'] = item.localizations
                result.append(data)
        return result


class FeedSerializer(object):
    """
    Serializer class that controls how individual results are
    serialized.

    """
    item_serializer = FeedItemSerializer

    def __init__(self, item_serializer=DEFAULT):
        """
        Initializes the class that is going to be used as context.

        :param item_serializer: class to be used to serialize items.

        """
        if item_serializer is not DEFAULT:
            self.item_serializer = item_serializer

    def serialize(self, items, fields):
        return self.serialize_items(items, fields)

    def serialize_items(self, items, fields):
        """
        Returns a generator that yields items as instances of
        self.item_serializer.

        :param items: the list of items to serialize

        """
        result = []
        for item in items:
            if self.item_serializer.name not in item.excluded_serializers:
                result.append(self.item_serializer(item, fields))
        return result


class JSONAPIBaseSerializer(object):
    def __init__(self, item, fields=None):
        self._item = item
        self._fields = fields
        self.content_type = None
        self.attributes = {}
        self.links = {}
        self.meta = {}
        self.relationships = {}
        self.included = set()

        self.process()

    def process(self):
        pass  # Override me

    @staticmethod
    def link_resource(view="feeddetail", **kwargs):
        return url_for(view,
                       _external=True,
                       _scheme=request.headers.get(app.config['HTTP_SCHEME_HEADER'], 'http'),
                       **kwargs)

    @property
    def resource(self):
        data = {"id": self.id, "type": self.content_type}

        if self.attributes:
            data["attributes"] = self.attributes

        if self.links:
            data["links"] = self.links

        if self.relationships:
            data["relationships"] = self.relationships

        return data


class JSONAPITaxonomyTopicSerializer(JSONAPIBaseSerializer):
    fields = ["name", "topic_type"]

    def process(self):
        self.content_type = "taxonomy_topic"
        self.id = self._item.key
        self.attributes = {key: self.data[key] for key in self.fields}
        self.links = {"self": self.link_resource(view="taxonomytopicdetail",
                                                 pk=self.id)}

    @property
    def data(self):
        return self._item.data


class JSONAPIItemSerializer(JSONAPIBaseSerializer, FeedItemSerializer):
    generated_fields = ['localizations', 'created_at']
    name = 'jsonapi'

    @property
    def fields(self):
        fields = getattr(self, '_fields', None)
        if fields is None:
            requested_fields = request.args.getlist('fields[{}]'.format(self.content_type)) or []
            requested_fields += request.args.getlist('fields') or []
            if requested_fields:
                all_fields = set(self.data.keys() + self.generated_fields)
                if 'all' in requested_fields:
                    fields = all_fields
                else:
                    requested_fields = reduce(
                        lambda x, y: x + y, [f.split(',') for f in requested_fields])
                    fields = set(requested_fields) & all_fields
            else:
                fields = self._item.default_fields
        return fields

    def process(self):
        self.content_type = self.data['content_type']
        self.attributes = {}
        for key in self.fields:
            if key in self.data:
                self.attributes[key] = self.data[key]
            else:
                if hasattr(self._item, key):
                    self.attributes[key] = getattr(self._item, key)
        self.links = {"self": self.link_resource(pk=self.id, format='jsonapi')}

        # content_type custom processing
        getattr(self, "process_{}_fields".format(self.content_type),
                lambda: None)()

        if 'meta' in self.data and 'created_at' in self.data['meta'] and\
           'created_at' in self.fields:
                self.attributes['created_at'] = self.data['meta']['created_at']
        self.process_taxonomic_fields()
        self.process_localizations_field()
        self.process_relationships()

    def process_relationships(self):
        if hasattr(self._item, 'relationships'):
            self.relationships.update(**self._item.relationships(format='jsonapi'))

    def process_localizations_field(self):
        if 'localizations' not in self.fields:
            return
        self.attributes['localizations'] = self._item.localizations

    def process_gallery_fields(self):
        for image in self.attributes.get('images', []):
            image["link"] = self.link_resource(pk=image['id'])

    def process_taxonomic_fields(self):
        taxonomic_fields = set(self.fields) & set(app.config['TAXONOMY_TOPIC_TYPES'])

        if not taxonomic_fields:
            return

        # Collect uuids
        taxonomy_topic_uuids = set()
        for key in taxonomic_fields:
            taxonomy_topic_uuids.update(self.attributes[key])

        # Resolve uuids to topics in one call, then serialize them
        serialized_topic_by_uuid = {}
        for topic in TaxonomyTopic.manager.mget(taxonomy_topic_uuids):
            serialized_topic = JSONAPITaxonomyTopicSerializer(topic)
            serialized_topic_by_uuid[serialized_topic.id] = serialized_topic

        # Make them available for inclusion
        self.included.update(serialized_topic_by_uuid.values())

        # Relate them
        for key in taxonomic_fields:
            data = []
            # Remove field from attributes and add them to relationships
            for uuid in self.attributes.pop(key):
                serialized_topic = serialized_topic_by_uuid[uuid]
                data.append({"type": serialized_topic.content_type,
                             "id": serialized_topic.id})

            # Add the relationship.
            # Currently we don't have relationship endpoints to link this to.
            self.relationships[key] = {'data': data}


class JSONAPISerializer(FeedSerializer):
    item_serializer = JSONAPIItemSerializer


class SitemapURLSerializer(object):

    name = 'sitemap_url'

    def __init__(self, item, fields):
        self._item = item
        self._genres = None

    @property
    def data(self):
        return self._item.data

    @property
    def url(self):
        return self.data['url']

    @property
    def last_modification(self):
        return string_to_utc_date(self.data['last_modified_datetime']).isoformat()

    @property
    def publication_datetime(self):
        return string_to_utc_date(self.data['publication_datetime']).isoformat()

    @property
    def language(self):
        return self.data['language'][:2]

    @property
    def images(self):
        return self.data['images']

    @property
    def priority(self):
        return self.data.get('priority', None)

    @property
    def change_frequency(self):
        return self.data.get('change_frequency', None)

    @property
    def genres(self):
        if self._genres is None and self.data['genres']:
            topics = TaxonomyTopic.manager.mget(self.data['genres'])
            self._genres = ', '.join(t.data['name'] for t in topics)
        return self._genres

    @property
    def keywords(self):
        return ', '.join(self.data['keywords'])

    @property
    def localizations(self):
        localizations = self._item.localizations
        if len(localizations) == 0:
            return []

        localizations.append(self._item.get_language_dict())
        return self._item.localizations
