"""Win11 kiosk helpers: keep the pygame/SDL window above Explorer and the taskbar."""
from __future__ import annotations

import ctypes
import time
from ctypes import wintypes

HWND_TOPMOST = -1
SW_SHOW = 5
SWP_SHOWWINDOW = 0x0040
SWP_FRAMECHANGED = 0x0020
MONITOR_DEFAULTTOPRIMARY = 1
ASFW_ANY = 0xFFFFFFFF
ABM_SETSTATE = 0x0000000A
ABS_AUTOHIDE = 0x00000001
ABS_ALWAYSONTOP = 0x00000002

_last_refresh = 0.0
_boot_apply_until = 0.0
_taskbar_auto_hidden = False


class _RECT(ctypes.Structure):
    _fields_ = [
        ("left", ctypes.c_long),
        ("top", ctypes.c_long),
        ("right", ctypes.c_long),
        ("bottom", ctypes.c_long),
    ]


class _MONITORINFO(ctypes.Structure):
    _fields_ = [
        ("cbSize", wintypes.DWORD),
        ("rcMonitor", _RECT),
        ("rcWork", _RECT),
        ("dwFlags", wintypes.DWORD),
    ]


class _APPBARDATA(ctypes.Structure):
    _fields_ = [
        ("cbSize", wintypes.DWORD),
        ("hWnd", wintypes.HWND),
        ("uCallbackMessage", wintypes.UINT),
        ("uEdge", wintypes.UINT),
        ("rc", wintypes.RECT),
        ("lParam", wintypes.LPARAM),
    ]


def _primary_monitor_bounds() -> tuple[int, int, int, int]:
    """Return left, top, right, bottom for the primary monitor (includes taskbar area)."""
    user32 = ctypes.windll.user32
    hmon = user32.MonitorFromPoint(wintypes.POINT(0, 0), MONITOR_DEFAULTTOPRIMARY)
    info = _MONITORINFO()
    info.cbSize = ctypes.sizeof(_MONITORINFO)
    if not user32.GetMonitorInfoW(hmon, ctypes.byref(info)):
        width = user32.GetSystemMetrics(0)
        height = user32.GetSystemMetrics(1)
        return 0, 0, width, height
    rect = info.rcMonitor
    return rect.left, rect.top, rect.right, rect.bottom


def _set_taskbar_auto_hide(enabled: bool) -> None:
    global _taskbar_auto_hidden
    shell32 = ctypes.windll.shell32
    appbar = _APPBARDATA()
    appbar.cbSize = ctypes.sizeof(_APPBARDATA)
    appbar.lParam = ABS_AUTOHIDE if enabled else ABS_ALWAYSONTOP
    shell32.SHAppBarMessage(ABM_SETSTATE, ctypes.byref(appbar))
    _taskbar_auto_hidden = enabled


def _pygame_hwnd():
    try:
        import pygame
    except ImportError:
        return None
    return pygame.display.get_wm_info().get("window")


def _try_set_foreground(hwnd) -> None:
    user32 = ctypes.windll.user32
    kernel32 = ctypes.windll.kernel32
    user32.AllowSetForegroundWindow(ASFW_ANY)
    if user32.SetForegroundWindow(hwnd):
        return
    foreground = user32.GetForegroundWindow()
    if not foreground or foreground == hwnd:
        return
    foreground_thread = user32.GetWindowThreadProcessId(foreground, None)
    current_thread = kernel32.GetCurrentThreadId()
    if foreground_thread and foreground_thread != current_thread:
        user32.AttachThreadInput(foreground_thread, current_thread, True)
        user32.SetForegroundWindow(hwnd)
        user32.AttachThreadInput(foreground_thread, current_thread, False)
    user32.BringWindowToTop(hwnd)


def apply_win32_kiosk_window() -> bool:
    """Cover the full primary monitor and sit above Explorer + taskbar."""
    hwnd = _pygame_hwnd()
    if not hwnd:
        return False

    user32 = ctypes.windll.user32
    left, top, right, bottom = _primary_monitor_bounds()
    width = max(1, right - left)
    height = max(1, bottom - top)
    flags = SWP_SHOWWINDOW | SWP_FRAMECHANGED

    if not _taskbar_auto_hidden:
        _set_taskbar_auto_hide(True)

    user32.ShowWindow(hwnd, SW_SHOW)
    # Toggle topmost so Win11 re-evaluates z-order over the taskbar shell band.
    user32.SetWindowPos(hwnd, HWND_TOPMOST, left, top, width, height, flags)
    user32.SetWindowPos(hwnd, HWND_TOPMOST, left, top, width, height, flags)
    user32.BringWindowToTop(hwnd)
    _try_set_foreground(hwnd)
    return True


def start_win32_kiosk_bootstrap(*, duration_s: float = 8.0) -> None:
    """Re-apply topmost frequently right after startup (HWND may not be ready on first call)."""
    global _boot_apply_until
    _boot_apply_until = time.monotonic() + max(1.0, float(duration_s))
    apply_win32_kiosk_window()


def maybe_refresh_win32_kiosk_window(*, interval_s: float = 2.0) -> None:
    """Re-apply topmost during boot bootstrap and periodically after."""
    global _last_refresh
    now = time.monotonic()
    if now < _boot_apply_until:
        apply_win32_kiosk_window()
        return
    if now - _last_refresh < interval_s:
        return
    _last_refresh = now
    apply_win32_kiosk_window()
