import xbmc
from abc import ABCMeta

from lib.libs import mediatypes
from lib.libs.addonsettings import settings
from lib.libs.pykodi import json, UTF8JSONDecoder
from lib.libs.utils import SortedDisplay
from lib.providers.base import AbstractProvider, AbstractImageProvider, cache, build_key_error

cfgurl = 'https://api.themoviedb.org/3/configuration'

class TheMovieDBAbstractProvider(AbstractImageProvider):
    __metaclass__ = ABCMeta
    contenttype = 'application/json'

    name = SortedDisplay('themoviedb.org', 'The Movie Database')
    _baseurl = None
    artmap = {}

    @property
    def baseurl(self):
        if not self._baseurl:
            apikey = settings.get_apikey('tmdb')
            if not apikey:
                raise build_key_error('tmdb')
            response = self.doget(cfgurl, params={'api_key': apikey})
            if response is None:
                return
            self._baseurl = response.json()['images']['secure_base_url']
        return self._baseurl

    def _get_rating(self, image):
        if image['vote_count']:
            # Reweigh ratings, increase difference from 5
            rating = image['vote_average']
            rating = 5 + (rating - 5) * 2
            return SortedDisplay(rating, '{0:.1f} stars'.format(image['vote_average']))
        else:
            return SortedDisplay(5, 'Not rated')

    def get_data(self, url):
        result = cache.cacheFunction(self._get_data, url)
        return result if result != 'Empty' else None

    def _get_data(self, url):
        apikey = settings.get_apikey('tmdb')
        if not apikey:
            raise build_key_error('tmdb')
        self.log('uncached', xbmc.LOGINFO)
        response = self.doget(url, params={'api_key': apikey})
        return 'Empty' if response is None else json.loads(response.text, cls=UTF8JSONDecoder)

    def login(self):
        raise build_key_error('tmdb')

    def process_data(self, data):
        result = {}
        for arttype, artlist in data.iteritems():
            if arttype not in self.artmap:
                continue
            previewbit = 'w300' if arttype in ('backdrops', 'stills') else 'w342'
            for image in artlist:
                resultimage = {'url': self.baseurl + 'original' + image['file_path'], 'provider': self.name}
                resultimage['preview'] = self.baseurl + previewbit + image['file_path']
                resultimage['language'] = image['iso_639_1'] if image['iso_639_1'] != 'xx' else None
                resultimage['rating'] = self._get_rating(image)
                sortsize = image['width' if arttype != 'posters' else 'height']
                resultimage['size'] = SortedDisplay(sortsize, '{0}x{1}'.format(image['width'], image['height']))
                generaltype = self.artmap[arttype]
                if settings.use_tmdb_keyart and generaltype == 'poster' and not resultimage['language']:
                    generaltype = 'keyart'
                if generaltype not in result:
                    result[generaltype] = []
                result[generaltype].append(resultimage)
        return result

    def provides(self, types):
        return any(x in types for x in self.artmap.values())

class TheMovieDBMovieProvider(TheMovieDBAbstractProvider):
    mediatype = mediatypes.MOVIE

    apiurl = 'https://api.themoviedb.org/3/movie/%s/images'
    artmap = {'backdrops': 'fanart', 'posters': 'poster', 'posters-alt': 'keyart'}

    def get_images(self, uniqueids, types=None):
        if not settings.get_apienabled('tmdb'):
            return {}
        if types is not None and not self.provides(types):
            return {}
        mediaid = get_mediaid(uniqueids)
        if not mediaid:
            return {}
        if not self.baseurl:
            return {}
        data = self.get_data(self.apiurl % mediaid)
        if not data:
            return {}

        return self.process_data(data)

