# -*- coding: utf-8 -*-
"""L4 unit tests: the shipped defaults (wcs/defaults.py, wcs/skin/defaults.py, resources/defaults/skin.json)."""
import json
import os
import re
import sys
import unittest

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

stubs.install()

import xbmc  # noqa: E402
import xbmcaddon  # noqa: E402
import xbmcgui  # noqa: E402

from wcs import defaults as addon_defaults  # noqa: E402
from wcs.skin import defaults as skin_defaults  # noqa: E402
from wcs.skin import slots  # noqa: E402

CREDENTIALS = ('tmdb_api_key', 'token', 'wsuser', 'wspass', 'kraska_token', 'kraska_pass', 'kraska_user', 'sc_auth_token',
               'vinyl_token', 'vinyl_login', 'milionar_tts_elevenlabs_api_key', 'duuid', 'dfolder')
ROOT = os.path.dirname(os.path.dirname(HERE))
SETTINGS_XML = os.path.join(ROOT, 'resources', 'settings.xml')
HOME_XML = os.path.join(ROOT, 'resources', 'skin.milionar', 'xml', 'Home.xml')


class AddonDefaultsTests(unittest.TestCase):
    def setUp(self):
        xbmcaddon.Addon.settings.clear()
        xbmcgui.NOTIFICATIONS.clear()
        xbmcaddon.Addon.strings[addon_defaults.S_RESET_DONE] = 'reset'
        self.addon = xbmcaddon.Addon()
        self.shipped = addon_defaults.resettable_defaults(SETTINGS_XML)

    def test_credentials_and_text_settings_are_never_in_the_reset(self):
        for sid in CREDENTIALS:
            self.assertNotIn(sid, self.shipped)
        self.assertIn('dialog_style', self.shipped)
        self.assertIn('openai_max_output_tokens', self.shipped)                  # a number, not a credential
        self.assertFalse([sid for sid in self.shipped if addon_defaults.is_protected(sid)])

    def test_reset_runs_once_and_keeps_logins(self):
        xbmcaddon.Addon.settings.update({'dialog_style': '0', 'wsuser': 'me', 'wspass': 'secret', 'tmdb_api_key': 'k',
                                         'kraska_token': 't'})
        changed = addon_defaults.enforce_addon_defaults(self.addon)
        self.assertIn('dialog_style', changed)
        self.assertEqual(self.addon.getSetting('dialog_style'), self.shipped['dialog_style'])
        self.assertEqual([self.addon.getSetting(k) for k in ('wsuser', 'wspass', 'tmdb_api_key', 'kraska_token')],
                         ['me', 'secret', 'k', 't'])
        self.assertEqual(self.addon.getSetting(addon_defaults.MARKER), addon_defaults.DEFAULTS_VERSION)
        self.assertEqual(len(xbmcgui.NOTIFICATIONS), 1)
        self.addon.setSetting('dialog_style', '0')                                  # the user changes it afterwards
        self.assertIsNone(addon_defaults.enforce_addon_defaults(self.addon))        # a later start does not reset again
        self.assertEqual(self.addon.getSetting('dialog_style'), '0')

    def test_an_install_on_another_version_is_reset(self):
        self.addon.setSetting(addon_defaults.MARKER, '2.1.28')
        self.assertIsNotNone(addon_defaults.enforce_addon_defaults(self.addon))

    def test_a_normal_start_reads_one_setting_and_imports_nothing_heavy(self):
        self.addon.setSetting(addon_defaults.MARKER, addon_defaults.DEFAULTS_VERSION)
        reads = []
        get = self.addon.getSetting
        self.addon.getSetting = lambda key: reads.append(key) or get(key)
        self.assertIsNone(addon_defaults.enforce_addon_defaults(self.addon))
        self.assertEqual(reads, [addon_defaults.MARKER])

    def test_settings_xml_declares_the_marker_and_keeps_credentials_empty(self):
        with open(SETTINGS_XML, encoding='utf-8') as handle:
            text = handle.read()
        self.assertIn('id="{}"'.format(addon_defaults.MARKER), text)
        for sid in CREDENTIALS:
            match = re.search(r'<setting\b[^<>]*\bid="{}"[^<>]*\btype="text"[^<>]*>'.format(sid), text) or \
                re.search(r'<setting\b[^<>]*\btype="text"[^<>]*\bid="{}"[^<>]*>'.format(sid), text)
            if match:
                self.assertIn('default=""', match.group(0), sid)


class SkinDefaultsTests(unittest.TestCase):
    def setUp(self):
        self.commands, self.strings = [], {}
        self._exec, self._info = xbmc.executebuiltin, xbmc.getInfoLabel
        xbmc.executebuiltin = self.fake_exec
        xbmc.getInfoLabel = lambda label: (self.strings.get(re.match(r'Skin\.String\((.*)\)', label).group(1), '')
                                           if label.startswith('Skin.String(') else '')
        xbmcaddon.Addon.strings.update({code: 'T{}'.format(code) for code in range(32300, 32600)})
        skin_defaults._cache[:] = []

    def tearDown(self):
        xbmc.executebuiltin, xbmc.getInfoLabel = self._exec, self._info
        skin_defaults._cache[:] = []

    def fake_exec(self, command, wait=False):
        self.commands.append(command)
        match = re.match(r'Skin\.SetString\((.*?),"(.*)"\)$', command)
        if match:
            self.strings[match.group(1)] = match.group(2)
        reset = re.match(r'Skin\.Reset\((.*)\)$', command)
        if reset:
            self.strings.pop(reset.group(1), None)

    def test_apply_writes_widgets_flags_and_the_once_flag(self):
        with open(os.path.join(ROOT, 'resources', 'defaults', 'skin.json'), encoding='utf-8') as handle:
            data = json.load(handle)
        self.assertTrue(skin_defaults.apply())
        for sec, shipped in data['sections'].items():
            for n, widget in enumerate(shipped['widgets'], 1):
                self.assertEqual(self.strings['mil.{}.{}.type'.format(sec, n)], widget['type'], (sec, n))
                self.assertEqual(self.strings['mil.{}.{}.uid'.format(sec, n)], widget['uid'])
                self.assertEqual(bool(self.strings.get('mil.{}.{}.hero'.format(sec, n))), bool(widget['hero']), (sec, n))
        for name, on in data['flags'].items():
            self.assertIn('Skin.SetBool({})'.format(name) if on else 'Skin.Reset({})'.format(name), self.commands)
        self.assertIn('Skin.SetBool({})'.format(skin_defaults.FLAG), self.commands)

    def test_home_runs_the_defaults_once_behind_the_flag_and_nothing_else(self):
        with open(HOME_XML, encoding='utf-8') as handle:
            home = handle.read()
        self.assertEqual(re.findall(r'<onload condition="([^"]*)">RunPlugin\(plugin://plugin\.video\.milionar/\?action=([a-z_]+)', home),
                         [('!Skin.HasSetting({}) + System.AddonIsEnabled(plugin.video.milionar)'.format(skin_defaults.FLAG),
                           'skin_defaults')])
        self.assertNotRegex(home, r'MilionarDefaultsV[1-7]|MilionarSlotsV|MilionarVinylV')

    def test_widget_reset_uses_the_shipped_widgets(self):
        slots.write_defaults('tmdb')
        shipped = skin_defaults.section('tmdb')['widgets']
        self.assertEqual([self.strings['mil.tmdb.{}.type'.format(n)] for n in range(1, len(shipped) + 1)],
                         [w['type'] for w in shipped])


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