# -*- coding: utf-8 -*-
"""Kodi stand-in for the headless Millionaire game: real addon code, fake Kodi modules.

`install()` reuses the library harness stubs (xbmc, xbmcgui, xbmcaddon, xbmcvfs, xbmcplugin), adds what the game
dialog needs (a WindowXMLDialog that the game modal can subclass), puts Kodi's own `requests` on the path and loads
the addon settings: defaults from resources/settings.xml, then the user's settings.xml from Kodi (read only), then
`overrides`. special://profile is a temp directory, so usage statistics of the real Kodi are never touched.
"""
import os
import sys
import threading
import time
import xml.etree.ElementTree as ET

HERE = os.path.dirname(os.path.abspath(__file__))
ADDON_DIR = os.path.abspath(os.path.join(HERE, '..', '..'))
KODI_HOME = os.path.expanduser('~/Library/Application Support/Kodi')
USER_SETTINGS = os.path.join(KODI_HOME, 'userdata', 'addon_data', 'plugin.video.milionar', 'settings.xml')
KODI_MODULES = ('script.module.requests', 'script.module.urllib3', 'script.module.idna', 'script.module.certifi',
                'script.module.chardet')
SECRET_PARTS = ('key', 'token', 'password', 'secret', 'login', 'user', 'pass', 'cookie')

_REAL_SLEEP = time.sleep
_state = {'scale': 0.0, 'installed': False}


def is_secret(setting_id):
    return any(part in setting_id.lower() for part in SECRET_PARTS)


def _defaults():
    values = {}
    for node in ET.parse(os.path.join(ADDON_DIR, 'resources', 'settings.xml')).getroot().iter('setting'):
        if node.get('id') and node.get('default') is not None:
            values[node.get('id')] = node.get('default')
    return values


def _user_settings(declared=()):
    """The user's values; default="true" on a setting the addon declares means Kodi uses the addon's default."""
    if not os.path.exists(USER_SETTINGS):
        return {}
    values = {}
    for node in ET.parse(USER_SETTINGS).getroot().iter('setting'):
        if node.get('id') and not (node.get('default') == 'true' and node.get('id') in declared):
            values[node.get('id')] = node.text or ''
    return values


def set_time_scale(scale):
    """Scale every time.sleep of the game (0: the countdowns and pauses take no time; 1: real time)."""
    _state['scale'] = max(0.0, float(scale))


def _scaled_sleep(seconds):
    # only the game threads are scaled; the harness waits with its own clock (wait helpers use _REAL_SLEEP)
    if threading.current_thread() is threading.main_thread():
        return _REAL_SLEEP(seconds)
    return _REAL_SLEEP(seconds * _state['scale']) if _state['scale'] else _REAL_SLEEP(0)


def real_sleep(seconds):
    _REAL_SLEEP(seconds)


def install(overrides=None, time_scale=0.0):
    """Install the fake Kodi once per process; returns the settings dict (live, shared with xbmcaddon.Addon)."""
    if _state['installed']:
        raise RuntimeError('env.install() runs once per process')
    _state['installed'] = True
    sys.path.insert(0, os.path.join(ADDON_DIR, 'scripts', 'library_harness'))
    for module in KODI_MODULES:
        sys.path.append(os.path.join(KODI_HOME, 'addons', module, 'lib'))
    import stubs
    stubs.install()

    import xbmc
    import xbmcaddon
    import xbmcgui

    xbmc.LOGNOTICE = xbmc.LOGINFO
    xbmc.LOGFATAL = 4
    xbmcgui.NOTIFICATION_ERROR = 'error'
    xbmcgui.NOTIFICATION_WARNING = 'warning'
    xbmcgui.ACTION_NAV_BACK, xbmcgui.ACTION_PREVIOUS_MENU = 92, 10
    xbmcgui.getCurrentWindowId = lambda: 10000
    xbmcgui.getCurrentWindowDialogId = lambda: 13000
    xbmcgui.getScreenWidth = lambda: 1920
    xbmcgui.getScreenHeight = lambda: 1080

    class WindowXMLDialog(object):
        """What the game modal needs: window properties, focus, close; the window XML is ignored."""

        def __new__(cls, *args, **kwargs):
            return object.__new__(cls)

        def __init__(self, *args, **kwargs):
            self.props = {}
            self.focus = None
            self.closed = False
            self.focus_log = []

        def setProperty(self, key, value):
            self.props[key] = value

        def getProperty(self, key):
            return self.props.get(key, '')

        def clearProperty(self, key):
            self.props.pop(key, None)

        def setFocusId(self, control_id):
            self.focus = control_id
            self.focus_log.append(control_id)

        def getFocusId(self):
            return self.focus

        def onAction(self, action):
            pass

        def show(self):
            pass

        def doModal(self):
            pass

        def close(self):
            self.closed = True
    xbmcgui.WindowXMLDialog = WindowXMLDialog
    xbmcgui.WindowXML = WindowXMLDialog

    class Action:
        def __init__(self, action_id):
            self._id = action_id

        def getId(self):
            return self._id

        def getButtonCode(self):
            return 0
    xbmcgui.Action = Action

    settings = xbmcaddon.Addon.settings
    settings.clear()
    defaults = _defaults()
    settings.update(defaults)
    settings.update(_user_settings(defaults))
    settings.update({key: str(value) for key, value in (overrides or {}).items()})

    set_time_scale(time_scale)
    time.sleep = _scaled_sleep
    return settings


def log_lines(min_level=0):
    """Kodi log lines written so far: [(level, message)]."""
    import stubs
    return [(level, msg) for level, msg in stubs.LOG if level >= min_level]


def public_settings(settings, prefixes=('ai_', 'openai_', 'anthropic_', 'google_', 'mistral_', 'groq_', 'milionar',
                                        'millionaire')):
    """Game related settings without secrets (for reports)."""
    return {key: value for key, value in sorted(settings.items())
            if key.startswith(prefixes) and not is_secret(key)}
