# -*- coding: utf-8 -*-

import json

from flask import request
from flask_api_auth.decorators import requires_auth
from flask_restful import abort, reqparse, fields, marshal_with
from flask_restful import inputs
from werkzeug.exceptions import NotFound

from distribution import adapters
from distribution import app
from distribution.adapters import models
from distribution.controllers.mixins import DatetimeFilterMixin

from .base import (
    BaseResource,
    GenericDetailResource,
    GenericListResource,
    ModelResource,
    LengthField,
)
from ..adapters.utils import resolve_adapter, format_from_accept_header, get_adapter
from ..models import get_model, ValidationError
from ..models.feed import FeedItem
from ..models.taxonomy import TaxonomyTopic
from ..models.sitemap_url import SitemapURL
from ..schemas.base import (
    BASIC_SCHEMA,
    POST_ITEM_SCHEMA,
    DELETE_SCHEMA,
    VALIDATION_SCHEMA,
)
from ..sequencer import FeedSequencer
from ..utils import (
    validate_rfc3339_date, validate_hours_day_format, validate_domain_string,
)

feed_req_parser = reqparse.RequestParser(bundle_errors=True)
feed_req_parser.add_argument('publication_datetime__from',
                             type=validate_rfc3339_date, location='args')
feed_req_parser.add_argument('publication_datetime__to',
                             type=validate_rfc3339_date, location='args')
feed_req_parser.add_argument('publication_datetime',
                             type=validate_hours_day_format, location='args')
feed_req_parser.add_argument('last_modified_datetime__from',
                             type=validate_rfc3339_date, location='args')
feed_req_parser.add_argument('last_modified_datetime__to',
                             type=validate_rfc3339_date, location='args')
feed_req_parser.add_argument('last_modified_datetime',
                             type=validate_hours_day_format, location='args')
feed_req_parser.add_argument('meta.last_update__from',
                             type=validate_rfc3339_date, location='args')
feed_req_parser.add_argument('meta.last_update__to',
                             type=validate_rfc3339_date, location='args')
feed_req_parser.add_argument('meta.last_update',
                             type=validate_hours_day_format, location='args')
feed_req_parser.add_argument('domain', type=validate_domain_string, location='args')
feed_req_parser.add_argument('limit', type=int, location='args')
feed_req_parser.add_argument('page', type=int, location='args')
feed_req_parser.add_argument('metadata', type=inputs.boolean, location='args')
feed_req_parser.add_argument('future_content', type=inputs.boolean, location='args')


class Feed(BaseResource, DatetimeFilterMixin):
    default_adapter = 'rss'

    @requires_auth()
    def get(self, adapter=None):
        output_adapter = None
        try:
            output_adapter = resolve_adapter(adapter, self.default_adapter)
        except (AttributeError, TypeError) as e:
            app.logger.exception(e)

        if output_adapter is None:
            # An adapter that doesn't exist was *explicitly* requested
            abort(404, message='Not Found')

        qs = feed_req_parser.parse_args()
        self.validate_get_feed_request(qs)

        arguments = request.args.to_dict()
        arguments = self.get_language_header(arguments, request.headers)
        result = FeedItem.manager.extended_search(arguments=arguments,
                                                  permissions=request.permissions)
        return output_adapter.render(**result)

    def execute_sequencer(self, schema):
        request_data = self.parse_request_data()
        self.validate_schema(request_data, schema)

        try:
            feed = FeedSequencer(request_data)
            items, errors = feed.execute()
            return {
                'succeeded': len(items),
                'failed': len(errors),
                'items': items,
                'errors': errors
            }
        except ValueError as e:
            app.logger.exception(e)
            abort(400, message='Invalid Request')

    def post(self):
        return self.execute_sequencer(BASIC_SCHEMA)

    def delete(self):
        return self.execute_sequencer(DELETE_SCHEMA)

    def get_language_header(self, arguments, header):
        if 'Accept-Language' in header and 'language' not in arguments:
            languages = header['Accept-Language'].split(',')
            for language in languages:
                language = language.split(';')
                if 'language' not in arguments:
                    arguments['language'] = language[0]
                else:
                    arguments['language'] = arguments['language'] + ' OR ' + language[0]

        return arguments


jsonapiitem_adapter = models.JSONAPIOutputAdapter(template_name='feed.jsonapiitem.json')


