"""
Kiwy Signage Player - Windows Entry Point
------------------------------------------
Patches platform-specific code and environment for Windows before launching
the original Kivy-based signage player application.

Usage:
    python run_win.py          (for development/testing)
    run_win.exe                (after PyInstaller build)
"""

import ctypes
import os
import sys
import platform
import tempfile
import subprocess
import shutil
import logging
from pathlib import Path


def _show_error_box(title, message):
    """Show a Windows message box with the error (visible even without console)."""
    try:
        ctypes.windll.user32.MessageBoxW(0, message, title, 0x10)  # MB_ICONERROR
    except Exception:
        pass

# --- Ensure we are on Windows; warn if not ---
if platform.system() != 'Windows':
    print(f"WARNING: This entry point is designed for Windows. Detected: {platform.system()}")

# =====================================================================
# 1. Set Windows-compatible environment variables BEFORE Kivy imports
# =====================================================================

# Video driver: Use 'windib' or 'angle' (DirectX via ANGLE) for Windows
os.environ.setdefault('SDL_VIDEODRIVER', 'windows')
# Audio driver: DirectSound for Windows
os.environ.setdefault('SDL_AUDIODRIVER', 'directsound')
# Prevent screensaver
os.environ.setdefault('SDL_VIDEO_ALLOW_SCREENSAVER', '0')
# Video backend via ffpyplayer
os.environ.setdefault('KIVY_VIDEO', 'ffpyplayer')
os.environ.setdefault('KIVY_AUDIO', 'ffpyplayer')
os.environ.setdefault('FFPYPLAYER_CODECS', 'h264,h265,vp9,vp8')
# Kivy window backend: prefer SDL2 on Windows
os.environ.setdefault('KIVY_WINDOW', 'sdl2')
# OpenGL
os.environ.setdefault('KIVY_GL_BACKEND', 'angle_sdl2')

# =====================================================================
# 2. Patch the evdev import — it is Linux-only. We provide a dummy
#    module so that `from evdev import ...` will not crash on Windows.
# =====================================================================
class _DummyEvdev:
    """Fake evdev module that raises ImportError for all meaningful uses."""

    class InputDevice:
        def __init__(self, *a, **kw):
            raise ImportError("evdev is not available on Windows")

    class ecodes:
        EV_KEY = 1
        EV_ABS = 3

    def categorize(self, *a, **kw):
        raise ImportError("evdev is not available on Windows")

    def list_devices(self):
        return []


class _FakeEvdevInputDevice:
    pass


# Inject the fake evdev module into sys.modules so that main.py's
# `try: import evdev` succeeds but EVDEV_AVAILABLE stays False.
_evdev_dummy = _DummyEvdev()
sys.modules['evdev'] = _evdev_dummy
sys.modules['evdev.InputDevice'] = _FakeEvdevInputDevice

# =====================================================================
# 3. Provide a Windows implementation of screen activity signaling
#    We monkey-patch the SignagePlayer.signal_screen_activity method
#    after the class is defined but before it's used, by hooking into
#    the import machinery.
# =====================================================================

# We'll store a reference to the original module's signal_screen_activity
# so we can replace it after import.  This is done inside _patch_main().

def _windows_screen_activity(self, dt):
    """Windows alternative to Linux screen-keep-awake commands.

    Uses SetThreadExecutionState via ctypes to tell Windows to keep
    the display and system awake.
    """
    try:
        # ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED | ES_CONTINUOUS
        ES_CONTINUOUS = 0x80000000
        ES_SYSTEM_REQUIRED = 0x00000001
        ES_DISPLAY_REQUIRED = 0x00000002

        ctypes.windll.kernel32.SetThreadExecutionState(
            ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED
        )
    except Exception:
        pass  # non-critical


# ── Try to import the embedded CEF browser ──────────────────────────
_CEF_BROWSER = None

