# coding=utf-8
import logging
import os
import io
import time

from zipfile import ZipFile
from guessit import guessit
from requests import Session
from subliminal import Episode, Movie
from subliminal.score import get_equivalent_release_groups
from subliminal.utils import sanitize_release_group, sanitize
from subliminal_patch.providers import Provider
from subliminal_patch.subtitle import Subtitle, guess_matches
from subliminal_patch.providers.mixins import ProviderSubtitleArchiveMixin
from subzero.language import Language

logger = logging.getLogger(__name__)


class ArgenteamSubtitle(Subtitle):
    provider_name = 'argenteam'
    hearing_impaired_verifiable = False
    _release_info = None

    def __init__(self, language, download_link, movie_kind, title, season, episode, year, release, version, source,
                 video_codec, tvdb_id, imdb_id, asked_for_episode=None, asked_for_release_group=None, *args, **kwargs):
        super(ArgenteamSubtitle, self).__init__(language, download_link, *args, **kwargs)
        self.download_link = download_link
        self.movie_kind = movie_kind
        self.title = title
        self.year = year
        self.season = season
        self.episode = episode
        self.release = release
        self.version = version
        self.asked_for_release_group = asked_for_release_group
        self.asked_for_episode = asked_for_episode
        self.matches = None
        self.format = source
        self.video_codec = video_codec
        self.tvdb_id = tvdb_id
        self.imdb_id = "tt" + imdb_id if imdb_id else None
        self.releases = self.release_info

    @property
    def id(self):
        return self.download_link

    @property
    def release_info(self):
        if self._release_info:
            return self._release_info

        combine = []
        for attr in ("format", "version", "video_codec"):
            value = getattr(self, attr)
            if value:
                combine.append(value)

        self._release_info = u".".join(combine) + (u"-"+self.release if self.release else "")
        return self._release_info

    def __repr__(self):
        ep_addon = (" S%02dE%02d" % (self.season, self.episode)) if self.episode else ""
        return '<%s %r [%s]>' % (
            self.__class__.__name__, u"%s%s%s." % (self.title, " (%s)" % self.year if self.year else "", ep_addon) +
            self.release_info, self.language)

    def get_matches(self, video):
        matches = set()
        # series
        if isinstance(video, Episode) and self.movie_kind == 'episode':
            if video.series and (sanitize(self.title) in (
                     sanitize(name) for name in [video.series] + video.alternative_series)):
                matches.add('series')
            # season
            if video.season and self.season == video.season:
                matches.add('season')
            # episode
            if video.episode and self.episode == video.episode:
                matches.add('episode')

            # tvdb_id
            if video.tvdb_id and str(self.tvdb_id) == str(video.tvdb_id):
                matches.add('tvdb_id')

        elif isinstance(video, Movie) and self.movie_kind == 'movie':
            # title
            if video.title and (sanitize(self.title) in (
                     sanitize(name) for name in [video.title] + video.alternative_titles)):
                matches.add('title')

            # imdb_id
            if video.imdb_id and self.imdb_id and str(self.imdb_id) == str(video.imdb_id):
                matches.add('imdb_id')

            # year
            if video.year and self.year == video.year:
                matches.add('year')
        else:
            logger.info('%r is not a valid movie_kind', self.movie_kind)
            return matches

        # release_group
        if video.release_group and self.release:
            rg = sanitize_release_group(video.release_group)
            if any(r in sanitize_release_group(self.release) for r in get_equivalent_release_groups(rg)):
                matches.add('release_group')

                # blatantly assume we've got a matching format if the release group matches
                # fixme: smart?
                #matches.add('format')

        # resolution
        if video.resolution and self.version and str(video.resolution) in self.version.lower():
            matches.add('resolution')
        # format
        if video.format and self.format:
            formats = [video.format]
            if video.format == "WEB-DL":
                formats.append("WEB")

            for fmt in formats:
                if fmt.lower() in self.format.lower():
                    matches.add('format')
                    break

        matches |= guess_matches(video, guessit(self.release_info), partial=True)
        self.matches = matches
        return matches


