"""
Screen capture and display information using Windows API via C# helper
"""

import subprocess
import os
from typing import Optional, List, Tuple, Dict
from pathlib import Path

# Path to the compiled helper
_HELPER_EXE = Path(__file__).parent / "helpers" / "WindowsHelper.exe"


def _ensure_helper_built():
    """Ensure the helper executable exists"""
    if not _HELPER_EXE.exists():
        raise FileNotFoundError(
            f"WindowsHelper.exe not found at {_HELPER_EXE}. "
            "Please run build_windows_helper.ps1 to compile it."
        )


def screenshot(
    filepath: str,
    screen: int = 0,
    region: Optional[Tuple[int, int, int, int]] = None
) -> bool:
    """
    Take a screenshot and save it to a file.

    Args:
        filepath: Path to save the screenshot (supports .png, .jpg, .bmp, .gif)
        screen: Screen index to capture (0 for primary, 1+ for additional monitors)
        region: Optional region to capture as (x, y, width, height)

    Returns:
        True if successful, False otherwise

    Examples:
        >>> # Capture entire primary screen
        >>> screenshot("screenshot.png")

        >>> # Capture secondary monitor
        >>> screenshot("monitor2.png", screen=1)

        >>> # Capture specific region (x=100, y=100, width=800, height=600)
        >>> screenshot("region.png", region=(100, 100, 800, 600))
    """
    _ensure_helper_built()

    try:
        cmd = [str(_HELPER_EXE), "screenshot", filepath, f"--screen={screen}"]

        if region:
            x, y, w, h = region
            cmd.append(f"--region={x},{y},{w},{h}")

        result = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            timeout=10,
            creationflags=subprocess.CREATE_NO_WINDOW if os.name == 'nt' else 0
        )

        if result.returncode == 0:
            print(f"Screenshot saved: {result.stdout.strip()}")
            return True
        else:
            print(f"Error: {result.stderr.strip()}")
            return False

    except Exception as e:
        print(f"Error taking screenshot: {e}")
        return False


def capture(filepath: str = "screenshot.png", screen: int = 0) -> bool:
    """
    Alias for screenshot() - capture screen to file.

    Args:
        filepath: Path to save the screenshot
        screen: Screen index to capture

    Returns:
        True if successful, False otherwise

    Example:
        >>> capture("desktop.png")
    """
    return screenshot(filepath, screen)


def get_display_info() -> Optional[Dict]:
    """
    Get detailed information about all displays.

    Returns:
        Dictionary containing display information, or None if error

    Example:
        >>> info = get_display_info()
        >>> if info:
        ...     print(f"Number of screens: {info['screen_count']}")
        ...     for i, screen in enumerate(info['screens']):
        ...         print(f"Screen {i}: {screen['width']}x{screen['height']}")
    """
    _ensure_helper_built()

    try:
        result = subprocess.run(
            [str(_HELPER_EXE), "display", "info"],
            capture_output=True,
            text=True,
            timeout=5,
            creationflags=subprocess.CREATE_NO_WINDOW if os.name == 'nt' else 0
        )

        if result.returncode == 0:
            lines = result.stdout.strip().split('\n')
            info = {}
            screens = []

            current_screen = {}
            screen_index = -1

            for line in lines:
                if '=' in line:
                    key, value = line.split('=', 1)

                    if key == 'ScreenCount':
                        info['screen_count'] = int(value)
                    elif key.startswith('Screen'):
                        parts = key.split('_', 1)
                        if len(parts) == 2:
                            idx_str = parts[0].replace('Screen', '')
                            idx = int(idx_str)
                            prop = parts[1]

                            if idx != screen_index:
                                if current_screen:
                                    screens.append(current_screen)
                                current_screen = {}
                                screen_index = idx

                            if prop == 'Bounds':
                                x, y, w, h = map(int, value.split(','))
                                current_screen['x'] = x
                                current_screen['y'] = y
                                current_screen['width'] = w
                                current_screen['height'] = h
                            elif prop == 'WorkingArea':
                                x, y, w, h = map(int, value.split(','))
                                current_screen['working_x'] = x
                                current_screen['working_y'] = y
                                current_screen['working_width'] = w
                                current_screen['working_height'] = h
                            elif prop == 'Primary':
                                current_screen['primary'] = value.lower() == 'true'
                            elif prop == 'BitsPerPixel':
                                current_screen['bits_per_pixel'] = int(value)
                            elif prop == 'DeviceName':
                                current_screen['device_name'] = value

            if current_screen:
                screens.append(current_screen)

            info['screens'] = screens
            return info

        return None

    except Exception as e:
        print(f"Error getting display info: {e}")
        return None


def list_displays() -> Optional[List[str]]:
    """
    Get a simple list of all displays with their resolutions.

    Returns:
        List of display descriptions, or None if error

    Example:
        >>> displays = list_displays()
        >>> if displays:
        ...     for display in displays:
        ...         print(display)
    """
    _ensure_helper_built()

    try:
        result = subprocess.run(
            [str(_HELPER_EXE), "display", "list"],
            capture_output=True,
            text=True,
            timeout=5,
            creationflags=subprocess.CREATE_NO_WINDOW if os.name == 'nt' else 0
        )

        if result.returncode == 0:
            return result.stdout.strip().split('\n')

        return None

    except Exception as e:
        print(f"Error listing displays: {e}")
        return None


def get_primary_resolution() -> Optional[Tuple[int, int]]:
    """
    Get the resolution of the primary display.

    Returns:
        Tuple of (width, height), or None if error

    Example:
        >>> width, height = get_primary_resolution()
        >>> print(f"Primary display: {width}x{height}")
    """
    _ensure_helper_built()

    try:
        result = subprocess.run(
            [str(_HELPER_EXE), "display", "primary"],
            capture_output=True,
            text=True,
            timeout=5,
            creationflags=subprocess.CREATE_NO_WINDOW if os.name == 'nt' else 0
        )

        if result.returncode == 0:
            resolution = result.stdout.strip()
            width, height = map(int, resolution.split('x'))
            return (width, height)

        return None

    except Exception as e:
        print(f"Error getting primary resolution: {e}")
        return None