def _get_cef_browser():
    """Return the shared CefBrowser singleton, or None if unavailable.

    v2 embeds CEF as a CHILD WINDOW inside Kivy's SDL_app window.
    This means: no separate taskbar entry, no z-order fighting,
    no desktop flash, no taskkill needed.
    """
    global _CEF_BROWSER
    if _CEF_BROWSER is None:
        try:
            from cef_browser import CefBrowser, CEF_AVAILABLE
            if CEF_AVAILABLE:
                _CEF_BROWSER = CefBrowser()
            else:
                return None
        except Exception:
            return None
    return _CEF_BROWSER


def _windows_find_browser():
    """Find Chrome or Edge executable on Windows for weblink support.

    Returns the path to the browser or None.
    """
    # Common install locations
    candidates = [
        # Chrome
        os.path.expandvars(r'%PROGRAMFILES%\Google\Chrome\Application\chrome.exe'),
        os.path.expandvars(r'%PROGRAMFILES(X86)%\Google\Chrome\Application\chrome.exe'),
        os.path.expandvars(r'%LOCALAPPDATA%\Google\Chrome\Application\chrome.exe'),
        # Edge
        os.path.expandvars(r'%PROGRAMFILES%\Microsoft\Edge\Application\msedge.exe'),
        os.path.expandvars(r'%PROGRAMFILES(X86)%\Microsoft\Edge\Application\msedge.exe'),
        os.path.expandvars(r'%LOCALAPPDATA%\Microsoft\Edge\Application\msedge.exe'),
    ]
    for path in candidates:
        if os.path.isfile(path):
            return path

    # Fallback: try PATH
    which = shutil.which('chrome') or shutil.which('msedge') or shutil.which('google-chrome')
    if which:
        return which

    return None


# ── Win32 API helpers via ctypes ─────────────────────────────────────
class _Win32Overlay:
    """Fullscreen black overlay window to mask desktop during transitions.

    When switching away from Chromium, the browser window disappears and
    there is a brief moment where the desktop is visible before Kivy
    manages to bring its window to the front.  This overlay covers that
    flash with a pure-black borderless always-on-top Win32 window.
    """

    _hwnd = None
    _class_atom = None

    @classmethod
    def show(cls):
        """Create a fullscreen black overlay on top of everything."""
        if cls._hwnd is not None:
            return  # already showing
        try:
            user32 = ctypes.windll.user32
            kernel32 = ctypes.windll.kernel32

            # Register a simple window class
            WNDPROC = ctypes.WINFUNCTYPE(
                ctypes.c_int64, ctypes.c_int64, ctypes.c_uint,
                ctypes.c_uint64, ctypes.c_int64
            )

            @WNDPROC
            def wnd_proc(hwnd, msg, wparam, lparam):
                if msg == 0x0002:  # WM_DESTROY
                    user32.PostQuitMessage(0)
                if msg == 0x0014:  # WM_ERASEBKGND
                    return 1  # tell Windows we erased it
                return user32.DefWindowProcW(hwnd, msg, wparam, lparam)

            hinstance = kernel32.GetModuleHandleW(None)

            # Register class
            class_name = 'KiwyOverlay_' + str(ctypes.c_uint64(int(kernel32.GetTickCount64())).value)
            wc = ctypes.create_unicode_buffer(256)

            _WNDCLASS = ctypes.c_byte * (6 * 8)  # rough size
            buf = _WNDCLASS()
            # Simple approach: use RegisterClassExW
            user32.RegisterClassExW.restype = ctypes.c_uint16
            user32.RegisterClassExW.argtypes = [ctypes.c_void_p]

            # We'll use a simpler method: just create a MessageBox-style window
            # Actually, let's use the simplest possible approach:

            # Get screen dimensions
            screen_w = user32.GetSystemMetrics(0)  # SM_CXSCREEN
            screen_h = user32.GetSystemMetrics(1)  # SM_CYSCREEN

            # Create a borderless always-on-top window
            cls._hwnd = user32.CreateWindowExW(
                0x00000008,  # WS_EX_TOPMOST | WS_EX_TOOLWINDOW
                b'#32770',  # Dialog class - always available
                b'',  # no title
                0x80000000 | 0x10000000,  # WS_POPUP | WS_VISIBLE
                0, 0, screen_w, screen_h,
                0, 0, hinstance, 0
            )

            if cls._hwnd:
                # Make it black
                from ctypes import wintypes
                gdi32 = ctypes.windll.gdi32
                hdc = user32.GetDC(cls._hwnd)
                rect = (ctypes.c_long * 4)(0, 0, screen_w, screen_h)
                brush = gdi32.CreateSolidBrush(0x00000000)  # black brush
                gdi32.FillRect(hdc, ctypes.byref(rect), brush)
                gdi32.DeleteObject(brush)
                user32.ReleaseDC(cls._hwnd, hdc)

                # Force it to the top
                user32.SetWindowPos(cls._hwnd, -1, 0, 0, screen_w, screen_h, 0x0002 | 0x0040)
                user32.ShowWindow(cls._hwnd, 1)  # SW_SHOWNORMAL
                user32.UpdateWindow(cls._hwnd)
        except Exception:
            cls._hwnd = None  # failed gracefully

    @classmethod
    def hide(cls):
        """Destroy the overlay window."""
        if cls._hwnd is None:
            return
        try:
            user32 = ctypes.windll.user32
            user32.DestroyWindow(cls._hwnd)
        except Exception:
            pass
        cls._hwnd = None


