# coding=utf-8
import warnings
warnings.filterwarnings('ignore', module=r'.*fuz.*', message='.*Sequence.*')
warnings.filterwarnings('ignore', module=r'.*dateutil.*', message='.*Unicode.*')
warnings.filterwarnings('ignore', module=r'.*', message=r'.*Python 2\.6.*')

import datetime
import os.path
import random
import sys
import test_lib as test
import unittest

sys.path.insert(1, os.path.abspath('..'))
sys.path.insert(1, os.path.abspath('../lib'))

from lxml_etree import etree
from lib.dateutil import parser
from sickgear.indexers.indexer_config import *
from sickgear.network_timezones import SG_TIMEZONE
from sickgear.providers import newznab
from six import iteritems, iterkeys

import sickgear

sickgear.SYS_ENCODING = 'UTF-8'

DEBUG = VERBOSE = False

item_parse_test_cases = [
    (('Show.Name.S02E01.720p.HDTV.x264-GROUP', 'https://test.h/test/hhhh'),
     ('Show.Name.S02E01.720p.HDTV.x264-GROUP', 'https://test.h/test/hhhh')),
    (('Show.Name.S02E02.720p.HDTV.x264-GROUP-JUNK', 'https://test.h/test/hhhh'),
     ('Show.Name.S02E02.720p.HDTV.x264-GROUP', 'https://test.h/test/hhhh')),
    (('Show.Name.S02E03.720p.HDTV.x264-GROUP[JUNK]', 'https://test.h'),
     ('Show.Name.S02E03.720p.HDTV.x264-GROUP', 'https://test.h')),
    (('Show.Name.S02E04.720p.HDTV.x264-GROUP-JUNK-JUNK', 'https://test.h'),
     ('Show.Name.S02E04.720p.HDTV.x264-GROUP', 'https://test.h')),
    (('Show.Name.S02E05.720p.HDTV.x264-GROUP-JUNK[JUNK]', 'https://test.h'),
     ('Show.Name.S02E05.720p.HDTV.x264-GROUP', 'https://test.h')),
    (('Show.Name.S02E06.720p.HDTV.x264-GROUP-JUNK[JUNK帝]', 'https://test.h'),
     ('Show.Name.S02E06.720p.HDTV.x264-GROUP', 'https://test.h')),
    (('Show.Name.S02E07-EpName帝.720p.HDTV.x264-GROUP帝-JUNK[JUNK帝]', 'https://test.h'),
     ('Show.Name.S02E07-EpName帝.720p.HDTV.x264-GROUP帝', 'https://test.h')),
    (('[grp 帝] Show Name - 11 [1024x576 h264 AAC ger-sub][123456].mp4', 'https://test.h'),
     ('[grp.帝].Show.Name.-.11.[1024x576.h264.AAC.ger-sub][123456]', 'https://test.h')),
]

size_test_cases = [
    ((1000, 'ad87987dadf7987987'), (1000, 'ad87987dadf7987987')),
    ((1254105454, 'ffdds7766dgdzhghdzghdgg'), (1254105454, 'ffdds7766dgdzhghdzghdgg')),
    ((-1, ''), (-1, None))
]

pubdate_test_cases = [
    'Sat, 28 Jul 2018 07:33:06 +0000',
    'Sun, 10 Sep 2017 23:11:09 +0200'
]

