# -*- coding: utf-8; -*-
#
# This file is part of Superdesk.
#
# Copyright 2013-2018 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 logging
import hashlib
import datetime
import re
from superdesk.utc import utc
import json
from copy import deepcopy
from superdesk import etree as sd_etree
from superdesk.io.registry import register_feed_parser
from superdesk.io.feed_parsers import FeedParser
from superdesk.io.iptc import subject_codes
from superdesk.metadata.item import ITEM_TYPE, CONTENT_TYPE, GUID_TAG, Priority
from superdesk.metadata.utils import generate_guid
from superdesk.metadata.item import ITEM_TYPE, CONTENT_TYPE, ITEM_URGENCY, ITEM_PRIORITY
from apps.archive.common import format_dateline_to_locmmmddsrc
from superdesk.utc import get_date
from flask import current_app as app
from superdesk import get_resource_service


logger = logging.getLogger(__name__)


class APMediaFeedParser(FeedParser):

    NAME = "ap_media"

    label = "AP Media API"

    direct_copy_properties = (
        "type",
        ITEM_URGENCY,
        "uri",
        "language",
        "pubstatus",
        "ednote",
        "headline",
        "slugline",
        "copyrightnotice",
    )

    # Mapping the received urgensy field to a priority value
    priority_map = {
        1: Priority.Flash.value,  # Flash
        2: Priority.Three_Paragraph.value,  # Bulletin
        3: Priority.Urgent.value,  # Urgent
        4: Priority.Ordinary.value,  # Routine
        5: Priority.Ordinary.value,  # Daily
        6: Priority.Ordinary.value,  # Release at will
        7: Priority.Ordinary.value,  # Weekday advance
        8: Priority.Ordinary.value,
    }  # Weekend advance

    slug_code_map = {  # Basketball
        "BKC-": "15008000",
        "BKL-": "15008000",
        "BKN-": "15008000",
        "BKW-": "15008000",
        "BKO-": "15008000",
        # Baseball
        "BBA-": "15007000",
        "BBC-": "15007000",
        "BBM-": "15007000",
        "BBN-": "15007000",
        "BBO-": "15007000",
        "BBY-": "15007000",
        # Boxing
        "BOX-": "15014000",
        # Motor racing
        "CAR-": "15039000",
        # Cycling
        "CYC-": "15019000",
        # American Football
        "FBL-": "15003000",
        "FBN-": "15003000",
        "FBO-": "15003000",
        # Figure skating
        "FIG-": "15025000",
        # Golf
        "GLF-": "15027000",
        # Ice Hockey
        "HKN-": "15031000",
        "HKO-": "15031000",
        # Horse Racing
        "RAC-": "15030000",
        # Soccer
        "SOC-": "15054000",
        # Tennis
        "TEN-": "15065000",
        # Cricket
        "CRI-": "15017000",
        # Rugby league
        "RGL-": "15048000",
    }

    RELATED_FIELD = None

    RENDITIONS_MAPPING = {
        "baseImage": "main",
    }

    def __init__(self):
        super().__init__()

    def can_parse(self, s_json):
        try:
            item = s_json.get("data", {}).get("item") and s_json.get("api_version")

            if item is None:
                return False

            return True
        except Exception:
            pass

        return False

    def datetime(self, string):
        try:
            return datetime.datetime.strptime(string, "%Y-%m-%dT%H:%M:%S+0000")
        except ValueError:
            return datetime.datetime.strptime(string, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=utc)

    def categorisation_mapping(self, in_item, item):
        """
        Function that maps categorisation fields may be overloaded if not required
        :param in_item:
        :param item:
        :return:
        """
        if in_item.get("subject"):
            categories = [{"qcode": c.get("code")} for c in in_item.get("subject") if c.get("rels") == ["category"]]
            if len(categories):
                item["anpa_category"] = [categories[0]]
                self._map_category_codes(item)

        self._map_sluglines_to_subjects(item=item)

    def _map_category_codes(self, item):
        """Map the category code that has been received to a more palatable value

        :param item:
        :return:
        """
        category_code_map = get_resource_service("vocabularies").find_one(req=None, _id="ap_category_map")
        if category_code_map:
            map = {c["ap_code"]: c["category_code"] for c in category_code_map["items"] if c["is_active"]}
            for category in item.get("anpa_category", []):
                category["qcode"] = map.get(category.get("qcode").lower(), map.get("default"))

    def _map_sluglines_to_subjects(self, item):
        """The first few characters of the slugline may match AP supplimetal categories this is used to set the subject code.

        :param item:
        :return:
        """
        if len(item.get("slugline", "")) > 4:
            qcode = self.slug_code_map.get(item["slugline"][:4])
            if qcode:
                try:
                    item["subject"] = []
                    item["subject"].append({"qcode": qcode, "name": subject_codes[qcode]})
                except KeyError:
                    logger.debug("Subject code '%s' not found" % qcode)

    def parse(self, s_json, provider=None):
        in_item = s_json.get("data", {}).get("item")
        nitf_item = s_json.get("nitf", {})
        item = {"guid": in_item.get("altids", {}).get("itemid") + ":" + str(in_item.get("version"))}
        item["source"] = provider.get("source") if provider else "AP"

        for copy_property in self.direct_copy_properties:
            if in_item.get(copy_property) is not None:
                item[copy_property] = in_item[copy_property]

        if in_item.get("version"):
            item["version"] = in_item["version"]

        if in_item.get("versioncreated"):
            item["versioncreated"] = self.datetime(in_item.get("versioncreated"))

        if in_item.get("firstcreated"):
            item["firstcreated"] = self.datetime(in_item.get("firstcreated"))

        if len(in_item.get("infosource", [])):
            item["original_source"] = ",".join([n.get("name") for n in in_item.get("infosource", [])])

        if in_item.get("datelinelocation"):
            cities = app.locators.find_cities()
            # Try to find a single matching city either by city and country or city country and state
            located = [
                c
                for c in cities
                if c["city"] == in_item.get("datelinelocation").get("city")
                and c["country"] == in_item.get("datelinelocation").get("countryname")
            ]
            if len(located) > 1:
                located = [
                    c
                    for c in cities
                    if c["city"] == in_item.get("datelinelocation").get("city")
                    and c["country"] == in_item.get("datelinelocation").get("countryname")
                    and c["state"] == in_item.get("datelinelocation").get("countryareaname")
                ]
            if len(located) == 1:
                item["dateline"] = dict()
                item["dateline"]["located"] = located[0]
                item["dateline"]["source"] = provider.get("source")
                item["dateline"]["text"] = format_dateline_to_locmmmddsrc(
                    item["dateline"]["located"], get_date(item["firstcreated"]), provider.get("source")
                )

        if len(in_item.get("bylines", [])):
            item["byline"] = ",".join(
                [
                    n.get("name")
                    if n.get("name")
                    else n.get("by", "") + (" ({})".format(n.get("title")) if n.get("title") else "")
                    for n in in_item.get("bylines", [])
                ]
            )
            if item.get("byline").startswith("By "):
                item["byline"] = item["byline"][3:]

        if len(in_item.get("usageterms", [])):
            item["usageterms"] = ", ".join([n for n in in_item.get("usageterms", [])])

        if in_item.get("type") == "picture":
            if in_item.get("renditions"):
                self._parse_renditions(in_item["renditions"], item, provider)

            if in_item.get("description_caption"):
                item["description_text"] = in_item.get("description_caption")
                item["archive_description"] = in_item.get("description_caption")

            if in_item.get("description_creditline"):
                item["credit"] = in_item.get("description_creditline")

            if in_item.get("photographer", {}).get("name"):
                item["byline"] = in_item.get("photographer", {}).get("name")

        if in_item.get("type") == "text":
            # Peel off the take key if possible
            if "," in item["slugline"]:
                item["anpa_take_key"] = item["slugline"].split(",")[1]
                item["slugline"] = item["slugline"].split(",")[0]
            if item["slugline"].startswith("BC-"):
                item["slugline"] = item["slugline"][3:]
            if item.get("ednote", "").startswith("Eds:"):
                item["ednote"] = item["ednote"][5:]
            if in_item.get("headline_extended"):
                item["abstract"] = in_item.get("headline_extended")

            self.categorisation_mapping(in_item, item)

            # Map the urgency to urgency and priority
            if in_item.get("urgency"):
                item[ITEM_URGENCY] = int(in_item["urgency"]) if in_item["urgency"] <= 5 else 5
                item[ITEM_PRIORITY] = self.priority_map.get(in_item["urgency"], 5)

            if nitf_item.get("body_html"):
                # item['body_html'] = sd_etree.clean_html_str(nitf_item.get('body_html'))
                item["body_html"] = nitf_item.get("body_html").replace('<block id="Main">', "").replace("</block>", "")

        if s_json.get("associations"):
            self._parse_associations(s_json["associations"], item, provider)

        return item

    def _parse_associations(self, associations, item, provider=None):
        related_id = getattr(self, "RELATED_ID", app.config.get("INGEST_AP_RELATED_ID"))
        if related_id:
            item["associations"] = {}
            for key, raw in associations.items():
                item["associations"]["{}--{}".format(related_id, key)] = self.parse(raw, provider)

    def _parse_renditions(self, renditions, item, provider=None):
        try:
            item["renditions"] = {}
            for dest, src in self.RENDITIONS_MAPPING.items():
                rend = renditions[src]
                item["renditions"][dest] = {
                    "href": rend["href"],
                    "mimetype": rend["mimetype"],
                    "width": rend["width"],
                    "height": rend["height"],
                }
        except KeyError:
            pass


register_feed_parser(APMediaFeedParser.NAME, APMediaFeedParser())