def _bring_kivy_to_front():
    """Bring the Kivy/SDL window to foreground using win32gui.

    Unlike Window.raise_window(), win32gui.SetForegroundWindow
    actually works reliably on Windows — it uses the same Win32
    API that the Task Manager uses.
    """
    try:
        import win32gui
        import win32con

        def _enum_cb(hwnd, hwnd_list):
            cls = win32gui.GetClassName(hwnd)
            title = win32gui.GetWindowText(hwnd)
            if cls == "SDL_app":
                hwnd_list.append(hwnd)
            elif "Kiwy" in title or "Signage" in title:
                hwnd_list.append(hwnd)

        hwnd_list = []
        win32gui.EnumWindows(_enum_cb, hwnd_list)

        if hwnd_list:
            kivy_hwnd = hwnd_list[-1]  # most recent
            win32gui.ShowWindow(kivy_hwnd, win32con.SW_SHOWNORMAL)
            win32gui.SetForegroundWindow(kivy_hwnd)
            win32gui.BringWindowToTop(kivy_hwnd)
    except Exception:
        # Fallback to Kivy's built-in raise
        try:
            from kivy.core.window import Window
            Window.show()
            Window.raise_window()
        except Exception:
            pass


def _windows_kill_process_tree(proc):
    """Kill a process AND all its children using taskkill.

    Chrome/Edge spawns many child processes (GPU, renderer, network,
    etc.).  A simple proc.terminate() leaves children running, causing
    lingering browser windows or zombie processes.
    """
    if proc is None or proc.poll() is not None:
        return
    try:
        subprocess.run(
            ['taskkill', '/F', '/T', '/PID', str(proc.pid)],
            capture_output=True, timeout=5
        )
    except Exception:
        # Fallback: try terminate + kill
        try:
            proc.terminate()
            try:
                proc.wait(timeout=3)
            except Exception:
                proc.kill()
        except Exception:
            pass


