# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import logging
import babelfish
import bs4
import requests
from . import Provider
from .. import __version__
from ..cache import region, SHOW_EXPIRATION_TIME
from ..exceptions import ConfigurationError, AuthenticationError, DownloadLimitExceeded, ProviderError, InvalidSubtitle
from ..subtitle import Subtitle, decode, fix_line_endings, is_valid_subtitle
from ..video import Episode
 
 
logger = logging.getLogger(__name__)
babelfish.language_converters.register('addic7ed = subliminal.converters.addic7ed:Addic7edConverter')
 
 
class Addic7edSubtitle(Subtitle):
    provider_name = 'addic7ed'
 
    def __init__(self, language, series, season, episode, title, year, version, hearing_impaired, download_link,
                 page_link):
        super(Addic7edSubtitle, self).__init__(language, hearing_impaired, page_link)
        self.series = series
        self.season = season
        self.episode = episode
        self.title = title
        self.year = year
        self.version = version
        self.download_link = download_link
 
    def compute_matches(self, video):
        matches = set()
        # series
        if video.series and self.series == video.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')
        # title
        if video.title and self.title.lower() == video.title.lower():
            matches.add('title')
        # year
        if self.year == video.year:
            matches.add('year')
        # release_group
        if video.release_group and self.version and video.release_group.lower() in self.version.lower():
            matches.add('release_group')
        # resolution
        if video.resolution and self.version and video.resolution in self.version.lower():
            matches.add('resolution')
        return matches
 
 
class Addic7edProvider(Provider):
    languages = {babelfish.Language('por', 'BR')} | {babelfish.Language(l)
                 for l in ['ara', 'aze', 'ben', 'bos', 'bul', 'cat', 'ces', 'dan', 'deu', 'ell', 'eng', 'eus', 'fas',
                           'fin', 'fra', 'glg', 'heb', 'hrv', 'hun', 'hye', 'ind', 'ita', 'jpn', 'kor', 'mkd', 'msa',
                           'nld', 'nor', 'pol', 'por', 'ron', 'rus', 'slk', 'slv', 'spa', 'sqi', 'srp', 'swe', 'tha',
                           'tur', 'ukr', 'vie', 'zho']}
    video_types = (Episode,)
    server = 'http://www.addic7ed.com'
 
    def __init__(self, username=None, password=None):
        if username is not None and password is None or username is None and password is not None:
            raise ConfigurationError('Username and password must be specified')
        self.username = username
        self.password = password
        self.logged_in = False
 
    def initialize(self):
        self.session = requests.Session()
        self.session.headers = {'User-Agent': 'Subliminal/%s' % __version__.split('-')[0]}
        # login
        if self.username is not None and self.password is not None:
            logger.debug('Logging in')
            data = {'username': self.username, 'password': self.password, 'Submit': 'Log in'}
            r = self.session.post(self.server + '/dologin.php', data, timeout=10, allow_redirects=False)
            if r.status_code == 302:
                logger.info('Logged in')
                self.logged_in = True
            else:
                raise AuthenticationError(self.username)
 
    def terminate(self):
        # logout
        if self.logged_in:
            r = self.session.get(self.server + '/logout.php', timeout=10)
            logger.info('Logged out')
            if r.status_code != 200:
                raise ProviderError('Request failed with status code %d' % r.status_code)
        self.session.close()
 
    def get(self, url, params=None):
        """Make a GET request on `url` with the given parameters
 
        :param string url: part of the URL to reach with the leading slash
        :param params: params of the request
        :return: the response
        :rtype: :class:`bs4.BeautifulSoup`
 
        """
        r = self.session.get(self.server + url, params=params, timeout=10)
        if r.status_code != 200:
            raise ProviderError('Request failed with status code %d' % r.status_code)
        return bs4.BeautifulSoup(r.content, ['permissive'])
 
    @region.cache_on_arguments(expiration_time=SHOW_EXPIRATION_TIME)
    def get_show_ids(self):
        """Load the shows page with default series to show ids mapping
 
        :return: series to show ids
        :rtype: dict
 
        """
        soup = self.get('/shows.php')
        show_ids = {}
        for html_show in soup.select('td.version > h3 > a[href^="/show/"]'):
            show_ids[html_show.string.lower()] = int(html_show['href'][6:])
        return show_ids
 
    @region.cache_on_arguments(expiration_time=SHOW_EXPIRATION_TIME)
    def find_show_id(self, series, year=None):
        """Find the show id from the `series` with optional `year`
 
        Use this only if the show id cannot be found with :meth:`get_show_ids`
 
        :param string series: series of the episode in lowercase
        :param year: year of the series, if any
        :type year: int or None
        :return: the show id, if any
        :rtype: int or None
 
        """
        series_year = series
        if year is not None:
            series_year += ' (%d)' % year
        params = {'search': series_year, 'Submit': 'Search'}
        logger.debug('Searching series %r', params)
        suggested_shows = self.get('/search.php', params).select('span.titulo > a[href^="/show/"]')
        if not suggested_shows:
            logger.info('Series %r not found', series_year)
            return None
        return int(suggested_shows[0]['href'][6:])
 
    def query(self, series, season, year=None):
        show_ids = self.get_show_ids()
        show_id = None
        if year is not None:  # search with the year
            series_year = '%s (%d)' % (series.lower(), year)
            if series_year in show_ids:
                show_id = show_ids[series_year]
            else:
                show_id = self.find_show_id(series.lower(), year)
        if show_id is None:  # search without the year
            year = None
            if series.lower() in show_ids:
                show_id = show_ids[series.lower()]
            else:
                show_id = self.find_show_id(series.lower())
        if show_id is None:
            return []
        params = {'show_id': show_id, 'season': season}
        logger.debug('Searching subtitles %r', params)
        link = '/show/{show_id}&season={season}'.format(**params)
        soup = self.get(link)
        subtitles = []
        for row in soup('tr', class_='epeven completed'):
            cells = row('td')
            if cells[5].string != 'Completed':
                continue
            if not cells[3].string:
                continue
            subtitles.append(Addic7edSubtitle(babelfish.Language.fromaddic7ed(cells[3].string), series, season,
                                              int(cells[1].string), cells[2].string, year, cells[4].string,
                                              bool(cells[6].string), cells[9].a['href'],
                                              self.server + cells[2].a['href']))
        return subtitles
 
    def list_subtitles(self, video, languages):
        return [s for s in self.query(video.series, video.season, video.year)
                if s.language in languages and s.episode == video.episode]
 
    def download_subtitle(self, subtitle):
        r = self.session.get(self.server + subtitle.download_link, timeout=10, headers={'Referer': subtitle.page_link})
        if r.status_code != 200:
            raise ProviderError('Request failed with status code %d' % r.status_code)
        if r.headers['Content-Type'] == 'text/html':
            raise DownloadLimitExceeded
        subtitle_content = fix_line_endings(decode(r.content, subtitle.language))
        if not is_valid_subtitle(subtitle_content):
            raise InvalidSubtitle
        subtitle.content = subtitle_content