# Author: Nyaran <nyayukko@gmail.com>, based on Antoine Bertin <diaoulael@gmail.com> work
#
# This file is part of SickGear.
#
# SickGear is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# SickGear is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with SickGear.  If not, see <http://www.gnu.org/licenses/>.

import datetime

from . import db, helpers, logger
from .common import *
from .scheduler import Job

import sickgear

from lib import subliminal

SINGLE = 'und'


def sorted_service_list():
    services_mapping = dict([(x.lower(), x) for x in subliminal.core.SERVICES])

    new_list = []

    # add all services in the priority list, in order
    cur_index = 0
    for cur_service in sickgear.SUBTITLES_SERVICES_LIST:
        if cur_service in services_mapping:
            cur_service_dict = dict(
                id=cur_service,
                image=cur_service + '.png',
                name=services_mapping[cur_service],
                enabled=1 == sickgear.SUBTITLES_SERVICES_ENABLED[cur_index],
                api_based=__import__('lib.subliminal.services.' + cur_service, globals=globals(),
                                     locals=locals(), fromlist=['Service']).Service.api_based,
                url=__import__('lib.subliminal.services.' + cur_service, globals=globals(),
                               locals=locals(), fromlist=['Service']).Service.site_url)
            new_list.append(cur_service_dict)
        cur_index += 1

    # add any services that are missing from that list
    for cur_service in services_mapping:
        if cur_service not in [x['id'] for x in new_list]:
            cur_service_dict = dict(
                id=cur_service,
                image=cur_service + '.png',
                name=services_mapping[cur_service],
                enabled=False,
                api_based=__import__('lib.subliminal.services.' + cur_service, globals=globals(),
                                     locals=locals(), fromlist=['Service']).Service.api_based,
                url=__import__('lib.subliminal.services.' + cur_service, globals=globals(),
                               locals=locals(), fromlist=['Service']).Service.site_url)
            new_list.append(cur_service_dict)

    return new_list


def get_enabled_service_list():
    return [x['name'] for x in sorted_service_list() if x['enabled']]


def is_valid_language(language):
    return subliminal.language.language_list(language)


def get_language_name(select_lang):
    return subliminal.language.Language(select_lang).name


def wanted_languages(sql_like=False):
    wanted_langs = sorted(sickgear.SUBTITLES_LANGUAGES)
    if sql_like:
        return '%' + ','.join(wanted_langs) + '%'
    return wanted_langs


def subtitles_languages(video_path):
    """Return a list detected subtitles for the given video file"""
    video = subliminal.videos.Video.from_path(video_path)
    video.subtitle_path = sickgear.SUBTITLES_DIR
    subtitles = video.scan()
    languages = set()
    for subtitle in subtitles:
        if subtitle.language:
            languages.add(subtitle.language.alpha2)
        else:
            languages.add(SINGLE)
    return list(languages)


# Return a list with languages that have alpha2 code
def subtitle_language_filter():
    return [language for language in subliminal.language.LANGUAGES if language[2] != ""]