def _patch_main():
    """Patch the main module after import for Windows compatibility."""
    # ── CRITICAL: Override Linux env vars BEFORE importing main ─────
    # main.py's top-level code sets SDL_VIDEODRIVER=wayland,x11,dummy
    # and other Linux values. We MUST override these before main.py
    # gets imported, otherwise Kivy will initialize with the wrong
    # window provider and crash with SystemExit(1).
    os.environ['SDL_VIDEODRIVER'] = 'windows'
    os.environ['SDL_AUDIODRIVER'] = 'directsound'
    os.environ['KIVY_WINDOW'] = 'sdl2'
    os.environ['KIVY_GL_BACKEND'] = 'angle_sdl2'
    os.environ['KIVY_INPUTPROVIDERS'] = ''  # Let Kivy auto-detect
    os.environ['KIVY_VIDEO'] = 'ffpyplayer'
    os.environ['KIVY_AUDIO'] = 'ffpyplayer'

    # Now safe to import main.py — env vars are already Windows-correct
    import main as signage_main

    # Replace signal_screen_activity
    # IMPORTANT: Assign under BOTH the attribute name AND the function's own name.
    # Kivy's WeakMethod stores self.__func__.__name__ (= '_windows_screen_activity')
    # and later does getattr(instance, '_windows_screen_activity'). If we only
    # assign under 'signal_screen_activity', the weakref lookup fails with
    # AttributeError: 'SignagePlayer' object has no attribute '_windows_screen_activity'
    signage_main.SignagePlayer.signal_screen_activity = _windows_screen_activity
    signage_main.SignagePlayer._windows_screen_activity = _windows_screen_activity

    # Store a reference to the original play_weblink so we can wrap it
    _original_play_weblink = signage_main.SignagePlayer.play_weblink

    def _windows_play_weblink(self, url, duration):
        """Windows-compatible weblink handler.

        Strategy (tried in order):
          1. CEF embedded browser  (best — no subprocess, no z-order fights)
          2. Chrome/Edge subprocess (fallback)
        """
        from kivy.logger import Logger
        from kivy.clock import Clock
        from urllib.parse import urlparse

        scheme = urlparse(url).scheme.lower()
        if scheme not in ('http', 'https'):
            Logger.warning(f"SignagePlayer: Refusing non-http(s) weblink: {url}")
            self.consecutive_errors += 1
            self._skip_to_next_media()
            return False

        # ── Strategy 1: CEF embedded browser ────────────────────────
        cef_browser = _get_cef_browser()
        if cef_browser is not None:
            Logger.info(f"SignagePlayer: Opening weblink via CEF (embedded in Kivy): {url}")
            try:
                self.ids.content_area.opacity = 0
            except Exception:
                pass
            # Attach resize handler so CEF follows Kivy window resizes
            from kivy.core.window import Window as KivyWindow
            _orig_on_resize = getattr(KivyWindow, '_on_resize', None)
            def _cef_resize(*args):
                try:
                    w, h = KivyWindow.size
                    cef_browser.resize(int(w), int(h))
                except Exception:
                    pass
                if _orig_on_resize:
                    try:
                        return _orig_on_resize(*args)
                    except Exception:
                        pass
            KivyWindow._on_resize = _cef_resize
            # Bind to size event as well
            try:
                KivyWindow.bind(size=_cef_resize)
            except Exception:
                pass

            cef_browser.show(url)
            Clock.unschedule(self.next_media)
            self._start_inactivity_watchdog(duration)
            self.preload_next_media()
            Logger.info("SignagePlayer: CEF embedded browser visible (inside Kivy window)")
            return True

        # ── Strategy 2: Subprocess Chrome/Edge (fallback) ────────────
        browser = _windows_find_browser()
        if not browser:
            Logger.error(
                "SignagePlayer: No embedded CEF and no Chrome/Edge found. "
                "Cannot display weblink on Windows."
            )
            self.consecutive_errors += 1
            self._skip_to_next_media()
            return False

        target_width, target_height = self._get_browser_target_size()

        try:
            Logger.info(f"SignagePlayer: Opening weblink via subprocess: {url} ({browser})")
            self._kill_weblink_preload()

            # Hide Kivy content
            from kivy.core.window import Window as KivyWindow
            try:
                self.ids.content_area.opacity = 0
                KivyWindow.minimize()
            except Exception:
                pass

            _Win32Overlay.show()

            self._weblink_proc = subprocess.Popen([
                browser,
                '--new-window',
                '--start-maximized',
                '--start-fullscreen',
                '--app=' + url,
                '--no-first-run',
                '--noerrdialogs',
                '--disable-infobars',
                '--incognito',
                '--no-default-browser-check',
                '--disable-session-crashed-bubble',
                '--disable-features=TranslateUI',
                '--disable-sync',
                '--disable-background-networking',
                '--window-position=0,0',
                f'--window-size={target_width},{target_height}',
                '--force-device-scale-factor=1',
                url,
            ], shell=False)

            def _hide_overlay(dt):
                _Win32Overlay.hide()
                try:
                    KivyWindow.raise_window()
                except Exception:
                    pass
            Clock.schedule_once(_hide_overlay, 1.5)

            Clock.unschedule(self.next_media)
            self._start_inactivity_watchdog(duration)
            self.preload_next_media()
            return True

        except Exception as e:
            Logger.error(f"SignagePlayer: Error opening weblink: {e}")
            _Win32Overlay.hide()
            self.consecutive_errors += 1
            self._skip_to_next_media()
            return False

    # Replace weblink handling
    signage_main.SignagePlayer.play_weblink = _windows_play_weblink

    # Patch the _get_browser_target_size to always return a reasonable size on Windows
    def _windows_get_browser_target_size(self):
        try:
            from kivy.core.window import Window
            width, height = Window.size
            if width >= 1280 and height >= 720:
                return int(width), int(height)
        except Exception:
            pass
        return 1920, 1080

    signage_main.SignagePlayer._get_browser_target_size = _windows_get_browser_target_size

    # Patch _start_inactivity_watchdog for Windows — /dev/input does not exist
    _original_watchdog = signage_main.SignagePlayer._start_inactivity_watchdog

    def _windows_watchdog(self, duration):
        """Windows watchdog: uses a simple timer since /dev/input is not available.
        
        Falls back to a fixed timer that advances after 'duration' seconds.
        """
        from kivy.clock import Clock
        import threading

        self._stop_inactivity_watchdog()
        stop_event = threading.Event()
        self._watchdog_stop = stop_event
        weblink_proc = self._weblink_proc

        def watchdog():
            import time
            # Simply wait for the duration, checking if Chromium exited early
            elapsed = 0.0
            step = 0.5
            while elapsed < duration and not stop_event.is_set():
                if weblink_proc is not None and weblink_proc.poll() is not None:
                    Clock.schedule_once(self.next_media, 0)
                    return
                time.sleep(step)
                elapsed += step
            if not stop_event.is_set():
                Clock.schedule_once(self.next_media, 0)

        self._weblink_watchdog_thread = threading.Thread(
            target=watchdog, daemon=True, name='weblink-watchdog-win'
        )
        self._weblink_watchdog_thread.start()

    signage_main.SignagePlayer._start_inactivity_watchdog = _windows_watchdog

    # ── Patch kill_weblink_after_frame for both CEF and subprocess ──
    def _windows_kill_weblink_after_frame(self):
        """Close the weblink (CEF or subprocess) immediately before next media."""
        import time
        from kivy.logger import Logger
        from kivy.clock import Clock
        self._stop_inactivity_watchdog()
        self._kill_weblink_preload()

        # Try CEF first
        cef_browser = _get_cef_browser()
        if cef_browser is not None and cef_browser.is_showing():
            Logger.info("SignagePlayer: Hiding CEF embedded browser")
            cef_browser.hide()
            self._weblink_proc = None
            return

        # Fallback: subprocess Chrome
        proc = self._weblink_proc
        self._weblink_proc = None

        if proc is None or proc.poll() is not None:
            return

        _Win32Overlay.show()
        Logger.info("SignagePlayer: Killing Chromium subprocess immediately")
        _windows_kill_process_tree(proc)
        time.sleep(0.1)
        _bring_kivy_to_front()
        _Win32Overlay.hide()
    signage_main.SignagePlayer._kill_weblink_after_frame = _windows_kill_weblink_after_frame

    # ── Patch play_current_media — same immediate-kill logic ────────
    _original_play_current = signage_main.SignagePlayer.play_current_media

    def _windows_play_current_media(self, force_reload=False, _after_weblink=False):
        """Wrapped play_current_media — closes weblink immediately on transition."""
        if not _after_weblink:
            # Kill CEF browser if showing
            cef_browser = _get_cef_browser()
            if cef_browser is not None and cef_browser.is_showing():
                cef_browser.hide()
                self._weblink_proc = None

            # Kill subprocess Chrome if running
            proc = self._weblink_proc
            if proc is not None and proc.poll() is None:
                _Win32Overlay.show()
                _windows_kill_process_tree(proc)
                self._weblink_proc = None
                self._stop_inactivity_watchdog()
                self._kill_weblink_preload()
                _bring_kivy_to_front()
                _Win32Overlay.hide()

        return _original_play_current(self, force_reload=force_reload, _after_weblink=_after_weblink)

    signage_main.SignagePlayer.play_current_media = _windows_play_current_media

    # Patch _prewarm_weblink for Windows — disabled for now.
    # The off-screen Chrome window on Windows can interfere with:
    #   - Audio playback (Chrome claims audio device)
    #   - GPU resources (Chrome's GPU process runs in background)
    #   - Taskbar icons showing duplicate Chrome windows
    # Pre-warming is less critical on desktop where launch is already fast.
    def _windows_prewarm_weblink(self, url):
        pass  # Disabled on Windows — desktop launch is fast enough
    signage_main.SignagePlayer._prewarm_weblink = _windows_prewarm_weblink

    # Patch cleanup of temp auth file (was using /tmp/)
    _original_connection_test = signage_main.SettingsPopup.test_connection

    def _windows_test_connection(self):
        """Test connection with Windows-safe temp file path."""
        import tempfile as _tf
        import os as _os
        from player_auth import PlayerAuth
        import re
        import threading
        from kivy.clock import Clock

        self.ids.connection_status.text = 'Testing connection...'
        self.ids.connection_status.color = (1, 0.7, 0, 1)

        def run_test():
            try:
                server_ip = self.ids.server_input.text.strip()
                screen_name = self.ids.screen_input.text.strip()
                quickconnect = self.ids.quickconnect_input.text.strip()
                port = self.ids.port_input.text.strip() or self.player.config.get('port', '')
                use_https = self.player.config.get('use_https', True)
                verify_ssl = self.player.config.get('verify_ssl', True)

                if not all([server_ip, screen_name, quickconnect]):
                    Clock.schedule_once(lambda dt: self.update_connection_status('Error: Fill all fields', False))
                    return

                if server_ip.startswith('http://') or server_ip.startswith('https://'):
                    server_url = server_ip
                    if ':' not in server_ip.replace('https://', '').replace('http://', ''):
                        if port and port not in ('443', '80'):
                            server_url = f"{server_ip}:{port}"
                else:
                    protocol = "https" if use_https else "http"
                    if ':' in server_ip:
                        server_url = f"{protocol}://{server_ip}"
                    else:
                        server_url = f"{protocol}://{server_ip}:{port}" if port else f"{protocol}://{server_ip}"

                # Use Windows temp path
                temp_file = _os.path.join(_tf.gettempdir(), 'temp_auth_test.json')
                auth = PlayerAuth(temp_file, use_https=use_https, verify_ssl=verify_ssl)
                success, error = auth.authenticate(server_url=server_url, hostname=screen_name, quickconnect_code=quickconnect)

                try:
                    if _os.path.exists(temp_file):
                        _os.remove(temp_file)
                except Exception:
                    pass

                if success:
                    player_name = auth.get_player_name()
                    Clock.schedule_once(lambda dt: self.update_connection_status(f'✓ Connected: {player_name}', True))
                else:
                    Clock.schedule_once(lambda dt: self.update_connection_status(f'✗ Failed: {error}', False))

            except Exception as e:
                Clock.schedule_once(lambda dt: self.update_connection_status(f'✗ Error: {str(e)}', False))

        threading.Thread(target=run_test, daemon=True).start()

    signage_main.SettingsPopup.test_connection = _windows_test_connection

    return signage_main


