# -*- coding: utf-8 -*-
"""The one way into the Millionaire game (wcs/games/millionaire): subjects, the plugin action and its callers.

Run: python3 tests/test_millionaire_entry.py
"""
import os
import re
import sys
import unittest
from urllib.parse import parse_qsl

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import kodi_stubs                                           # noqa: E402

kodi_stubs.install()

from wcs.games import millionaire                           # noqa: E402
from wcs.games.millionaire import subject as subjects       # noqa: E402

ROOT = kodi_stubs.ADDON_DIR


def read(path):
    with open(path, encoding='utf-8', errors='ignore') as handle:
        return handle.read()


class OpenedWindows:
    """Stands in for the game modal: records the arguments it was opened with."""

    def __init__(self):
        self.opened = []

    def __enter__(self):
        import wcs.games.DialogMilionarGame as module
        self.module, self.original = module, module.MilionarGameModal
        recorder = self

        class Modal:
            def __init__(self, **kwargs):
                recorder.opened.append(kwargs)

            def doModal(self):
                pass
        module.MilionarGameModal = Modal
        return self

    def __exit__(self, *exc):
        self.module.MilionarGameModal = self.original


class SubjectTests(unittest.TestCase):
    def test_the_query_reads_back_as_the_same_subject(self):
        subject = millionaire.Subject(title='Přátelé', media_type='series', plot='Šest kamarádů.', poster='p.jpg',
                                      backdrop='b.jpg', episode_title='Pilot', season_number=1, episode_number=2,
                                      tmdb_id='1668')
        params = dict(parse_qsl(millionaire.query(subject)))
        self.assertEqual(params['action'], 'milionar_game')
        self.assertEqual(millionaire.from_params(params), subject)

    def test_missing_plot_and_backdrop_come_from_tmdb(self):
        original = subjects._tmdb_details
        subjects._tmdb_details = lambda tmdb_id, media_type: {
            'title': '', 'plot': 'Děj z TMDb.', 'genre': 'Drama, Krimi', 'backdrop': 'https://tmdb/w780/b.jpg',
            'person': None}
        try:
            subject = millionaire.from_params({'title': 'Kmotr', 'tmdb_id': '238', 'poster': 'poster.jpg'})
        finally:
            subjects._tmdb_details = original
        self.assertEqual((subject.plot, subject.genre, subject.poster, subject.backdrop),
                         ('Děj z TMDb.', 'Drama, Krimi', 'poster.jpg', 'https://tmdb/w780/b.jpg'))

    def test_a_given_plot_and_fanart_are_used_as_they_are(self):
        original = subjects._tmdb_details
        subjects._tmdb_details = lambda *args: self.fail('no TMDb call when plot and fanart are given')
        try:
            subject = millionaire.from_params({'title': 'X', 'tmdb_id': '1', 'plot': 'P', 'fanart': 'f.jpg'})
        finally:
            subjects._tmdb_details = original
        self.assertEqual((subject.plot, subject.poster, subject.backdrop, subject.genre), ('P', 'f.jpg', 'f.jpg', ''))

    def test_a_person_game_uses_the_profile_and_a_short_biography(self):
        data = {'name': 'Keanu Reeves', 'biography': 'B' * 900, 'profile_path': '/k.jpg'}
        subject = millionaire.from_person(data, 'actor')
        self.assertEqual((subject.title, subject.media_type, len(subject.plot)), ('Keanu Reeves', 'actor', 500))
        self.assertEqual(subject.poster, 'https://image.tmdb.org/t/p/w780/k.jpg')
        self.assertEqual(subject.backdrop, subject.poster)
        self.assertIs(subject.person, data)


class OpenGameTests(unittest.TestCase):
    def test_open_game_passes_the_subject_to_the_modern_card(self):
        subject = millionaire.Subject(title='Matrix', plot='Neo.', genre='Sci-fi', year=1999, poster='p.jpg',
                                      backdrop='b.jpg')
        with OpenedWindows() as windows:
            millionaire.open_game(subject, content='demo')
        args = windows.opened[0]
        self.assertEqual((args['title'], args['fanart'], args['banner_fanart'], args['plot'], args['genre']),
                         ('Matrix', 'p.jpg', 'b.jpg', 'Neo.', 'Sci-fi'))
        self.assertEqual((args['media_type'], args['content'], args['year']), ('movie', 'demo', 1999))

    def test_the_plugin_action_opens_the_game(self):
        with OpenedWindows() as windows:
            millionaire.run({'title': 'Matrix', 'plot': 'P', 'fanart': 'f.jpg'})
        self.assertEqual(windows.opened[0]['title'], 'Matrix')
        router = read(os.path.join(ROOT, 'wcs', 'router.py'))
        self.assertRegex(router, r"action == 'milionar_game':\s*\n(\s*#.*\n)*\s*from wcs.games import millionaire\s*\n"
                                 r"\s*millionaire.run\(params\)")

    def test_start_runs_the_plugin_action(self):
        before = len(kodi_stubs.BUILTINS)
        millionaire.start(millionaire.Subject(title='Matrix', tmdb_id='603'))
        command = kodi_stubs.BUILTINS[before]
        self.assertTrue(command.startswith('RunPlugin(plugin://plugin.video.milionar/?action=milionar_game'), command)


class CallersTests(unittest.TestCase):
    """Every place that offers the game goes through the package (no window built by hand, no old action name)."""

    def sources(self):
        for folder, _dirs, files in os.walk(os.path.join(ROOT, 'wcs')):
            for name in files:
                if name.endswith(('.py', '.xml')):
                    yield os.path.join(folder, name)
        for folder, _dirs, files in os.walk(os.path.join(ROOT, 'resources')):
            for name in files:
                if name.endswith('.xml'):
                    yield os.path.join(folder, name)

    def test_no_caller_builds_the_game_window_itself(self):
        allowed = {os.path.join(ROOT, 'wcs', 'games', 'millionaire', '__init__.py'),
                   os.path.join(ROOT, 'wcs', 'games', 'DialogMilionarGame.py')}
        offenders = [path for path in self.sources() if path not in allowed
                     and re.search(r'MilionarGameModal\(', read(path))]
        self.assertEqual(offenders, [])

    def test_the_old_action_name_is_gone(self):
        offenders = [path for path in self.sources()
                     if 'play_text_game' in read(path)]
        self.assertEqual(offenders, [])


if __name__ == '__main__':
    unittest.main()