class FeedDetail(GenericDetailResource):
    model = FeedItem
    method_decorators = [requires_auth()]
    updatable_fields = [
        'title',
        'short_title',
        'abstract',
        'body',
        'contributors',
        'publication_datetime',
        'subjects',
        'series'
    ]

    def get(self, pk):
        obj = self.get_object(pk)
        if request.args.get('format') == 'jsonapi':
            return jsonapiitem_adapter.render([obj])
        elif "accept" in request.headers:
            accept = request.headers['accept']
            if format_from_accept_header(accept) == 'jsonapi':
                return jsonapiitem_adapter.render([obj])
        json_response = obj.clear_data
        if 'meta' in json_response and 'created_at' in json_response['meta']:
            json_response['created_at'] = json_response['meta']['created_at']
        json_response['localizations'] = obj.localizations
        return json_response

    def delete_object(self, obj):
        obj.manager.disable_many([obj.key])
        adapters.run_delete([obj.key])


class FeedDetailPost(BaseResource):

    def post(self):
        request_data = self.parse_request_data()
        self.validate_schema(request_data, POST_ITEM_SCHEMA)

        # The default value feeditem is for backwards compatibility
        model_name = request_data.get('model', 'feeditem')
        model = get_model(model_name)

        try:
            item = model(request_data['item'], request_data['domain'])
        except ValidationError as e:
            return e.message, 422

        try:
            existing_item = model.manager.get(item.key)
        except NotFound:
            existing_item = None

        if not existing_item or existing_item.is_deleted():
            if existing_item:
                model.manager.delete([existing_item.key])
            item.save()

            return item.data, 201

        message = '{} with key {} already exists, use the PUT method to update the item'.format(
            model_name, item.key)
        abort(409, message=message)


class ValidateFeed(BaseResource):
    """Validates the given JSON using the cerberus schema."""
    req_parser = reqparse.RequestParser()
    req_parser.add_argument('strict', type=inputs.boolean, location='args',
                            help='Enable strict validation for all components. '
                                 'Allowed values: true / false.')

    resource_fields = {
        'succeeded': LengthField(attribute='result', default=0),
        'failed': LengthField(attribute='errors', default=0),
        'items': fields.List(fields.String, default=[], attribute='result'),
        'errors': fields.Raw(default=[]),
    }

    @requires_auth()
    @marshal_with(resource_fields)
    def post(self):
        qs = ValidateFeed.req_parser.parse_args()
        request_data = self.parse_request_data()
        is_valid, validation_errors = self.validate_schema(request_data, VALIDATION_SCHEMA,
                                                           abort_on_failure=False)
        if not is_valid:
            return {
                'errors': validation_errors
            }
        try:
            feed = FeedSequencer(request_data)
            items, errors = feed.execute(strict_validation=qs['strict'])
            return {
                'result': items,
                'errors': errors
            }
        except ValueError as e:
            app.logger.exception(e)
            abort(400, message='Invalid Request')


class TaxonomyTopicDetail(GenericDetailResource):
    """
    This resource represents the taxonomy topic details and update /
    delete operations.

    """
    model = TaxonomyTopic
    updatable_fields = ['name', 'topic_type', 'children', 'updated_datetime']


class TaxonomyTopicList(GenericListResource):
    """
    This resource represents the taxonomy topic list.
    POST requests to this resource will create a new taxonomy topics.

    """
    model = TaxonomyTopic
    enable_multipost = True


class PublicAdapterDetail(ModelResource):
    model = FeedItem

    def get(self, adapter, slug):
        output_adapter = get_adapter(adapter, is_public=True)

        if output_adapter is None:
            abort(404, message='Adapter not found')

        obj = self.get_object_by_field('uri', slug)

        return output_adapter.render(obj)


class DeletedFeed(BaseResource, DatetimeFilterMixin):
    """Return deleted articles."""

    @requires_auth()
    def get(self):
        output_adapter = get_adapter("jsonapi")
        # Return only the following fields for deleted articles
        deleted_fields = [
            "id",
            "content_type",
            "domain",
            "last_modified_datetime",
            "publication_datetime",
            "last_update"
        ]

        qs = feed_req_parser.parse_args()
        self.validate_get_feed_request(qs)

        arguments = request.args.to_dict()
        arguments['deleted'] = True
        result = FeedItem.manager.extended_search(arguments=arguments,
                                                  permissions=request.permissions)
        return output_adapter.render(fields=deleted_fields, **result)


class SitemapURLDetail(GenericDetailResource):
    """
    This resource represents the model sitemap url details and update /
    delete operations.

    """
    model = SitemapURL
    method_decorators = [requires_auth()]
    updatable_fields = [
        'title',
        'url',
        'canonical_url',
        'change_frequency',
        'last_modified_datetime',
        'publication_datetime',
        'genres',
        'language',
        'content_type',
        'images',
        'parent',
        'keywords',
        'priority'
    ]


class SitemapURLList(GenericListResource):
    """
    This resource represents the taxonomy topic list.
    POST requests to this resource will create a new taxonomy topics.

    """
    model = SitemapURL
    method_decorators = [requires_auth()]
