"""
bwdata contains the BWData class.
"""
import datetime
from . import filters
import logging

logger = logging.getLogger("bwapi")


class BWData:
    """
    This class is a superclass for brandwatch BWQueries and BWGroups.  It was built to handle resources that access data (e.g. mentions, topics, charts, etc).
    """

    def get_mentions(self, name=None, startDate=None, max_pages=None, **kwargs):
        """
        Retrieves a list of mentions.
        Note: Clients do not have access to full Twitter mentions through the API because of our data agreement with Twitter.

        Args:
            name:       You must pass in a query / group name (string).
            startDate:  You must pass in a start date (string).
            max_pages:  Maximum number of pages to retrieve, where each page is 5000 mentions by default - Optional.  If you don't pass max_pages, it will retrieve all mentions that match your request.
            kwargs:     All other filters are optional and can be found in filters.py.

        Raises:
            KeyError:   If the mentions call fails.

        Returns:
            A list of mentions.
        """
        kwargs = {
            key: value for (key, value) in kwargs.items() if key != "iter_by_page"
        }
        all_mentions = list(
            self.iter_mentions(
                name=name,
                startDate=startDate,
                max_pages=max_pages,
                iter_by_page=False,
                **kwargs
            )
        )
        logger.info("{} mentions downloaded".format(len(all_mentions)))
        return all_mentions

    def iter_mentions(
        self, name=None, startDate=None, max_pages=None, iter_by_page=False, **kwargs
    ):
        """
        Same as get_mentions function, but returns an iterator. Fetch one page at a time to reduce memory footprint.

        Args:
            name:          You must pass in a query / group name (string).
            startDate:     You must pass in a start date (string).
            max_pages:     Maximum number of pages to retrieve, where each page is 5000 mentions by default - Optional.  If you don't pass max_pages, it will retrieve all mentions that match your request.
            iter_by_page:  Enumerate by page when set to True, else by mention, default to False - Optional.
            kwargs:        All other filters are optional and can be found in filters.py.

        Raises:
            KeyError:   If the mentions call fails.

        Returns:
            A list of mentions.
        """
        params = self._fill_params(name, startDate, kwargs)
        page_size = kwargs["pageSize"] if "pageSize" in kwargs else 5000
        params["pageSize"] = page_size
        cursor = params.get("cursor", None)
        page_idx = 0

        while True:
            if cursor:
                params["cursor"] = cursor
            if max_pages and page_idx >= max_pages:
                break
            else:
                page_idx += 1
            next_cursor, next_mentions = self._get_mentions_page(params)
            if len(next_mentions) > 0:
                cursor = next_cursor
                logger.info(
                    "Mentions page {} of {} {} retrieved".format(
                        page_idx, self.resource_type, name
                    )
                )
                if iter_by_page:
                    yield next_mentions
                else:
                    for mention in next_mentions:
                        yield mention
            if len(next_mentions) < page_size or not next_cursor:
                break

    def num_mentions(self, name=None, startDate=None, **kwargs):
        """
        Retrieves a count of the mentions in a given timeframe.

        Args:
            name:       You must pass in a query / group name (string).
            startDate:  You must pass in a start date (string).
            kwargs:     All other filters are optional and can be found in filters.py.

        Returns:
            A count of the mentions in a given timeframe.
        """
        params = self._fill_params(name, startDate, kwargs)
        return self.project.get(endpoint="data/mentions/count", params=params)[
            "mentionsCount"
        ]

    def get_chart(
        self,
        name=None,
        startDate=None,
        y_axis=None,
        x_axis=None,
        breakdown_by=None,
        **kwargs
    ):
        """
        Retrieves chart data.

        Args:
            name:           You must pass in a query / group name (string).
            startDate:      You must pass in a start date (string).
            y_axis:         Pass in the y axis of your chart (string in camel case). See Brandwatch app dropdown menu "For (Y-Axis)" for options
            x_axis:         Pass in the x axis of your chart (string in camel case). See Brandwatch app dropdown menu "Show (X-Axis)" for options.
            breakdown_by:   Pass in breakdown_by (string in camel case). See Brandwatch app dropdown menu "Breakdown by" for options.
            kwargs:         You must pass in name (query name/group) and startDate (string).  Additionally, if you x_axis or breakdown_by consists of categories or tags you must pass in dim1Args or dim2Args, respectively, which should be a list of the names of those cats/tags. All other filters are optional and can be found in filters.py.

        Returns:
            A dictionary representation of the specified chart

        Raises:
            KeyError:       If you fail to pass in x_axis, y_axis or breakdown_by.

        """
        if not (y_axis and x_axis and breakdown_by):
            raise KeyError("You must pass in an y_axis, x_axis and breakdown_by")

        params = self._fill_params(name, startDate, kwargs)
        if "dim1Args" in params:
            params["dim1Args"] = self._name_to_id(x_axis, params["dim1Args"])
        if "dim2Args" in params:
            params["dim2Args"] = self._name_to_id(breakdown_by, params["dim2Args"])

        return self.project.get(
            endpoint="data/" + y_axis + "/" + x_axis + "/" + breakdown_by, params=params
        )

    def get_topics(self, name=None, startDate=None, **kwargs):
        """
        Retrieves topics data.

        Args:
            name:           You must pass in a query / group name (string).
            startDate:      You must pass in a start date (string).
            kwargs:         All other filters are optional and can be found in filters.py.

        Returns:
            A dictionary representation of the topics including everything that can be seen in the chart view of the topics cloud (e.g. the topic, the number of mentions including that topic, the number of mentions by sentiment, the burst value, etc)
        """
        params = self._fill_params(name, startDate, kwargs)
        return self.project.get(endpoint="data/volume/topics/queries", params=params)[
            "topics"
        ]

    def get_topics_comparison(self, name=None, startDate=None, **kwargs):
        """
        Retrieves topics comparison data.

        Args:
            name:           You must pass in a query / group name (string).
            startDate:      You must pass in a start date (string).
            kwargs:         All other filters are optional and can be found in filters.py.

        Returns:
            A dictionary representation of the topics including everything that can be seen in the chart view of the topics comparison (e.g. the topic, the number of mentions including that topic, the number of mentions by sentiment, the burst value, etc)
        """
        params = self._fill_params(name, startDate, kwargs)
        return self.project.get(
            endpoint="data/volume/topics/compare/gender", params=params
        )["topics"]

    def get_authors(self, name=None, startDate=None, **kwargs):
        """
        Retrieves author data.

        Args:
            name:           You must pass in a query / group name (string).
            startDate:      You must pass in a start date (string).
            kwargs:         All other filters are optional and can be found in filters.py.

        Returns:
            A dictionary representation of the authors including everything that can be seen in the list of authors
        """
        params = self._fill_params(name, startDate, kwargs)
        return self.project.get(
            endpoint="data/volume/topauthors/queries", params=params
        )["results"]

    def get_history(self, name=None, startDate=None, **kwargs):
        """
        Retrieves history data.

        Args:
            name:           You must pass in a query / group name (string).
            startDate:      You must pass in a start date (string).
            kwargs:         All other filters are optional and can be found in filters.py.

        Returns:
            A dictionary representation of the history component, all the points of time that the timeline covers
        """
        params = self._fill_params(name, startDate, kwargs)
        return self.project.get(endpoint="data/volume/queries/days", params=params)[
            "results"
        ]

    def get_topsites(self, name=None, startDate=None, **kwargs):
        """
        Retrieves top sites data.

        Args:
            name:           You must pass in a query / group name (string).
            startDate:      You must pass in a start date (string).
            kwargs:         All other filters are optional and can be found in filters.py.

        Returns:
            A dictionary representation of top sites
        """
        params = self._fill_params(name, startDate, kwargs)
        return self.project.get(endpoint="data/volume/topsites/queries", params=params)[
            "results"
        ]

    def get_tweeters(self, name=None, startDate=None, **kwargs):
        """
        Retrieves tweeters data.

        Args:
            name:           You must pass in a query / group name (string).
            startDate:      You must pass in a start date (string).
            kwargs:         All other filters are optional and can be found in filters.py.

        Returns:
            A dictionary representation of top tweeters
        """
        params = self._fill_params(name, startDate, kwargs)
        return self.project.get(
            endpoint="data/volume/toptweeters/queries", params=params
        )["results"]

    def get_volume(self, name=None, startDate=None, **kwargs):
        """
        Retrieves volume data.

        Args:
            name:           You must pass in a query / group name (string).
            startDate:      You must pass in a start date (string).
            kwargs:         All other filters are optional and can be found in filters.py.

        Returns:
            A dictionary representation of volume data
        """
        params = self._fill_params(name, startDate, kwargs)
        return self.project.get(
            endpoint="data/volume/queries/pageTypes", params=params
        )["results"]

    def get_world(self, name=None, startDate=None, **kwargs):
        """
        Retrieves world overview (mentions map) data.

        Args:
            name:           You must pass in a query / group name (string).
            startDate:      You must pass in a start date (string).
            kwargs:         All other filters are optional and can be found in filters.py.

        Returns:
            A dictionary representation of mapped mentions on a globe data
        """
        params = self._fill_params(name, startDate, kwargs)
        return self.project.get(
            endpoint="data/volume/queries/countries", params=params
        )["results"]["values"]

    def get_keyinsights(self, name=None, startDate=None, **kwargs):
        """
        Retrieves key insights component data.

        Args:
            name:           You must pass in a query / group name (string).
            startDate:      You must pass in a start date (string).
            kwargs:         All other filters are optional and can be found in filters.py.

        Returns:
            A dictionary representation of the component key insights
        """
        key_insights = {
            "total_mentions": self.get_keyinsights_mention_count(name, startDate),
            "unique_authors": self.get_keyinsights_author_count(name, startDate),
            "topic_trends": self.get_keyinsights_topics(name, startDate),
            "rising_news": self.get_keyinsights_news(name, startDate),
        }
        return key_insights

    def get_keyinsights_mention_count(self, name=None, startDate=None, **kwargs):
        """
        Retrieves total mentions count data from key insights component.

        Args:
            name:           You must pass in a query / group name (string).
            startDate:      You must pass in a start date (string).
            kwargs:         All other filters are optional and can be found in filters.py.

        Returns:
            An integer that represents the total number of mentions
        """
        params = self._fill_params(name, startDate, kwargs)
        return self.project.get(endpoint="data/mentions/count", params=params)[
            "mentionsCount"
        ]

    def get_keyinsights_author_count(self, name=None, startDate=None, **kwargs):
        """
        Retrieves total unique authors count data from key insights component.

        Args:
            name:           You must pass in a query / group name (string).
            startDate:      You must pass in a start date (string).
            kwargs:         All other filters are optional and can be found in filters.py.

        Returns:
            An integer that represents the total number of unique authors
        """
        params = self._fill_params(name, startDate, kwargs)
        return self.project.get(endpoint="data/authors/months/queries", params=params)[
            "results"
        ][0]["values"][0]["value"]

    def get_keyinsights_topics(self, name=None, startDate=None, **kwargs):
        """
        Retrieves the top 3 trending topics data from key insights component.

        Args:
            name:           You must pass in a query / group name (string).
            startDate:      You must pass in a start date (string).
            kwargs:         All other filters are optional and can be found in filters.py.

        Returns:
            A dictionary representation of the top 3 trending topics
        """
        params = self._fill_params(name, startDate, kwargs)
        params["limit"] = kwargs["limit"] if "limit" in kwargs else 3
        return self.project.get(endpoint="data/volume/topics/queries", params=params)[
            "topics"
        ]

    def get_keyinsights_news(self, name=None, startDate=None, **kwargs):
        """
        Retrieves the top 3 rising news data from the key insights component.

        Args:
            name:           You must pass in a query / group name (string).
            startDate:      You must pass in a start date (string).
            kwargs:         All other filters are optional and can be found in filters.py.

        Returns:
            A dictionary representation of the rising top 3 rising news urls
        """
        params = self._fill_params(name, startDate, kwargs)
        params["pageSize"] = kwargs["pageSize"] if "pageSize" in kwargs else 3
        return self.project.get(endpoint="data/mentions", params=params)["results"]

    def get_summary(self, name=None, startDate=None, **kwargs):
        """
        Retrieves the summary component data.

        Args:
            name:           You must pass in a query / group name (string).
            startDate:      You must pass in a start date (string).
            kwargs:         All other filters are optional and can be found in filters.py.

        Returns:
            A dictionary representation of the summary component analysis
        """
        summary = {
            "sentiment": self.get_summary_sentiment(name, startDate),
            "topsites": self.get_summary_topsites(name, startDate),
            "pagetypes": self.get_summary_pagetypes(name, startDate),
        }
        return summary

    def get_summary_sentiment(self, name=None, startDate=None, **kwargs):
        """
        Retrieves the sentiment data from the summary component.

        Args:
            name:           You must pass in a query / group name (string).
            startDate:      You must pass in a start date (string).
            kwargs:         All other filters are optional and can be found in filters.py.

        Returns:
            A dictionary representation of the summary sentiment analysis
        """
        params = self._fill_params(name, startDate, kwargs)
        return self.project.get(endpoint="data/volume/sentiment/days", params=params)[
            "results"
        ]

    def get_summary_topsites(self, name=None, startDate=None, **kwargs):
        """
        Retrieves the top sites data from the summary component.

        Args:
            name:           You must pass in a query / group name (string).
            startDate:      You must pass in a start date (string).
            kwargs:         All other filters are optional and can be found in filters.py.

        Returns:
            A dictionary representation of the summary sites analysis
        """
        params = self._fill_params(name, startDate, kwargs)
        return self.project.get(endpoint="data/volume/topsites/queries", params=params)[
            "results"
        ]

    def get_summary_pagetypes(self, name=None, startDate=None, **kwargs):
        """
        Retrieves the top page type data from the summary component.

        Args:
            name:           You must pass in a query / group name (string).
            startDate:      You must pass in a start date (string).
            kwargs:         All other filters are optional and can be found in filters.py.

        Returns:
            A dictionary representation of the summary page type analysis
        """
        params = self._fill_params(name, startDate, kwargs)
        return self.project.get(
            endpoint="data/volume/queries/pageTypes", params=params
        )["results"]

    def get_twitter_insights(self, name=None, startDate=None, **kwargs):
        """
        Retrieves the twitter insights component data.

        Args:
            name:           You must pass in a query / group name (string).
            startDate:      You must pass in a start date (string).
            kwargs:         All other filters are optional and can be found in filters.py.

        Returns:
            A dictionary representation of the twitter insights component data
        """
        twitter_insights = {
            "hashtags": self.get_twitter_insights_feature(name, startDate, "hashtags"),
            "emoticons": self.get_twitter_insights_feature(
                name, startDate, "emoticons"
            ),
            "urls": self.get_twitter_insights_feature(name, startDate, "urls"),
            "mentionedauthors": self.get_twitter_insights_feature(
                name, startDate, "mentionedauthors"
            ),
        }
        return twitter_insights

    def get_twitter_insights_feature(
        self, name=None, startDate=None, feature=None, **kwargs
    ):