caps_test_cases = [
    {
        'name': 'newznab',
        'data_files': {'caps': 'newznab_caps.xml'},
        'caps': {
            TVINFO_TVDB: 'tvdbid', TVINFO_TVRAGE: 'rid', TVINFO_TVMAZE: 'tvmazeid',
            newznab.NewznabConstants.SEARCH_EPISODE: 'ep', newznab.NewznabConstants.SEARCH_SEASON: 'season',
            newznab.NewznabConstants.SEARCH_TEXT: 'q'
        },
        'cats': {
            newznab.NewznabConstants.CAT_SD: ['5030'], newznab.NewznabConstants.CAT_SPORT: ['5060'],
            newznab.NewznabConstants.CAT_ANIME: ['5070'], newznab.NewznabConstants.CAT_UHD: ['5045'],
            newznab.NewznabConstants.CAT_HD: ['5040']
        },
        'all_cats': [
            {'id': '5070', 'name': 'Anime'}, {'id': '5185', 'name': 'Docu HD'},
            {'id': '5180', 'name': 'Docu SD'}, {'id': '5020', 'name': 'Foreign'},
            {'id': '5040', 'name': 'HD'}, {'id': '5200', 'name': 'HEVC'},
            {'id': '5050', 'name': 'Other'}, {'id': '5030', 'name': 'SD'},
            {'id': '5060', 'name': 'Sport'}, {'id': '5045', 'name': 'UHD'}
        ],
        'limits': 100,
        'server_type': newznab.NewznabConstants.SERVER_DEFAULT
    }, {
        'name': 'nzedb',
        'data_files': {'caps': 'nzedb_caps.xml'},
        'caps': {
            TVINFO_TVDB: 'tvdbid', TVINFO_TVRAGE: 'rid', TVINFO_TVMAZE: 'tvmazeid', TVINFO_TRAKT: 'traktid',
            TVINFO_IMDB: 'imdbid', TVINFO_TMDB: 'tmdbid',
            newznab.NewznabConstants.SEARCH_EPISODE: 'ep', newznab.NewznabConstants.SEARCH_SEASON: 'season',
            newznab.NewznabConstants.SEARCH_TEXT: 'q'
        },
        'cats': {
            newznab.NewznabConstants.CAT_SD: ['5030'], newznab.NewznabConstants.CAT_SPORT: ['5060'],
            newznab.NewznabConstants.CAT_ANIME: ['5070'], newznab.NewznabConstants.CAT_UHD: ['5045'],
            newznab.NewznabConstants.CAT_HD: ['5040'], newznab.NewznabConstants.CAT_WEBDL: ['5010']
        },
        'all_cats': [
            {'id': '5070', 'name': 'Anime'}, {'id': '5080', 'name': 'Documentary'},
            {'id': '5020', 'name': 'Foreign'}, {'id': '5040', 'name': 'HD'},
            {'id': '5999', 'name': 'Other'}, {'id': '5030', 'name': 'SD'},
            {'id': '5060', 'name': 'Sport'}, {'id': '5045', 'name': 'UHD'},
            {'id': '5010', 'name': 'WEB-DL'}
        ],
        'limits': 100,
        'server_type': newznab.NewznabConstants.SERVER_DEFAULT
    }, {
        'name': 'nntmux',
        'data_files': {'caps': 'nntmux_caps.xml'},
        'caps': {
            TVINFO_TVDB: 'tvdbid', TVINFO_TVRAGE: 'rid', TVINFO_TVMAZE: 'tvmazeid', TVINFO_TRAKT: 'traktid',
            TVINFO_IMDB: 'imdbid', TVINFO_TMDB: 'tmdbid',
            newznab.NewznabConstants.SEARCH_EPISODE: 'ep', newznab.NewznabConstants.SEARCH_SEASON: 'season',
            newznab.NewznabConstants.SEARCH_TEXT: 'q'
        },
        'cats': {
            newznab.NewznabConstants.CAT_SD: ['5030'], newznab.NewznabConstants.CAT_SPORT: ['5060'],
            newznab.NewznabConstants.CAT_ANIME: ['5070'], newznab.NewznabConstants.CAT_UHD: ['5045'],
            newznab.NewznabConstants.CAT_HD: ['5040'], newznab.NewznabConstants.CAT_WEBDL: ['5010']
        },
        'all_cats': [
            {'id': '5070', 'name': 'Anime'}, {'id': '5080', 'name': 'Documentary'},
            {'id': '5020', 'name': 'Foreign'}, {'id': '5040', 'name': 'HD'}, {'id': '5999', 'name': 'Other'},
            {'id': '5030', 'name': 'SD'}, {'id': '5060', 'name': 'Sport'}, {'id': '5045', 'name': 'UHD'},
            {'id': '5010', 'name': 'WEB-DL'}, {'id': '5090', 'name': 'X265'}
        ],
        'limits': 100,
        'server_type': newznab.NewznabConstants.SERVER_DEFAULT
    }, {
        'name': 'spotweb',
        'data_files': {'caps': 'spotweb_caps.xml'},
        'caps': {
            TVINFO_TVRAGE: 'rid', TVINFO_TVMAZE: 'tvmazeid',
            newznab.NewznabConstants.SEARCH_EPISODE: 'ep', newznab.NewznabConstants.SEARCH_SEASON: 'season',
            newznab.NewznabConstants.SEARCH_TEXT: 'q'
        },
        'cats': {
            newznab.NewznabConstants.CAT_SD: ['5030'], newznab.NewznabConstants.CAT_SPORT: ['5060'],
            newznab.NewznabConstants.CAT_ANIME: ['5070'], newznab.NewznabConstants.CAT_HD: ['5040']
        },
        'all_cats': [
            {'id': '5020', 'name': 'Foreign'}, {'id': '5030', 'name': 'SD'}, {'id': '5040', 'name': 'HD'},
            {'id': '5050', 'name': 'Other'}, {'id': '5060', 'name': 'Sport'}, {'id': '5070', 'name': 'Anime'}
        ],
        'limits': 500,
        'server_type': newznab.NewznabConstants.SERVER_SPOTWEB
    }, {
        'name': 'NZBHydra',
        'data_files': {'caps': 'hydra1_caps.xml'},
        'caps': {
            TVINFO_TVDB: 'tvdbid', TVINFO_TVRAGE: 'rid',
            newznab.NewznabConstants.SEARCH_EPISODE: 'ep', newznab.NewznabConstants.SEARCH_SEASON: 'season',
            newznab.NewznabConstants.SEARCH_TEXT: 'q'
        },
        'cats': {
            newznab.NewznabConstants.CAT_SD: ['5030'], newznab.NewznabConstants.CAT_SPORT: ['5060'],
            newznab.NewznabConstants.CAT_ANIME: ['5070'], newznab.NewznabConstants.CAT_HD: ['5040']
        },
        'all_cats': [
            {'id': '5020', 'name': 'Foreign'}, {'id': '5030', 'name': 'SD'}, {'id': '5040', 'name': 'HD'},
            {'id': '5050', 'name': 'Other'}, {'id': '5060', 'name': 'Sport'}, {'id': '5070', 'name': 'Anime'},
            {'id': '5080', 'name': 'Documentary'}
        ],
        'limits': 100,
        'server_type': newznab.NewznabConstants.SERVER_HYDRA1
    }, {
        'name': 'NZBHydra 2',
        'data_files': {'caps': 'hydra2_caps.xml'},
        'caps': {
            TVINFO_TVDB: 'tvdbid', TVINFO_TVRAGE: 'rid', TVINFO_TVMAZE: 'tvmazeid', TVINFO_TRAKT: 'traktid',
            newznab.NewznabConstants.SEARCH_EPISODE: 'ep', newznab.NewznabConstants.SEARCH_SEASON: 'season',
            newznab.NewznabConstants.SEARCH_TEXT: 'q'
        },
        'cats': {
            newznab.NewznabConstants.CAT_SD: ['5030'], newznab.NewznabConstants.CAT_SPORT: ['5060'],
            newznab.NewznabConstants.CAT_ANIME: ['5070'], newznab.NewznabConstants.CAT_HD: ['5040']
        },
        'all_cats': [
            {'id': '5070', 'name': 'Anime'},  {'id': '5040', 'name': 'TV HD'},  {'id': '5030', 'name': 'TV SD'}
        ],
        'limits': 100,
        'server_type': newznab.NewznabConstants.SERVER_HYDRA2
    },
]