# =====================================================================
# 4. Adjust path so we can import the src modules
# =====================================================================
# When run from PyInstaller .exe: the runtime hook inserts paths.
# When run as plain python, we add src/ relative to this file.
_script_dir = Path(__file__).resolve().parent
_project_root = _script_dir.parent
_src_dir = _project_root / 'src'

for p in [str(_src_dir), str(_project_root)]:
    if p not in sys.path:
        sys.path.insert(0, p)

# =====================================================================
# 5. Determine the local data directory (next to the executable)
# =====================================================================
# The pyi_runtime_hook.py (when packaged) sets KIWY_DATA_DIR.
# When running in dev mode from python, use the project root.
# The executable will create its own local folders for playlist,
# media, config, and logs where the executable is launched.
DATA_DIR = os.environ.get('KIWY_DATA_DIR', str(_project_root))

# Create local data folders NEXT TO the executable
os.makedirs(os.path.join(DATA_DIR, 'config', 'resources'), exist_ok=True)
os.makedirs(os.path.join(DATA_DIR, 'media'), exist_ok=True)
os.makedirs(os.path.join(DATA_DIR, 'media', 'edited_media'), exist_ok=True)
os.makedirs(os.path.join(DATA_DIR, 'playlists'), exist_ok=True)
os.makedirs(os.path.join(DATA_DIR, 'logs'), exist_ok=True)
os.makedirs(os.path.join(DATA_DIR, 'config', 'certs'), exist_ok=True)