def get_screen_count() -> int:
    """
    Get the number of connected displays.

    Returns:
        Number of displays, or 0 if error

    Example:
        >>> count = get_screen_count()
        >>> print(f"Number of displays: {count}")
    """
    info = get_display_info()
    if info:
        return info.get('screen_count', 0)
    return 0


def get_pixel_color(x: int, y: int) -> Optional[Tuple[int, int, int]]:
    """
    Get the RGB color of a pixel at the specified screen coordinates.

    Args:
        x: X coordinate on screen
        y: Y coordinate on screen

    Returns:
        Tuple of (R, G, B) values (0-255), or None if error

    Example:
        >>> color = get_pixel_color(100, 100)
        >>> if color:
        ...     r, g, b = color
        ...     print(f"Color at (100, 100): RGB({r}, {g}, {b})")
    """
    _ensure_helper_built()

    try:
        result = subprocess.run(
            [str(_HELPER_EXE), "pixel", "get", str(x), str(y)],
            capture_output=True,
            text=True,
            timeout=5,
            creationflags=subprocess.CREATE_NO_WINDOW if os.name == 'nt' else 0
        )

        if result.returncode == 0:
            # Parse RGB values from output like "R,G,B"
            rgb = result.stdout.strip().split(',')
            if len(rgb) == 3:
                return (int(rgb[0]), int(rgb[1]), int(rgb[2]))

        return None

    except Exception as e:
        print(f"Error getting pixel color: {e}")
        return None


def wait_for_color_change(
    x: int,
    y: int,
    timeout: float = 30.0,
    poll_interval: float = 0.1
) -> Optional[Tuple[int, int, int]]:
    """
    Wait for the pixel color at the specified position to change.

    Args:
        x: X coordinate on screen
        y: Y coordinate on screen
        timeout: Maximum time to wait in seconds (default: 30)
        poll_interval: How often to check in seconds (default: 0.1)

    Returns:
        New RGB color tuple when change is detected, or None if timeout

    Example:
        >>> # Wait for color change at position (500, 300)
        >>> new_color = wait_for_color_change(500, 300, timeout=10)
        >>> if new_color:
        ...     print(f"Color changed to: RGB{new_color}")
        ... else:
        ...     print("No change detected within timeout")
    """
    import time

    initial_color = get_pixel_color(x, y)
    if initial_color is None:
        return None

    start_time = time.time()

    while (time.time() - start_time) < timeout:
        current_color = get_pixel_color(x, y)
        if current_color and current_color != initial_color:
            return current_color

        time.sleep(poll_interval)

    return None


def wait_for_color(
    x: int,
    y: int,
    target_color: Tuple[int, int, int],
    tolerance: int = 0,
    timeout: float = 30.0,
    poll_interval: float = 0.1
) -> bool:
    """
    Wait for a pixel to become a specific color.

    Args:
        x: X coordinate on screen
        y: Y coordinate on screen
        target_color: Target RGB color tuple (R, G, B)
        tolerance: Allowed difference per channel (default: 0 = exact match)
        timeout: Maximum time to wait in seconds (default: 30)
        poll_interval: How often to check in seconds (default: 0.1)

    Returns:
        True if target color detected, False if timeout

    Example:
        >>> # Wait for pixel to turn red
        >>> if wait_for_color(500, 300, (255, 0, 0), tolerance=10, timeout=5):
        ...     print("Pixel turned red!")
        ... else:
        ...     print("Timeout waiting for color")
    """
    import time

    def colors_match(c1, c2, tol):
        """Check if two colors match within tolerance"""
        return all(abs(a - b) <= tol for a, b in zip(c1, c2))

    start_time = time.time()

    while (time.time() - start_time) < timeout:
        current_color = get_pixel_color(x, y)
        if current_color and colors_match(current_color, target_color, tolerance):
            return True

        time.sleep(poll_interval)

    return False


def find_color_on_screen(
    target_color: Tuple[int, int, int],
    region: Optional[Tuple[int, int, int, int]] = None,
    tolerance: int = 0,
    sample_rate: int = 1
) -> Optional[Tuple[int, int]]:
    """
    Search for a specific color on screen and return its position.

    Args:
        target_color: RGB color to search for (R, G, B)
        region: Search region as (x, y, width, height), or None for full screen
        tolerance: Allowed difference per channel (default: 0 = exact match)
        sample_rate: Check every Nth pixel (1=every pixel, 2=every other, etc.)

    Returns:
        Tuple of (x, y) where color was found, or None if not found

    Example:
        >>> # Find a red pixel on screen
        >>> pos = find_color_on_screen((255, 0, 0), tolerance=10)
        >>> if pos:
        ...     x, y = pos
        ...     print(f"Found red pixel at ({x}, {y})")
    """
    def colors_match(c1, c2, tol):
        """Check if two colors match within tolerance"""
        return all(abs(a - b) <= tol for a, b in zip(c1, c2))

    # Get screen dimensions
    if region is None:
        res = get_primary_resolution()
        if res is None:
            return None
        width, height = res
        x_start, y_start = 0, 0
    else:
        x_start, y_start, width, height = region

    # Search for the color
    for y in range(y_start, y_start + height, sample_rate):
        for x in range(x_start, x_start + width, sample_rate):
            current_color = get_pixel_color(x, y)
            if current_color and colors_match(current_color, target_color, tolerance):
                return (x, y)

    return None