class FakeNewznabProvider(newznab.NewznabProvider):

    def __init__(self, *args, **kwargs):
        self.FILEDIR = os.path.join(test.TESTDIR, 'newznab_data')
        self._data_files_dict = ({}, kwargs['data_files_dict'])[isinstance(kwargs.get('data_files_dict'), dict)]
        if 'data_files_dict' in kwargs:
            del kwargs['data_files_dict']
        super(FakeNewznabProvider, self).__init__(*args, **kwargs)

    def _read_data(self, filename):
        try:
            f = os.path.join(self.FILEDIR, filename)
            if os.path.isfile(f):
                with open(f, 'rb') as d:
                    return d.read()
        except (BaseException, Exception):
            return

    # simulate Data from provider
    def get_url(self, url, skip_auth=False, use_tmr_limit=True, *args, **kwargs):
        data = None
        if '/api?' in url:
            api_parameter = url[url.find('/api?') + 5:]
            if 't=caps' in api_parameter and 'caps' in self._data_files_dict:
                data = self._read_data(self._data_files_dict['caps'])
        return data


class BasicTests(test.SickbeardTestDBCase):

    ns = {'newznab': 'http://www.newznab.com/DTD/2010/feeds/attributes/', 'atom': 'http://www.w3.org/2005/Atom'}
    ns_parsed = dict((k, '{%s}' % v) for (k, v) in ns.items())

    @staticmethod
    def _create_item(title, link, size=-1, uuid='', ids=None, pubdate=None):
        item = etree.Element('item', nsmap=BasicTests.ns)
        title_item = etree.Element('title')
        title_item.text = title
        link_item = etree.Element('link')
        link_item.text = link
        item.append(title_item)
        item.append(link_item)
        if -1 != size:
            size_item = etree.Element('{%s}attr' % BasicTests.ns['newznab'], nsmap=BasicTests.ns)
            size_item.set('name', 'size')
            size_item.set('value', '%s' % size)
            item.append(size_item)
        if uuid:
            uuid_item = etree.Element('{%s}attr' % BasicTests.ns['newznab'], nsmap=BasicTests.ns)
            uuid_item.set('name', 'guid')
            uuid_item.set('value', '%s' % uuid)
            item.append(uuid_item)
        if ids:
            for a, b in iteritems(ids):
                ids_item = etree.Element('{%s}attr' % BasicTests.ns['newznab'], nsmap=BasicTests.ns)
                ids_item.set('name', a)
                ids_item.set('value', '%s' % b)
                item.append(ids_item)
        if pubdate:
            pubdate_item = etree.Element('pubDate')
            pubdate_item.text = pubdate
            item.append(pubdate_item)
        return item

    def test_title_and_url(self):

        if VERBOSE:
            print('Running tests')

        newznab_provider = newznab.NewznabProvider('test', '')

        for cur_test, cur_expected in item_parse_test_cases:
            item = self._create_item(cur_test[0], cur_test[1])
            result = newznab_provider._title_and_url(item)
            self.assertEqual(cur_expected, result)

    def test_get_size_uid(self):

        newznab_provider = newznab.NewznabProvider('test', '')

        for cur_test, cur_expected in size_test_cases:
            item = self._create_item('Show.Name.S01E01.x264-Group', 'http://test.h', cur_test[0], cur_test[1])
            result = newznab_provider.get_size_uid(item, name_space=BasicTests.ns_parsed)
            self.assertEqual(cur_expected, result)

    def test_parse_ids(self):
        ids_test_cases = []
        for k in iterkeys(newznab.NewznabConstants.providerToIndexerMapping):
            rand_id = random.randrange(1, 99999999)
            ids_test_cases.append(({k: rand_id}, {newznab.NewznabConstants.providerToIndexerMapping[k]: rand_id}))

        all_case = {}
        all_case_ex = {}
        for k in iterkeys(newznab.NewznabConstants.providerToIndexerMapping):
            rand_id = random.randrange(1, 99999999)
            all_case.update({k: rand_id})
            all_case_ex.update({newznab.NewznabConstants.providerToIndexerMapping[k]: rand_id})

        ids_test_cases.append((all_case, all_case_ex))

        newznab_provider = newznab.NewznabProvider('test', '')

        for cur_test, cur_expected in ids_test_cases:
            item = self._create_item('Show.Name.S01E01.x264-Group', 'https://test.h', ids=cur_test)
            result = newznab_provider.cache.parse_ids(item, BasicTests.ns_parsed)
            self.assertEqual(cur_expected, result)

    @staticmethod
    def _parse_pub_date(date_str):
        parsed_date = None
        try:
            if date_str:
                p = parser.parse(date_str, fuzzy=True)
                try:
                    p = p.astimezone(SG_TIMEZONE)
                except (BaseException, Exception):
                    pass
                if isinstance(p, datetime.datetime):
                    parsed_date = p.replace(tzinfo=None)
        except (BaseException, Exception):
            pass

        return parsed_date

    def test_parse_pub_date(self):
        newznab_provider = newznab.NewznabProvider('test', '')

        for cur_test in pubdate_test_cases:
            item = self._create_item('Show.Name.S01E01.x264-Group', 'https://test.h', pubdate=cur_test)
            result = newznab_provider._parse_pub_date(item)
            cur_expected = self._parse_pub_date(cur_test)
            self.assertEqual(cur_expected, result)


