# -*- coding: utf-8; -*-
#
# This file is part of Superdesk.
#
# Copyright 2013, 2014 Sourcefabric z.u. and contributors.
#
# For the full copyright and license information, please see the
# AUTHORS and LICENSE files distributed with this source code, or
# at https://www.sourcefabric.org/superdesk/license

import json
from uuid import uuid4

import superdesk
from werkzeug.datastructures import ImmutableMultiDict
from eve.utils import ParsedRequest
from superdesk.text_utils import get_text
from superdesk.services import BaseService
from superdesk.errors import SuperdeskApiError
from apps.auth import get_user


class ConceptItemsService(BaseService):
    """
    CRUD service for concept items
    """

    def get(self, req, lookup):
        if all([req, req.sort]):
            CASE_INSENSITIVE_COLLATION = '{"locale": "en", "strength":"1"}'
            COLLATION_SORT_ARGS = (
                "name",
                "-name",
                "definition_html",
                "-definition_html",
                "definition_text",
                "-definition_text",
            )

            # apply case insensitive collation only if collation was not provided explicitly
            # and sorting by `name`, `definition_html` or `definition_text` was used
            req_sort_args = [arg.strip() for arg in req.sort.split(",")]
            if [arg for arg in req_sort_args if arg in COLLATION_SORT_ARGS] and (
                not req.args or not req.args.get("collation")
            ):
                if req.args:
                    req.args = req.args.to_dict()
                    req.args["collation"] = CASE_INSENSITIVE_COLLATION
                    req.args = ImmutableMultiDict(req.args)
                else:
                    req.args = ImmutableMultiDict({"collation": CASE_INSENSITIVE_COLLATION})

            # if sort by (-)definition_html was used, we'll apply sort by definition_text under the hood
            if any([arg for arg in req_sort_args if arg in ("definition_html", "-definition_html")]):
                req_sort_args = ["definition_text" if arg == "definition_html" else arg for arg in req_sort_args]
                req_sort_args = ["-definition_text" if arg == "-definition_html" else arg for arg in req_sort_args]
                if req.args:
                    req.args = req.args.to_dict()
                    req.args["sort"] = ",".join(set(req_sort_args))
                    req.args = ImmutableMultiDict(req.args)
                else:
                    req.args = ImmutableMultiDict({"sort": ",".join(set(req_sort_args))})
                req.sort = req.args["sort"]

        return super().get(req, lookup)

    def on_create(self, docs):
        for doc in docs:
            self._validate_properties(doc)
            self._validate_language(doc)
            self._setup_created_by(doc)
            self._setup_group_id(doc)
            self._fill_definition_text(doc)

    def on_replace(self, doc, original):
        self._validate_properties(doc)
        self._validate_language(doc)
        self._validate_group_id(doc)
        self._setup_created_by(doc, original)
        self._setup_updated_by(doc)
        self._fill_definition_text(doc)

    def on_update(self, updates, original):
        if "cpnat_type" in updates:
            # we must validate `properties` even if they were not in payload
            if "properties" not in updates and "properties" in original:
                updates["properties"] = original["properties"]
        elif "properties" in updates:
            # we must validate `cpnat_type` even if it was not in payload
            updates["cpnat_type"] = original["cpnat_type"]
        if "cpnat_type" in updates:
            self._validate_properties(updates)

        if "language" in updates:
            self._validate_language(updates)

        if "definition_html" in updates:
            self._fill_definition_text(updates)

        self._setup_updated_by(updates)

    def _validate_language(self, doc):
        try:
            languages = superdesk.get_resource_service("vocabularies").get_languages()
            assert languages
        except AssertionError:
            raise SuperdeskApiError.badRequestError(
                message="Request is not valid",
                payload={"language": "Concept items requires 'languages' vocabulary to be set"},
            )

        languages_qcodes = [lang["qcode"] for lang in languages]

        if doc["language"] not in languages_qcodes:
            raise SuperdeskApiError.badRequestError(
                message="Request is not valid", payload={"language": "unallowed value '{}'".format(doc["language"])}
            )

    def _validate_group_id(self, doc):
        if "group_id" not in doc:
            raise SuperdeskApiError.badRequestError(
                message="Request is not valid", payload={"group_id": "This field is required"}
            )

    def _fill_definition_text(self, doc):
        doc["definition_text"] = get_text(doc["definition_html"], content="html", lf_on_block=True).strip()

    def _validate_properties(self, doc):
        getattr(self, "_validate_{}_properties".format(doc["cpnat_type"].rsplit(":", 1)[-1].lower()))(doc)

    def _validate_abstract_properties(self, doc):
        if "properties" in doc:
            raise SuperdeskApiError.badRequestError(
                message="Request is not valid",
                payload={"properties": "field is not supported when 'cpnat_type' is 'cpnat:abstract'"},
            )

    def _validate_event_properties(self, doc):
        # Step to implement validation(s):
        # 1) define schema_event in resource (https://iptc.org/std/NewsML-G2/guidelines/#more-real-world-entities)
        # 2) create cerberus Validator based on the schema_event
        # 3) validate doc['properties'] using cerberus Validator instance
        # TODO if we want to have a case insensitive sort, please, add a proper index

        raise SuperdeskApiError.badRequestError(
            message="Request is not valid", payload={"cpnat_type": "concept type 'cpnat:event' is not supported"}
        )

    def _validate_geoarea_properties(self, doc):
        raise SuperdeskApiError.badRequestError(
            message="Request is not valid", payload={"cpnat_type": "concept type 'cpnat:geoArea' is not supported"}
        )

    def _validate_object_properties(self, doc):
        raise SuperdeskApiError.badRequestError(
            message="Request is not valid", payload={"cpnat_type": "concept type 'cpnat:object' is not supported"}
        )

    def _validate_organisation_properties(self, doc):
        raise SuperdeskApiError.badRequestError(
            message="Request is not valid", payload={"cpnat_type": "concept type 'cpnat:organisation' is not supported"}
        )

    def _validate_person_properties(self, doc):
        raise SuperdeskApiError.badRequestError(
            message="Request is not valid", payload={"cpnat_type": "concept type 'cpnat:person' is not supported"}
        )

    def _validate_poi_properties(self, doc):
        raise SuperdeskApiError.badRequestError(
            message="Request is not valid", payload={"cpnat_type": "concept type 'cpnat:poi' is not supported"}
        )

    def _setup_created_by(self, doc, original=None):
        if not original:
            doc["created_by"] = get_user().get("_id")
        else:
            # for PUT request
            doc["created_by"] = original["created_by"]

    def _setup_updated_by(self, doc):
        doc["updated_by"] = get_user().get("_id")

    def _setup_group_id(self, doc):
        if "group_id" not in doc:
            doc["group_id"] = str(uuid4())