# =====================================================================
# 6. Set Kivy config BEFORE importing Kivy
# =====================================================================
os.environ['KIVY_NO_FILELOG'] = '1'  # Avoid file logging issues on Windows
os.environ['KIVY_HOME'] = os.path.join(DATA_DIR, '.kivy')

from kivy.config import Config
Config.set('kivy', 'keyboard_mode', '')  # Disable default virtual keyboard
Config.set('graphics', 'fullscreen', '0')
Config.set('graphics', 'window_state', 'maximized')
Config.set('graphics', 'multisampling', '0')
Config.set('graphics', 'fast_rgba', '1')
Config.set('kivy', 'log_level', 'warning')

# =====================================================================
# 7. Patch the main module, then run the app
# =====================================================================
if __name__ == '__main__':
    try:
        # Write a startup marker so we know the .exe at least launched
        try:
            os.makedirs(os.path.join(DATA_DIR, 'logs'), exist_ok=True)
            marker = os.path.join(DATA_DIR, 'logs', 'startup_marker.txt')
            with open(marker, 'w') as f:
                f.write(f"run_win.py started at {__import__('time').time()}\n")
        except Exception:
            pass

        # Apply all Windows patches before launching
        try:
            patched_main = _patch_main()
        except Exception as e:
            # Catch early import errors (pre-Logger) to a file
            import traceback
            try:
                err_log = os.path.join(DATA_DIR, 'logs', 'startup_error.log')
                with open(err_log, 'w') as f:
                    f.write(f"Error in _patch_main(): {e}\n")
                    traceback.print_exc(file=f)
            except Exception:
                pass
            raise  # Re-raise so console shows it too

        from kivy.logger import Logger
        Logger.info("=" * 80)
        Logger.info("Kiwy Signage Player - Windows Edition")
        Logger.info(f"Python: {sys.version}")
        Logger.info(f"Platform: {platform.platform()}")
        Logger.info(f"Data directory: {DATA_DIR}")
        Logger.info("=" * 80)

        # Patch base_dir in SignagePlayer instances to point to local data folder
        _original_init = patched_main.SignagePlayer.__init__

        def _patched_init(self, **kwargs):
            """Override SignagePlayer.__init__ to use local data folders.
            
            Creates all necessary folders (config, media, playlists, logs)
            in the same directory where the executable is launched.
            """
            # Call parent Widget.__init__
            _original_init(self, **kwargs)
            # Now override ALL paths to point to local data directory
            # (where the .exe is located)
            self.base_dir = DATA_DIR
            self.config_dir = os.path.join(DATA_DIR, 'config')
            self.media_dir = os.path.join(DATA_DIR, 'media')
            self.playlists_dir = os.path.join(DATA_DIR, 'playlists')
            self.config_file = os.path.join(self.config_dir, 'app_config.json')
            self.resources_path = os.path.join(self.config_dir, 'resources')
            self.heartbeat_file = os.path.join(DATA_DIR, '.player_heartbeat')
            # Ensure all required folders exist locally
            for directory in [
                self.config_dir,
                self.resources_path,
                os.path.join(self.media_dir, 'edited_media'),
                self.playlists_dir,
                os.path.join(DATA_DIR, 'logs'),
                os.path.join(DATA_DIR, 'config', 'certs'),
            ]:
                os.makedirs(directory, exist_ok=True)

        patched_main.SignagePlayer.__init__ = _patched_init

        # Patch SSLManager cert directory to use local data folder
        # ssl_utils is imported by player_auth.py and get_playlists_v2.py, not main.py
        import ssl_utils
        ssl_utils.SSLManager.CERT_DIR = os.path.join(DATA_DIR, 'config', 'certs')
        ssl_utils.SSLManager.CERT_FILE = os.path.join(
            ssl_utils.SSLManager.CERT_DIR, 'server_cert.pem'
        )
        ssl_utils.SSLManager.CERT_INFO_FILE = os.path.join(
            ssl_utils.SSLManager.CERT_DIR, 'cert_info.json'
        )

        # Run the app
        try:
            app = patched_main.SignagePlayerApp()
            app.run()
        except KeyboardInterrupt:
            Logger.info("Application stopped by user (Ctrl+C)")
        except SystemExit as _se:
            Logger.critical(f"Kivy SystemExit (likely window provider missing): {_se}")
            try:
                crash_log = os.path.join(DATA_DIR, 'logs', 'crash.log')
                with open(crash_log, 'w') as f:
                    f.write(f"Kivy SystemExit: {_se}\n")
                    f.write("This usually means Kivy could not find a window provider on this system.\n")
            except Exception:
                pass
            _show_error_box(
                "Kiwy Signage Player - Kivy Error",
                f"Kivy exited: {_se}\n\n"
                "This usually means Kivy could not create a window.\n"
                "Check your GPU drivers and DirectX installation.\n\n"
                "See logs/crash.log for details."
            )
            sys.exit(1)
        except Exception as e:
            Logger.critical(f"Fatal error: {e}")
            Logger.exception("Full traceback:")
            # Also write to a crash log next to the executable
            try:
                import traceback
                crash_log = os.path.join(DATA_DIR, 'logs', 'crash.log')
                with open(crash_log, 'w') as f:
                    f.write(f"Fatal error: {e}\n")
                    traceback.print_exc(file=f)
            except Exception:
                pass
            sys.exit(1)
        finally:
            Logger.info("Application shutdown complete")
    except BaseException as _top_e:
        # Catch any error BEFORE Logger is available (including SystemExit)
        import traceback as _tb
        _trace = _tb.format_exc()
        try:
            _crash_log = os.path.join(DATA_DIR, 'logs', 'fatal_crash.log')
            with open(_crash_log, 'w') as _f:
                _f.write(f"FATAL (pre-Logger): {_top_e}\n")
                _f.write(_trace)
        except Exception:
            pass
        _show_error_box(
            "Kiwy Signage Player - Startup Error",
            f"{_top_e}\n\nSee logs/fatal_crash.log for details."
        )
        raise  # Re-raise so .exe still shows the error