class FakeProviderTests(test.SickbeardTestDBCase):

    def test_caps(self):
        self.longMessage = True
        for cur_test in caps_test_cases:
            newznab_provider = FakeNewznabProvider('test', 'https://fake.fake/', data_files_dict=cur_test['data_files'])
            newznab_provider.enabled = True
            # TODO: This test is now failing, this next line returns data that does not match the test data
            newznab_provider.get_caps()
            msg = 'Test case: %s' % cur_test['name']
            self.assertEqual(cur_test['server_type'], newznab_provider.server_type, msg=msg)
            self.assertEqual(cur_test['limits'], newznab_provider.limits, msg=msg)
            self.assertEqual(cur_test['caps'], newznab_provider.caps, msg=msg)
            self.assertEqual(cur_test['cats'], newznab_provider.cats, msg=msg)
            self.assertEqual(cur_test['all_cats'], newznab_provider.all_cats, msg=msg)


if '__main__' == __name__:
    if 1 < len(sys.argv):
        suite = unittest.TestLoader().loadTestsFromName('newznab_tests.BasicTests.test_' + sys.argv[1])
    else:
        suite = unittest.TestLoader().loadTestsFromTestCase(BasicTests)
    unittest.TextTestRunner(verbosity=2).run(suite)

    suite = unittest.TestLoader().loadTestsFromTestCase(FakeProviderTests)
    unittest.TextTestRunner(verbosity=2).run(FakeNewznabProvider)
