import datetime
import urllib.parse

from fHDHR.tools import xmldictmaker
from fHDHR.exceptions import EPGSetupError


class zap2itEPG():

    def __init__(self, fhdhr, channels):
        self.fhdhr = fhdhr

        self.channels = channels

    @property
    def postalcode(self):
        if self.fhdhr.config.dict["zap2it"]["postalcode"]:
            return self.fhdhr.config.dict["zap2it"]["postalcode"]
        try:
            postalcode_url = 'http://ipinfo.io/json'
            postalcode_req = self.fhdhr.web.session.get(postalcode_url)
            data = postalcode_req.json()
            postalcode = data["postal"]
        except Exception as e:
            raise EPGSetupError("Unable to automatically optain postalcode: " + str(e))
            postalcode = None
        return postalcode

    def update_epg(self):
        programguide = {}

        # Start time parameter is now rounded down to nearest `zap_timespan`, in s.
        zap_time = datetime.datetime.utcnow().timestamp()
        self.remove_stale_cache(zap_time)
        zap_time_window = int(self.fhdhr.config.dict["zap2it"]["timespan"]) * 3600
        zap_time = int(zap_time - (zap_time % zap_time_window))

        # Fetch data in `zap_timespan` chunks.
        i_times = []
        for i in range(int(7 * 24 / int(self.fhdhr.config.dict["zap2it"]["timespan"]))):
            i_times.append(zap_time + (i * zap_time_window))

        cached_items = self.get_cached(i_times)
        for result in cached_items:

            for c in result['channels']:

                cdict = xmldictmaker(c, ["callSign", "name", "channelNo", "channelId", "thumbnail"])

                if str(cdict['channelNo']) not in list(programguide.keys()):

                    programguide[str(cdict['channelNo'])] = {
                                                        "callsign": cdict["callSign"],
                                                        "name": cdict["name"] or cdict["callSign"],  # TODO
                                                        "number": str(cdict["channelNo"]),
                                                        "id": str(cdict["channelId"]),
                                                        "thumbnail": str(cdict['thumbnail']).replace("//", "https://").split("?")[0],
                                                        "listing": [],
                                                        }

                for event in c['events']:

                    eventdict = xmldictmaker(event, ["startTime", "endTime", "duration", "rating", "flag"], list_items=["filter", "flag"])
                    progdict = xmldictmaker(event['program'], ["title", "sub-title", "releaseYear", "episodeTitle", "shortDesc", "season", "episode", "id"])

                    timestamp = self.zap2it_timestamps(eventdict['startTime'], eventdict['endTime'])

                    clean_prog_dict = {
                                    "time_start": timestamp['time_start'],
                                    "time_end": timestamp['time_end'],
                                    "duration_minutes": eventdict['duration'],
                                    "thumbnail": str("https://zap2it.tmsimg.com/assets/" + str(eventdict['thumbnail']) + ".jpg"),
                                    "title": progdict['title'] or "Unavailable",
                                    "sub-title": progdict['sub-title'] or "Unavailable",
                                    "description": progdict['shortDesc'] or "Unavailable",
                                    "rating": eventdict['rating'] or "N/A",
                                    "episodetitle": progdict['episodeTitle'],
                                    "releaseyear": progdict['releaseYear'],
                                    "genres": [],
                                    "seasonnumber": progdict['season'],
                                    "episodenumber": progdict['episode'],
                                    "isnew": False,
                                    "id": str(progdict['id'] or "%s_%s" % (cdict["channelId"], timestamp['time_start'])),
                                    }

                    for f in eventdict['filter']:
                        clean_prog_dict["genres"].append(f.replace('filter-', ''))

                    if 'movie' in clean_prog_dict['genres'] and clean_prog_dict['releaseyear']:
                        clean_prog_dict["sub-title"] = 'Movie: ' + clean_prog_dict['releaseyear']
                    elif clean_prog_dict['episodetitle']:
                        clean_prog_dict["sub-title"] = clean_prog_dict['episodetitle']

                    if 'New' in eventdict['flag'] and 'live' not in eventdict['flag']:
                        clean_prog_dict["isnew"] = True

                    if not any((d['time_start'] == clean_prog_dict['time_start'] and d['id'] == clean_prog_dict['id']) for d in programguide[str(cdict["channelNo"])]["listing"]):
                        programguide[str(cdict["channelNo"])]["listing"].append(clean_prog_dict)

        return programguide

    def zap2it_timestamps(self, starttime, endtime):
        timestamp = {}
        for time_item, time_value in zip(["time_start", "time_end"], [starttime, endtime]):
            timestamp[time_item] = datetime.datetime.fromisoformat(time_value.replace('Z', '+00:00')).timestamp()
        return timestamp

    def get_cached(self, i_times):

        # Fetch data in `zap_timespan` chunks.
        for i_time in i_times:

            parameters = {
                'aid': self.fhdhr.config.dict["zap2it"]['affiliate_id'],
                'country': self.fhdhr.config.dict["zap2it"]['country'],
                'device': self.fhdhr.config.dict["zap2it"]['device'],
                'headendId': self.fhdhr.config.dict["zap2it"]['headendid'],
                'isoverride': "true",
                'languagecode': self.fhdhr.config.dict["zap2it"]['languagecode'],
                'pref': 'm,p',
                'timespan': self.fhdhr.config.dict["zap2it"]['timespan'],
                'timezone': self.fhdhr.config.dict["zap2it"]['timezone'],
                'userId': self.fhdhr.config.dict["zap2it"]['userid'],
                'postalCode': str(self.postalcode),
                'lineupId': '%s-%s-DEFAULT' % (self.fhdhr.config.dict["zap2it"]['country'], self.fhdhr.config.dict["zap2it"]['device']),
                'time': i_time,
                'Activity_ID': 1,
                'FromPage': "TV%20Guide",
                }

            url = 'https://tvlistings.zap2it.com/api/grid?'
            url += urllib.parse.urlencode(parameters)
            self.get_cached_item(str(i_time), url)
        cache_list = self.fhdhr.db.get_cacheitem_value("cache_list", "epg_cache", "zap2it") or []
        return [self.fhdhr.db.get_cacheitem_value(x, "epg_cache", "zap2it") for x in cache_list]

    def get_cached_item(self, cache_key, url):
        cacheitem = self.fhdhr.db.get_cacheitem_value(cache_key, "epg_cache", "zap2it")
        if cacheitem:
            self.fhdhr.logger.info('FROM CACHE:  ' + str(cache_key))
            return cacheitem
        else:
            self.fhdhr.logger.info('Fetching:  ' + url)
            try:
                resp = self.fhdhr.web.session.get(url)
            except self.fhdhr.web.exceptions.HTTPError:
                self.fhdhr.logger.info('Got an error!  Ignoring it.')
                return
            result = resp.json()

            self.fhdhr.db.set_cacheitem_value(cache_key, "epg_cache", result, "zap2it")
            cache_list = self.fhdhr.db.get_cacheitem_value("cache_list", "epg_cache", "zap2it") or []
            cache_list.append(cache_key)
            self.fhdhr.db.set_cacheitem_value("cache_list", "epg_cache", cache_list, "zap2it")

    def remove_stale_cache(self, zap_time):

        cache_list = self.fhdhr.db.get_cacheitem_value("cache_list", "epg_cache", "zap2it") or []
        cache_to_kill = []
        for cacheitem in cache_list:
            cachedate = int(cacheitem)
            if cachedate < zap_time:
                cache_to_kill.append(cacheitem)
                self.fhdhr.db.delete_cacheitem_value(cacheitem, "epg_cache", "zap2it")
                self.fhdhr.logger.info('Removing stale cache:  ' + str(cacheitem))
        self.fhdhr.db.set_cacheitem_value("cache_list", "epg_cache", [x for x in cache_list if x not in cache_to_kill], "zap2it")

    def clear_cache(self):
        cache_list = self.fhdhr.db.get_cacheitem_value("cache_list", "epg_cache", "zap2it") or []
        for cacheitem in cache_list:
            self.fhdhr.db.delete_cacheitem_value(cacheitem, "epg_cache", "zap2it")
            self.fhdhr.logger.info('Removing cache:  ' + str(cacheitem))
        self.fhdhr.db.delete_cacheitem_value("cache_list", "epg_cache", "zap2it")
