"""Shared utilities for ui-query scripts."""

import sys

import pyatspi

# Debug mode - set via set_debug() or --debug flag in scripts
_debug = False


def set_debug(enabled):
    """Enable or disable debug mode for verbose error logging."""
    global _debug
    _debug = enabled


def log_debug(msg):
    """Log message to stderr if debug mode is enabled."""
    if _debug:
        print(f"[DEBUG] {msg}", file=sys.stderr)


def safe_get_attr(accessible, attr, default=""):
    """Safely get an attribute from an accessible, handling stale objects.

    AT-SPI objects can become stale at any time (window closed, app crashed).
    This helper catches those errors and returns a default value.
    """
    try:
        value = getattr(accessible, attr)
        return value if value is not None else default
    except Exception as e:
        log_debug(f"Failed to get {attr}: {e}")
        return default


def find_windows(pattern=None, app_name=None, all_windows=False):
    """Find windows matching criteria.

    Args:
        pattern: Substring to match in window name (case-insensitive)
        app_name: Substring to match in application name (case-insensitive)
        all_windows: If True, return all windows (ignore pattern/app_name)

    Returns:
        List of dicts with 'app', 'window', and 'accessible' keys
    """
    desktop = pyatspi.Registry.getDesktop(0)
    matches = []

    for i in range(desktop.childCount):
        app = desktop.getChildAtIndex(i)
        if not app:
            continue

        # Filter by app name
        if app_name and app_name.lower() not in (app.name or "").lower():
            continue

        for j in range(app.childCount):
            window = app.getChildAtIndex(j)
            if not window:
                continue

            window_name = window.name or ""

            # Filter by window pattern
            if pattern and pattern.lower() not in window_name.lower():
                continue

            # Include if: all_windows requested, or any filter was specified
            if all_windows or pattern or app_name:
                matches.append({
                    "app": app.name or "(unnamed)",
                    "window": window_name,
                    "accessible": window,
                })

    return matches


def get_all_windows():
    """Get all windows without filtering.

    Returns:
        List of dicts with 'accessible' key (minimal info for searching)
    """
    desktop = pyatspi.Registry.getDesktop(0)
    windows = []

    for i in range(desktop.childCount):
        app = desktop.getChildAtIndex(i)
        if not app:
            continue
        for j in range(app.childCount):
            window = app.getChildAtIndex(j)
            if window:
                windows.append({"accessible": window})

    return windows


def find_elements(accessible, role=None, name=None, results=None, max_depth=15, depth=0, limit=20):
    """Recursively search for elements matching role/name criteria.

    Args:
        accessible: Root accessible to search from
        role: Role substring to match (case-insensitive)
        name: Name substring to match (case-insensitive)
        results: Accumulator list (internal use)
        max_depth: Maximum tree depth to traverse
        depth: Current depth (internal use)
        limit: Maximum results to return

    Returns:
        List of matching accessible objects
    """
    if results is None:
        results = []

    if len(results) >= limit or depth > max_depth:
        return results

    try:
        matches = True

        if role:
            elem_role = accessible.getRoleName().lower().replace(" ", "-")
            if role.lower() not in elem_role:
                matches = False

        if name and matches:
            elem_name = (accessible.name or "").lower()
            if name.lower() not in elem_name:
                matches = False

        if matches and (role or name):
            results.append(accessible)

        for i in range(accessible.childCount):
            if len(results) >= limit:
                break
            try:
                child = accessible.getChildAtIndex(i)
                if child:
                    find_elements(child, role, name, results, max_depth, depth + 1, limit)
            except Exception as e:
                log_debug(f"Error accessing child {i}: {e}")
                continue

    except Exception as e:
        log_debug(f"Error searching element: {e}")

    return results
