import time
from typing import Dict, List, Union

import feedparser
from flask import current_app as app

from feedrsub.ingestion.parsers.parser_base import ParserBase
from feedrsub.ingestion.parsers.rss.rss_item_parser import RssItemParser
from feedrsub.models.feed import Feed
from feedrsub.utils.exceptions import ParserException


class RssParser(ParserBase):
    item_parser_type = RssItemParser

    @staticmethod
    def parse_raw_data(
        raw_data: Union[str, bytes], encoding: str = "utf-8", headers: Dict = None
    ) -> Dict:
        """
        Loads the raw RSS/Atom XML data.
        Returns feedparser Dict.
        https://pythonhosted.org/feedparser/

        :param raw_data: RSS/Atom XML feed
        :type raw_data: str
        :param encoding: Character encoding of raw_data
        :type encoding: str
        :return: Dict
        """
        if not encoding:
            encoding = "utf-8"

        h = {}
        if headers:
            if isinstance(headers, dict):
                h = headers
            else:
                try:
                    h.update({k.lower(): v for (k, v) in headers.items()})
                except:
                    pass

        try:
            start = time.perf_counter()

            if isinstance(raw_data, str):
                raw_data: bytes = raw_data.encode(encoding)

            content_length = len(raw_data)

            # We want to pass data into feedparser as bytes
            data = feedparser.parse(raw_data, response_headers=h)

            dur = int((time.perf_counter() - start) * 1000)
            app.logger.info(
                "RAW_DATA_PARSE: type=rss_parser size=%s dur=%sms", content_length, dur
            )

            return data
        except Exception as e:
            raise ParserException(f"Could not parse RSS data: {e}") from e

    def update_feed_details(self, data: Dict, feed: Feed) -> None:
        """
        Updates the title and description of a feed if they have been sent
        with the notification.

        :param data: parsed XML feed
        :type data: Dict
        :param feed: The Feed to be updated
        :type feed: Feed
        :return: Feed object
        """
        feed_data = data.get("feed")
        if feed_data:
            if feed_data.get("title"):
                feed.title = feed_data["title"]

            if feed_data.get("subtitle"):
                feed.description = feed_data.get("subtitle")
            elif feed_data.get("description"):
                feed.description = feed_data.get("description")

    def update_feed_version(self, data: Dict, feed: Feed) -> None:
        """
        Sets the format of the Feed.

        :param data: parsed XML feed
        :type data: Dict
        :param feed: A Feed object
        :type feed: Feed
        :return: Feed object
        """
        feed.feed_format = data.get("version", "RSS/Atom")

    def get_items(self, data: Dict) -> List[Dict]:
        """
        Gets a list of items from the data.

        :param data: parsed XML feed
        :type data: Dict
        :return: list
        """
        return data.get("entries", None)

    def create_item_parsers(self, items: List[Dict]) -> None:
        """
        Creates a list of RssItemParsers given a list of parsed XML items.

        :param items: A list of parsed XML items from feedparser
        :type items: list
        """
        for item in items:
            parser = self.item_parser_type(
                item, self.feed, data=self.data, encoding=self.encoding
            )
            try:
                self.item_parsers[parser.item_id] = parser
            except KeyError:
                app.logger.error(
                    "Parser with Item Id %s in %s already exists",
                    parser.item_id,
                    self.feed,
                )
