"""
Public API for xbot-form SDK.

This module provides the main entry point for displaying JSON Schema forms.
"""

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

if sys.version_info >= (3, 8):
    from typing import Literal
else:
    from typing_extensions import Literal

from ._internal.logging import debug_log
from .config import DisplayOptions, FormBuilder, FormConfig, FormSchema, WindowOptions
from .exceptions import (
    FormCancelledError,
    WebViewError,
    WindowError,
    XBotFormError,
    _InternalCancelSignal,
    _InternalExitSignal,
)
from .message_loop import run_message_loop
from .webview import WebView
from .window import NativeWindow, WindowConfig

_MODULE = "api"
_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:
    1. SetProcessDpiAwarenessContext (Windows 10 1703+) - Per-Monitor V2
    2. SetProcessDpiAwareness (Windows 8.1+) - Per-Monitor
    3. SetProcessDPIAware (Windows Vista+) - System DPI aware
    """
    # Try Windows 10 1703+ API (best quality)
    try:
        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",
                _MODULE,
            )
            return
        else:
            error = ctypes.get_last_error()
            debug_log(
                f"DPI: SetProcessDpiAwarenessContext failed, error={error}", _MODULE
            )
    except (AttributeError, OSError) as e:
        # AttributeError: API not available on this Windows version
        # OSError: DLL load or call failed
        debug_log(f"DPI: SetProcessDpiAwarenessContext exception: {e}", _MODULE)

    # 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", _MODULE)
            return
        debug_log(f"DPI: SetProcessDpiAwareness returned HRESULT={result}", _MODULE)
    except (AttributeError, OSError) as e:
        debug_log(f"DPI: SetProcessDpiAwareness exception: {e}", _MODULE)

    # 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}", _MODULE)
    except (AttributeError, OSError) as e:
        debug_log(f"DPI: SetProcessDPIAware exception: {e}", _MODULE)


def show_form(
    schema: Optional[Dict[str, Any]] = None,
    title: Optional[str] = None,
    width: int = 1000,
    height: int = 800,
    frameless: bool = True,
    corner_radius: int = 32,
    debug: bool = False,
    theme: Literal["light", "dark"] = "light",
) -> Dict[str, Any]:
    """
    Display a form based on JSON Schema and return user input.

    This is a blocking call that shows a native window with a WebView2-based
    form and waits for user interaction.

    Args:
        schema: The JSON Schema for the form. Can be:
            - A JSON Schema object (with type, properties, title, etc.)
            - A config object (with schema, uiSchema, formData keys)
        title: Window title. Priority: title param > schema["title"] > "应用配置".
        width: Window width in pixels (default 1000).
        height: Window height in pixels (default 800).
        frameless: If True, creates a borderless window (default True).
        corner_radius: Radius for rounded corners when frameless (default 32).
        debug: If True, enables DevTools and context menu (default False).
        theme: Color theme, either "light" or "dark" (default "light").

    Returns:
        Dictionary containing the form data submitted by user.
        Returns empty dict {} if user cancelled or closed the form.

    Raises:
        WebViewError: WebView2 initialization or runtime error.
        WindowError: Window creation or management error.
        XBotFormError: Other unexpected errors.

    Example:
        >>> from xbot_form import show_form
        >>> # Window title auto-extracted from schema["title"]
        >>> schema = {
        ...     "type": "object",
        ...     "title": "User Registration",
        ...     "properties": {"name": {"type": "string", "title": "Name"}},
        ... }
        >>> result = show_form(schema=schema)
        >>> # Or override with explicit title parameter
        >>> result = show_form(schema=schema, title="Custom Title")
    """
    window = None
    com_initialized = False

    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 = "应用配置"

        # 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}", _MODULE)
            raise XBotFormError(f"COM initialization failed with HRESULT: {hr}")
        com_initialized = True

        # 2. Create window
        debug_log(
            f"Creating window: title={window_title}, size={width}x{height}, "
            f"frameless={frameless}, corner_radius={corner_radius}",
            _MODULE,
        )

        window_config = WindowConfig(
            title=window_title,
            width=width,
            height=height,
            frameless=frameless,
            corner_radius=corner_radius,
        )
        window = NativeWindow(window_config)
        hwnd = window.create()

        if not hwnd:
            debug_log("Window creation returned invalid HWND", _MODULE)
            raise WindowError("Failed to create window")

        # 3. Create WebView
        webview = WebView(schema=schema, debug=debug, theme=theme)

        # Set up callbacks
        def on_ready():
            window.set_close_guard(False)
            window.show()

        def on_cancel():
            # Will be handled by exception
            pass

        def on_start_drag():
            window.start_drag()

        webview.set_callbacks(
            on_ready=on_ready,
            on_cancel=on_cancel,
            on_start_drag=on_start_drag,
        )

        webview.create(hwnd)
        debug_log(f"WebView created, debug={debug}, theme={theme}", _MODULE)

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

        # 5. Enter message loop (blocking)
        debug_log("Entering message loop", _MODULE)
        run_message_loop()
        debug_log("Message loop exited", _MODULE)

        # 6. Cleanup resize callback
        window.set_resize_callback(None)

        # 7. Check result
        if webview.result is not None:
            # Cleanup COM before returning
            if com_initialized:
                ctypes.windll.ole32.CoUninitialize()
            return webview.result
        else:
            # If loop finished but no result, return empty dict (cancelled)
            debug_log("Form closed without submission, returning empty dict", _MODULE)
            return {}

    except _InternalCancelSignal:
        debug_log("Internal cancel signal received, returning empty dict", _MODULE)
        return {}

    except _InternalExitSignal as e:
        debug_log(f"Internal exit signal: {e}", _MODULE)
        raise WebViewError(str(e))

    except (WebViewError, WindowError, XBotFormError):
        # Re-raise known SDK exceptions as-is
        raise

    except KeyboardInterrupt:
        debug_log("KeyboardInterrupt received, returning empty dict", _MODULE)
        return {}

    except Exception as exc:
        debug_log(f"Unhandled exception: {exc!r}", _MODULE)
        raise XBotFormError(f"Unexpected error: {exc}") from exc

    finally:
        # Cleanup
        if window:
            try:
                window.destroy()
            except Exception:
                pass
        if com_initialized:
            try:
                ctypes.windll.ole32.CoUninitialize()
            except Exception:
                pass


def show_form_with_config(config: FormConfig) -> Dict[str, Any]:
    """
    Display a form using a FormConfig object.

    This is an alternative API that accepts a FormConfig object instead of
    individual parameters, providing better type safety and IDE support.

    Args:
        config: A FormConfig object containing all form settings.

    Returns:
        Dictionary containing the form data submitted by user.
        Returns empty dict {} if user cancelled or closed the form.

    Raises:
        WebViewError: WebView2 initialization or runtime error.
        WindowError: Window creation or management error.
        XBotFormError: Other unexpected errors.

    Example:
        >>> from xbot_form import show_form_with_config, FormConfig, WindowOptions
        >>> config = FormConfig(
        ...     schema={"type": "object", "properties": {"name": {"type": "string"}}},
        ...     window=WindowOptions(width=800, height=600),
        ... )
        >>> result = show_form_with_config(config)
    """
    return show_form(
        schema=config.get_schema_dict(),
        title=config.window.title,
        width=config.window.width,
        height=config.window.height,
        frameless=config.window.frameless,
        corner_radius=config.window.corner_radius,
        debug=config.display.debug,
        theme=config.display.theme,
    )


# Convenience function for creating a FormBuilder
def form_builder() -> FormBuilder:
    """
    Create a new FormBuilder for fluent configuration.

    This is a convenience function for creating a FormBuilder instance.

    Returns:
        A new FormBuilder instance.

    Example:
        >>> from xbot_form import form_builder
        >>> result = (
        ...     form_builder()
        ...     .schema({"type": "object", "properties": {"name": {"type": "string"}}})
        ...     .title("User Info")
        ...     .theme("dark")
        ...     .show()
        ... )
    """
    return FormBuilder()
