import ctypes
import os
from typing import Any, Dict, Optional

from .lifecycle import hard_exit
from .message_loop import run_message_loop
from .webview import WebView
from .window import create_window, set_resize_callback

_DEBUG_ENV = "WEBVIEW_FORM_DEBUG"


def _debug_log(message: str) -> None:
    if os.environ.get(_DEBUG_ENV) == "1":
        print(message, flush=True)


_COINIT_APARTMENTTHREADED = 0x2
_RPC_E_CHANGED_MODE = -2147417850


def _enable_high_dpi_support() -> None:
    """
    Enable high DPI support to prevent blurry rendering.
    Tries the best available API in order of preference.
    """
    # Try Windows 10 1703+ API (best quality)
    try:
        # SetProcessDpiAwarenessContext takes DPI_AWARENESS_CONTEXT handle
        SetProcessDpiAwarenessContext = (
            ctypes.windll.user32.SetProcessDpiAwarenessContext
        )
        SetProcessDpiAwarenessContext.argtypes = [ctypes.c_void_p]
        SetProcessDpiAwarenessContext.restype = ctypes.c_bool
        # DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = -4
        result = SetProcessDpiAwarenessContext(ctypes.c_void_p(-4))
        if result:
            _debug_log("DPI: SetProcessDpiAwarenessContext (Per-Monitor V2) succeeded")
            return
        else:
            error = ctypes.get_last_error()
            _debug_log(f"DPI: SetProcessDpiAwarenessContext failed, error={error}")
    except Exception as e:
        _debug_log(f"DPI: SetProcessDpiAwarenessContext exception: {e}")

    # Try Windows 8.1+ API
    try:
        SetProcessDpiAwareness = ctypes.windll.shcore.SetProcessDpiAwareness
        SetProcessDpiAwareness.argtypes = [ctypes.c_int]
        SetProcessDpiAwareness.restype = ctypes.c_long
        # PROCESS_PER_MONITOR_DPI_AWARE = 2
        result = SetProcessDpiAwareness(2)
        if result == 0:  # S_OK
            _debug_log("DPI: SetProcessDpiAwareness (Per-Monitor) succeeded")
            return
        _debug_log(f"DPI: SetProcessDpiAwareness returned HRESULT={result}")
    except Exception as e:
        _debug_log(f"DPI: SetProcessDpiAwareness exception: {e}")

    # Fallback to Vista+ API
    try:
        SetProcessDPIAware = ctypes.windll.user32.SetProcessDPIAware
        SetProcessDPIAware.restype = ctypes.c_bool
        result = SetProcessDPIAware()
        _debug_log(f"DPI: SetProcessDPIAware (System) returned {result}")
    except Exception as e:
        _debug_log(f"DPI: SetProcessDPIAware exception: {e}")


def show_form(
    schema: Optional[Dict[str, Any]] = None,
    title: Optional[str] = None,
    width: int = 1000,
    height: int = 800,
    frameless: bool = False,
    corner_radius: int = 0,
    debug: bool = False,
) -> Dict[str, Any]:
    """
    Blocking call to show the form.

    Args:
        schema (Dict[str, Any]): The JSON schema for the form.
        title (str): Window title. If not provided, will try to extract from schema.title,
                     otherwise defaults to "XBot Form".
        width (int): Window width.
        height (int): Window height.
        frameless (bool): If True, creates a window without title bar and border.
        corner_radius (int): Radius for rounded corners (only applies when frameless=True).
        debug (bool): If True, enables right-click context menu and DevTools (default False).

    Returns:
        Dict[str, Any]: The payload from the form if confirmed.

    Raises:
        This function DOES NOT raise exceptions. Any error terminates the process.
    """
    try:
        # 0. Enable high DPI support (must be called before window creation)
        _enable_high_dpi_support()

        # 0.1. Determine window title: explicit title > schema.title > default
        window_title: str
        if title is not None:
            window_title = title
        elif schema and isinstance(schema.get("title"), str):
            window_title = schema["title"]
        else:
            window_title = "XBot Form"

        # 1. Initialize COM
        hr = ctypes.windll.ole32.CoInitializeEx(None, _COINIT_APARTMENTTHREADED)
        # RPC_E_CHANGED_MODE is okay (already initialized)
        if hr < 0 and hr != _RPC_E_CHANGED_MODE:
            _debug_log(f"CoInitializeEx failed: {hr}")
            hard_exit()

        # 2. create_window()
        _debug_log(
            f"Creating window: title={window_title}, size={width}x{height}, frameless={frameless}, corner_radius={corner_radius}"
        )
        hwnd = create_window(
            window_title,
            width,
            height,
            frameless=frameless,
            corner_radius=corner_radius,
        )
        if not hwnd:
            _debug_log("create_window returned 0 (invalid HWND)")
            hard_exit()

        # 3. create_webview()
        webview = WebView(schema=schema, debug=debug)
        webview.create(hwnd)
        _debug_log(f"WebView create() returned, debug={debug}")

        # 4. Set resize callback to handle window resize
        set_resize_callback(webview.resize)

        # 5. Enter run_message_loop() (Blocking)
        _debug_log("Entering message loop")
        run_message_loop()
        _debug_log("Message loop exited")

        # 6. Cleanup resize callback
        set_resize_callback(None)

        # 7. Check result
        # Only return if result is not None (meaning explicit confirm with payload)
        if webview.result is not None:
            # Cleanup COM before returning
            ctypes.windll.ole32.CoUninitialize()
            return webview.result
        else:
            # If loop finished but no result (e.g. unexpected WM_QUIT),
            # strictly enforce lifecycle rules.
            hard_exit()

    except KeyboardInterrupt:
        _debug_log("KeyboardInterrupt received; exiting immediately")
        # Ctrl+C should always close the window and end the process.
        hard_exit()
    except Exception as exc:
        _debug_log(f"Unhandled exception: {exc!r}")
        # 8. Any exception -> hard_exit()
        hard_exit()

    # Should not be reached
    hard_exit()
    return {}
