#!/usr/bin/env python3
"""Offline checks of the Pro tebe button "Zobrazit" (wcs/foryou/mode.py, docs/plan/pro-tebe/030).

Run: python3 scripts/skin_harness/test_foryou_mode.py
"""
import os
import sys
import types
import unittest
from unittest.mock import Mock, patch

ROOT = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..'))
sys.path.insert(0, ROOT)
KODI = {593: 'Vše', 342: 'Filmy', 20343: 'Seriály'}
ADDON = {35344: 'Zobrazit: {}', 35345: 'Zobrazit (vše / filmy / seriály)', 32692: 'Upravit tlačítka hero',
         32420: 'Další…'}


class ModeTests(unittest.TestCase):
    def setUp(self):
        self.skin, self.props, self.builtins, self.menus, self.answers = {}, {}, [], [], []
        self.updating = []
        modules = {name: types.ModuleType(name) for name in ('xbmc', 'xbmcaddon', 'xbmcgui', 'xbmcplugin', 'xbmcvfs')}
        xbmc = modules['xbmc']
        xbmc.LOGINFO = xbmc.LOGWARNING = xbmc.LOGERROR = xbmc.LOGDEBUG = 0
        xbmc.log = Mock()
        xbmc.getLocalizedString = KODI.get
        xbmc.getInfoLabel = lambda label: (self.skin.get(label[len('Skin.String('):-1], '') if label.startswith('Skin.String(')
                                          else '32903' if label == 'System.CurrentControlID' else '')

        def run(command, wait=False):
            self.builtins.append(command)
            if command.startswith('Skin.SetString('):
                key, value = command[len('Skin.SetString('):-1].split(',')
                self.skin[key] = value
            elif command.startswith('Skin.Reset('):
                self.skin.pop(command[len('Skin.Reset('):-1], None)

        xbmc.executebuiltin = run
        xbmc.getCondVisibility = lambda condition: self.updating.pop(0) if self.updating and 'IsUpdating' in condition else False
        monitor = Mock()
        monitor.waitForAbort.return_value = False
        xbmc.Monitor = Mock(return_value=monitor)
        addon = Mock()
        addon.getLocalizedString.side_effect = lambda string_id: ADDON.get(string_id, '')
        modules['xbmcaddon'].Addon = Mock(return_value=addon)
        window = Mock()
        window.setProperty.side_effect = self.props.__setitem__
        window.clearProperty.side_effect = lambda key: self.props.pop(key, None)
        modules['xbmcgui'].Window = Mock(return_value=window)
        dialog = Mock()

        def contextmenu(labels):
            self.menus.append(list(labels))
            return self.answers.pop(0)

        dialog.contextmenu.side_effect = contextmenu
        modules['xbmcgui'].Dialog = Mock(return_value=dialog)
        patcher = patch.dict(sys.modules, modules)
        patcher.start()
        self.addCleanup(patcher.stop)
        for name in [n for n in sys.modules if n.startswith('wcs')]:
            sys.modules.pop(name)
        from wcs.foryou import mode
        from wcs.skin import context, hero_buttons
        self.mode, self.context, self.hero_buttons = mode, context, hero_buttons
        patch.object(hero_buttons, 'focused_widget', return_value='foryou:1:uid').start()
        self.addCleanup(patch.stopall)

    def test_a_hero_buttons_menu_opens_at_its_heros_anchor(self):
        from wcs.skin import menu_anchor
        self.assertEqual(menu_anchor.anchor_of(32903), 32907)          # Pro tebe
        self.assertEqual(menu_anchor.anchor_of(31906), 31907)          # Vinyl, plain primary
        self.assertEqual(menu_anchor.anchor_of(41903), 41907)          # the Home detail
        self.assertIsNone(menu_anchor.anchor_of(32100))                # a row: Kodi centres on it already

    def test_the_menu_moves_the_focus_to_the_anchor_and_back(self):
        self.answers = [-1]
        self.mode.choose()
        self.assertEqual(self.builtins, ['SetFocus(32907)'])         # back only when the anchor still has it

    def test_only_pro_tebe_rows_have_the_action(self):
        self.assertIn('fy_mode', self.context.catalog('fy_for_you'))
        self.assertIn('fy_mode', self.context.catalog('fy_continue'))
        self.assertNotIn('fy_mode', self.context.catalog('my_movies_all'))

    def test_the_hero_shows_it_by_default_and_a_saved_layout_gains_it(self):
        with patch('wcs.library2.clicks.kind_of', return_value='mixed'):
            self.assertIn('fy_mode', self.hero_buttons.defaults('fy_for_you')['main'])
            saved = self.hero_buttons.normalize('fy_for_you', {'main': ['play', 'detail'], 'more': [], 'hidden': []})
        self.assertIn('fy_mode', saved['main'])

    def test_the_label_names_the_choice_and_shows_it_opens_a_menu(self):
        self.assertEqual(self.mode.label(), 'Zobrazit: Vše' + self.mode.ARROW)
        self.skin['mil.foryou.mode'] = 'tv'
        self.assertEqual(self.mode.label(), 'Zobrazit: Seriály' + self.mode.ARROW)

    def test_menu_has_the_choices_and_the_hero_editor_under_more(self):
        self.answers = [3, 0]
        self.mode.choose()
        self.assertEqual(self.menus, [['Vše', 'Filmy', 'Seriály', 'Další…'], ['Upravit tlačítka hero']])
        self.assertTrue(self.builtins[-1].startswith('RunPlugin(plugin://plugin.video.milionar/?action=skin_hero&do=edit'))

    def test_a_choice_reloads_the_rows_and_shows_it_until_they_are_back(self):
        self.answers = [1]
        self.updating = [False, True, True, False]
        seen = []
        original = self.mode._updating
        with patch.object(self.mode, '_updating', side_effect=lambda: seen.append(dict(self.props)) or original()):
            self.mode.choose()
        self.assertEqual(self.skin['mil.foryou.mode'], 'movie')
        self.assertTrue(all(props.get('milionar.switching.foryou') == '1' for props in seen))
        self.assertNotIn('milionar.switching.foryou', self.props)

    def test_choosing_the_current_mode_changes_nothing(self):
        self.answers = [0]
        self.mode.choose()
        self.assertEqual(self.builtins, ['SetFocus(32907)'])          # only the menu's anchor

    def test_all_resets_the_skin_string(self):
        self.skin['mil.foryou.mode'] = 'movie'
        self.answers = [0]
        self.mode.choose()
        self.assertNotIn('mil.foryou.mode', self.skin)


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