"""What's on screen right now — the active window's title, and whether
something is running fullscreen.

Two consumers:
  * annotate() tacks the focused window's title onto what you said, so
    "what's this error?" has a referent without you describing it (the desk
    API takes text only, so this is a text annotation — no screenshot upload).
  * is_fullscreen_active() feeds do-not-disturb: the pet shouldn't announce
    anything over a call or a fullscreen game.

Everything here is best-effort and must never raise: on a locked-down Wayland
session none of it is available, and the correct behaviour is simply "no
context", not a crashed pipeline. The subprocess *parsing* is split into pure
functions so it can be tested without a display server.
"""

from __future__ import annotations

import platform
import re
import shutil
import subprocess
from typing import Optional

_TIMEOUT = 2.0
_MAX_TITLE_CHARS = 160

# Titles that are just the desktop itself — annotating with these is noise.
_BORING_TITLES = {"", "desktop", "@!0,0;bdib", "plasmashell", "gnome-shell", "xfdesktop"}


def _run(args: list[str]) -> Optional[str]:
    try:
        completed = subprocess.run(args, capture_output=True, text=True, timeout=_TIMEOUT)
    except Exception:
        return None
    if completed.returncode != 0:
        return None
    return completed.stdout


# ── pure parsing helpers (unit tested; no display server needed) ─────────────

def parse_xprop_window_id(output: str) -> Optional[str]:
    """`xprop -root _NET_ACTIVE_WINDOW` -> '_NET_ACTIVE_WINDOW(WINDOW): window id # 0x3c00007'"""
    match = re.search(r"(0x[0-9a-fA-F]+)", output or "")
    if not match or int(match.group(1), 16) == 0:
        return None
    return match.group(1)


def parse_xprop_window_name(output: str) -> Optional[str]:
    """`xprop -id <id> _NET_WM_NAME` -> '_NET_WM_NAME(UTF8_STRING) = "Firefox"'"""
    match = re.search(r'=\s*"(.*)"\s*$', (output or "").strip(), re.DOTALL)
    if not match:
        return None
    return match.group(1).strip()


def parse_xprop_fullscreen(output: str) -> bool:
    return "_NET_WM_STATE_FULLSCREEN" in (output or "")


def clean_title(title: Optional[str]) -> Optional[str]:
    title = (title or "").strip().replace("\n", " ")
    if title.lower() in _BORING_TITLES:
        return None
    if len(title) > _MAX_TITLE_CHARS:
        title = title[: _MAX_TITLE_CHARS - 1].rstrip() + "…"
    return title or None


def annotate(text: str, title: Optional[str]) -> str:
    """Attach the window title as an explicit aside rather than splicing it
    into the sentence, so the model can ignore it when it's irrelevant."""
    text = (text or "").strip()
    title = clean_title(title)
    if not text or not title:
        return text
    return f"{text}\n\n[on screen right now: {title}]"


# ── platform probes ──────────────────────────────────────────────────────────

def _linux_active_window_id() -> Optional[str]:
    if not shutil.which("xprop"):
        return None
    output = _run(["xprop", "-root", "_NET_ACTIVE_WINDOW"])
    return parse_xprop_window_id(output or "")


def _linux_title() -> Optional[str]:
    if shutil.which("xdotool"):
        output = _run(["xdotool", "getactivewindow", "getwindowname"])
        if output and output.strip():
            return output.strip()
    window_id = _linux_active_window_id()
    if window_id is None:
        return None
    for prop in ("_NET_WM_NAME", "WM_NAME"):
        output = _run(["xprop", "-id", window_id, prop])
        title = parse_xprop_window_name(output or "")
        if title:
            return title
    return None


def _windows_title() -> Optional[str]:
    try:
        import ctypes

        user32 = ctypes.windll.user32
        handle = user32.GetForegroundWindow()
        if not handle:
            return None
        length = user32.GetWindowTextLengthW(handle)
        buffer = ctypes.create_unicode_buffer(length + 1)
        user32.GetWindowTextW(handle, buffer, length + 1)
        return buffer.value or None
    except Exception:
        return None


def _macos_title() -> Optional[str]:
    script = (
        'tell application "System Events" to get name of first application process '
        "whose frontmost is true"
    )
    output = _run(["osascript", "-e", script])
    return (output or "").strip() or None


def active_window_title() -> Optional[str]:
    """Focused window's title, or None if the platform won't tell us."""
    try:
        system = platform.system()
        if system == "Linux":
            return clean_title(_linux_title())
        if system == "Windows":
            return clean_title(_windows_title())
        if system == "Darwin":
            return clean_title(_macos_title())
    except Exception:
        pass
    return None


def is_fullscreen_active() -> bool:
    """True when the focused window is fullscreen (call, game, presentation).
    False whenever we can't tell — do-not-disturb should be something you opt
    into, not something a failed probe turns on."""
    try:
        system = platform.system()
        if system == "Linux":
            window_id = _linux_active_window_id()
            if window_id is None:
                return False
            return parse_xprop_fullscreen(_run(["xprop", "-id", window_id, "_NET_WM_STATE"]) or "")
        if system == "Windows":
            import ctypes
            from ctypes import wintypes

            user32 = ctypes.windll.user32
            handle = user32.GetForegroundWindow()
            if not handle:
                return False
            rect = wintypes.RECT()
            user32.GetWindowRect(handle, ctypes.byref(rect))
            screen_w = user32.GetSystemMetrics(0)
            screen_h = user32.GetSystemMetrics(1)
            return (rect.right - rect.left) >= screen_w and (rect.bottom - rect.top) >= screen_h
    except Exception:
        pass
    return False


def context_for(text: str) -> str:
    """What the controller sends: *text* plus the active window title, when
    SCREEN_CONTEXT is on and there's a title worth mentioning."""
    from . import config

    if not config.SCREEN_CONTEXT:
        return text
    return annotate(text, active_window_title())