class ArgenteamProvider(Provider, ProviderSubtitleArchiveMixin):
    provider_name = 'argenteam'
    languages = {Language.fromalpha2(l) for l in ['es']}
    video_types = (Episode, Movie)
    API_URL = "http://argenteam.net/api/v1/"
    subtitle_class = ArgenteamSubtitle
    hearing_impaired_verifiable = False
    language_list = list(languages)

    multi_result_throttle = 2  # seconds

    def __init__(self):
        self.session = None

    def initialize(self):
        self.session = Session()
        self.session.headers = {'User-Agent': "Bazarr/1"}

    def terminate(self):
        self.session.close()

    def search_ids(self, title, year=None, imdb_id=None, season=None, episode=None, titles=None):
        """Search movie or episode id from the `title`, `season` and `episode`.

        :param imdb_id: imdb id of the given movie
        :param titles: all titles of the given series or movie
        :param year: release year of the given movie
        :param str title: series of the episode or movie name
        :param int season: season of the episode.
        :param int episode: episode number.
        :return: list of ids
        :rtype: list

        """
        # make the search
        query = title
        titles = titles or []

        is_episode = False
        if season and episode:
            is_episode = True
            query = '%s S%#02dE%#02d' % (title, season, episode)

        logger.info(u'Searching %s ID for %r', "episode" if is_episode else "movie", query)
        r = self.session.get(self.API_URL + 'search', params={'q': query}, timeout=10)
        r.raise_for_status()
        results = r.json()
        match_ids = []
        if results['total'] >= 1:
            for result in results["results"]:
                if (result['type'] == "episode" and not is_episode) or (result['type'] == "movie" and is_episode):
                    continue

                # shortcut in case of matching imdb id
                if not is_episode and imdb_id and "imdb" in result and "tt%s" % result["imdb"] == str(imdb_id):
                    logger.debug("Movie matched by IMDB ID %s, taking shortcut", imdb_id)
                    match_ids = [result['id']]
                    break

                # advanced title check in case of multiple movie results
                if results['total'] > 1:
                    if not is_episode and year:
                        if result["title"] and not (sanitize(result["title"]) in (u"%s %s" % (sanitize(name), year)
                                                                                  for name in titles)):
                            continue

                match_ids.append(result['id'])
        else:
            logger.error(u'No episode ID found for %r', query)

        if match_ids:
            logger.debug(u"Found matching IDs: %s", ", ".join(str(id) for id in match_ids))

        return match_ids

    def query(self, title, video, titles=None):
        is_episode = isinstance(video, Episode)
        season = episode = None
        url = self.API_URL + 'movie'
        if is_episode:
            season = video.season
            episode = video.episode
            url = self.API_URL + 'episode'
            argenteam_ids = self.search_ids(title, season=season, episode=episode, titles=titles)

        else:
            argenteam_ids = self.search_ids(title, year=video.year, imdb_id=video.imdb_id, titles=titles)

        if not argenteam_ids:
            return []

        language = self.language_list[0]
        subtitles = []
        has_multiple_ids = len(argenteam_ids) > 1
        for aid in argenteam_ids:
            response = self.session.get(url, params={'id': aid}, timeout=10)

            response.raise_for_status()
            content = response.json()

            imdb_id = year = None
            returned_title = title
            if not is_episode and "info" in content:
                imdb_id = content["info"].get("imdb")
                year = content["info"].get("year")
                returned_title = content["info"].get("title", title)

            for r in content['releases']:
                for s in r['subtitles']:
                    sub = ArgenteamSubtitle(language, s['uri'], "episode" if is_episode else "movie", returned_title,
                                            season, episode, year, r.get('team'), r.get('tags'),
                                            r.get('source'), r.get('codec'), content.get("tvdb"), imdb_id,
                                            asked_for_release_group=video.release_group,
                                            asked_for_episode=episode
                                            )
                    subtitles.append(sub)

            if has_multiple_ids:
                time.sleep(self.multi_result_throttle)

        return subtitles

    def list_subtitles(self, video, languages):
        if isinstance(video, Episode):
            titles = [video.series] + video.alternative_series
        else:
            titles = [video.title] + video.alternative_titles

        for title in titles:
            subs = self.query(title, video, titles=titles)
            if subs:
                return subs

            time.sleep(self.multi_result_throttle)

        return []

    def download_subtitle(self, subtitle):
        # download as a zip
        logger.info('Downloading subtitle %r', subtitle)
        r = self.session.get(subtitle.download_link, timeout=10)
        r.raise_for_status()

        # open the zip
        with ZipFile(io.BytesIO(r.content)) as zf:
            subtitle.content = self.get_subtitle_from_archive(subtitle, zf)