class TheMovieDBEpisodeProvider(TheMovieDBAbstractProvider):
    mediatype = mediatypes.EPISODE

    tvdbidsearch_url = 'https://api.themoviedb.org/3/find/%s?external_source=tvdb_id'
    apiurl = 'https://api.themoviedb.org/3/tv/%s/season/%s/episode/%s/images'
    artmap = {'stills': 'fanart'}

    def get_images(self, uniqueids, types=None):
        if not settings.get_apienabled('tmdb'):
            return {}
        if types is not None and not self.provides(types):
            return {}
        if not self.baseurl:
            return {}
        mediaid = get_mediaid(uniqueids, ('tmdbse', 'tvdb', 'tvdbse', 'unknown'))
        if not mediaid:
            return {}
        if 'tmdbse' in uniqueids:
            splits = mediaid.split('/')
            data = {'show_id': splits[0], 'season_number': splits[1], 'episode_number': splits[2]}
        elif 'tvdbse' in uniqueids and 'tvdb' not in uniqueids:
            splits = mediaid.split('/')
            data = self.get_data(self.tvdbidsearch_url % mediaid)
            if not data or not data['tv_results']:
                return {}
            data = data['tv_results'][0]
            data = {'show_id': data['id'], 'season_number': splits[1], 'episode_number': splits[2]}
        else:
            data = self.get_data(self.tvdbidsearch_url % mediaid)
            if not data or not data['tv_episode_results']:
                return {}
            data = data['tv_episode_results'][0]
        data = self.get_data(self.apiurl % (data['show_id'], data['season_number'], data['episode_number']))
        if not data:
            return {}

        return self.process_data(data)

class TheMovieDBMovieSetProvider(TheMovieDBAbstractProvider):
    mediatype = mediatypes.MOVIESET

    apiurl = 'https://api.themoviedb.org/3/collection/%s/images'
    artmap = {'backdrops': 'fanart', 'posters': 'poster', 'posters-alt': 'keyart'}

    def get_images(self, uniqueids, types=None):
        if not settings.get_apienabled('tmdb'):
            return {}
        if types is not None and not self.provides(types):
            return {}
        mediaid = get_mediaid(uniqueids, ('tmdb', 'unknown'))
        if not mediaid:
            return {}
        if not self.baseurl:
            return {}
        data = self.get_data(self.apiurl % mediaid)
        if not data:
            return {}

        return self.process_data(data)

class TheMovieDBSearch(AbstractProvider):
    name = SortedDisplay('themoviedb.org:search', 'The Movie Database search')
    contenttype = 'application/json'

    searchurl = 'https://api.themoviedb.org/3/search/{0}'
    tvexternalidsurl = 'https://api.themoviedb.org/3/tv/{0}/external_ids'
    typemap = {mediatypes.MOVIESET: 'collection'}

    def get_data(self, url, params=None):
        result = cache.cacheFunction(self._get_data, url, params)
        return result if result != 'Empty' else None

    def _get_data(self, url, params=None):
        apikey = settings.get_apikey('tmdb')
        if not apikey:
            raise build_key_error('tmdb')
        self.log('uncached', xbmc.LOGINFO)
        if params is None:
            params = {'api_key': apikey}
        else:
            params = dict(params, api_key=apikey)
        response = self.doget(url, params=params)
        return 'Empty' if response is None else response.json()

    def login(self):
        raise build_key_error('tmdb')

    def search(self, query, mediatype):
        if mediatype not in self.typemap:
            return []
        url = self.searchurl.format(self.typemap[mediatype])
        data = self.get_data(url, {'query': query})
        if not data or 'results' not in data:
            return []

        return [{'label': item['name'], 'uniqueids': {'tmdb': item['id']}} for item in data['results']]

    def get_more_uniqueids(self, uniqueids, mediatype):
        if mediatype != mediatypes.TVSHOW or 'tvdb' in uniqueids:
            return {}
        mediaid = get_mediaid(uniqueids)
        url = self.tvexternalidsurl.format(mediaid)
        data = self.get_data(url)
        return {} if not data or not data.get('tvdb_id') else {'tvdb': data['tvdb_id']}

def get_mediaid(uniqueids, sources=('imdb', 'tmdb', 'unknown')):
    for source in sources:
        if source in uniqueids:
            return uniqueids[source]