class SubtitlesFinder(Job):
    """
    The SubtitlesFinder will be executed every hour but will not necessarily search
    and download subtitles. Only if the defined rule is true
    """

    def __init__(self):
        super(SubtitlesFinder, self).__init__(self.job_run, kwargs={}, thread_lock=True)

    @staticmethod
    def is_enabled():
        return sickgear.USE_SUBTITLES

    def job_run(self):
        if self.is_enabled():
            self._main()

    def _main(self):
        if 1 > len(sickgear.subtitles.get_enabled_service_list()):
            logger.error('Not enough services selected. At least 1 service is required to'
                         ' search subtitles in the background')
            return

        logger.log('Checking for subtitles')

        # get episodes on which we want subtitles
        # criteria is:
        #  - show subtitles = 1
        #  - episode subtitles != config wanted languages or SINGLE (depends on config multi)
        #  - search count < 2 and diff(airdate, now) > 1 week : now -> 1d
        #  - search count < 7 and diff(airdate, now) <= 1 week : now -> 4h -> 8h -> 16h -> 1d -> 1d -> 1d

        today = datetime.date.today().toordinal()

        # you have 5 minutes to understand that one. Good luck
        my_db = db.DBConnection()
        sql_result = my_db.select(
            'SELECT s.show_name, e.indexer AS tv_id, e.showid AS prod_id,'
            ' e.season, e.episode, e.status, e.subtitles,'
            ' e.subtitles_searchcount AS searchcount, e.subtitles_lastsearch AS lastsearch,'
            ' e.location, (? - e.airdate) AS airdate_daydiff'
            ' FROM tv_episodes AS e'
            ' INNER JOIN tv_shows AS s'
            ' ON (e.indexer = s.indexer AND e.showid = s.indexer_id)'
            ' WHERE s.subtitles = 1 AND e.subtitles NOT LIKE (?)'
            ' AND ((e.subtitles_searchcount <= 2 AND (? - e.airdate) > 7)'
            ' OR (e.subtitles_searchcount <= 7 AND (? - e.airdate) <= 7))'
            ' AND (e.status IN (%s)' % ','.join([str(x) for x in Quality.DOWNLOADED])
            + ' OR (e.status IN (%s)' % ','.join([str(x) for x in Quality.SNATCHED + Quality.SNATCHED_PROPER])
            + ' AND e.location != \'\'))', [today, wanted_languages(True), today, today])
        if 0 == len(sql_result):
            logger.log('No subtitles to download', logger.MESSAGE)
            return

        rules = self._get_rules()
        now = datetime.datetime.now()
        for cur_result in sql_result:

            if not os.path.isfile(cur_result['location']):
                logger.debug(f'Episode file does not exist, cannot download subtitles for episode'
                             f' {cur_result["season"]:d}x{cur_result["episode"]:d} of show {cur_result["show_name"]}')
                continue

            # Old shows rule
            _ = datetime.datetime.strptime('20110101', '%Y%m%d')
            if ((cur_result['airdate_daydiff'] > 7 and cur_result['searchcount'] < 2
                 and now - datetime.datetime.strptime(cur_result['lastsearch'], '%Y-%m-%d %H:%M:%S')
                 > datetime.timedelta(hours=rules['old'][cur_result['searchcount']])) or
                    # Recent shows rule
                    (cur_result['airdate_daydiff'] <= 7 and cur_result['searchcount'] < 7
                     and now - datetime.datetime.strptime(cur_result['lastsearch'], '%Y-%m-%d %H:%M:%S')
                     > datetime.timedelta(hours=rules['new'][cur_result['searchcount']]))):
                logger.debug(f'Downloading subtitles for episode {cur_result["season"]:d}x{cur_result["episode"]:d}'
                             f' of show {cur_result["show_name"]}')

                show_obj = helpers.find_show_by_id({int(cur_result['tv_id']): int(cur_result['prod_id'])})
                if not show_obj:
                    logger.debug('Show not found')
                    return

                ep_obj = show_obj.get_episode(int(cur_result['season']), int(cur_result['episode']))
                if isinstance(ep_obj, str):
                    logger.debug('Episode not found')
                    return

                # noinspection PyUnusedLocal
                previous_subtitles = ep_obj.subtitles

                try:
                    # noinspection PyUnusedLocal
                    subtitles = ep_obj.download_subtitles()
                except (BaseException, Exception):
                    logger.debug('Unable to find subtitles')
                    return

    @staticmethod
    def _get_rules():
        """
        Define the hours to wait between 2 subtitles search depending on:
        - the episode: new or old
        - the number of searches done so far (searchcount), represented by the index of the list
        """
        return {'old': [0, 24], 'new': [0, 4, 8, 4, 16, 24, 24]}