"""
Configuration classes for xbot-form SDK.

This module provides type-safe configuration classes for customizing
form display and behavior, following the Builder pattern for complex setups.
"""

from __future__ import annotations

import sys
from dataclasses import dataclass, field
from typing import Any, Dict, Optional, Union

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

# Use typing_extensions for Python < 3.11
if sys.version_info >= (3, 11):
    from typing import Self
else:
    from typing import TypeVar

    Self = TypeVar("Self", bound="FormBuilder")


@dataclass
class FormSchema:
    """
    JSON Schema configuration for the form.

    This class wraps the JSON Schema and optional UI schema for form rendering.

    Attributes:
        schema: The JSON Schema defining form structure and validation.
        ui_schema: Optional UI schema for customizing widget rendering.
        form_data: Optional initial form data.

    Example:
        >>> schema = FormSchema(
        ...     schema={"type": "object", "properties": {"name": {"type": "string"}}},
        ...     ui_schema={"name": {"ui:placeholder": "Enter your name"}},
        ... )
    """

    schema: Dict[str, Any]
    ui_schema: Optional[Dict[str, Any]] = None
    form_data: Optional[Dict[str, Any]] = None

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary format expected by the frontend."""
        result: Dict[str, Any] = {"schema": self.schema}
        if self.ui_schema:
            result["uiSchema"] = self.ui_schema
        if self.form_data:
            result["formData"] = self.form_data
        return result


@dataclass
class WindowOptions:
    """
    Window appearance and behavior options.

    Attributes:
        width: Window width in pixels (default 1000).
        height: Window height in pixels (default 800).
        title: Window title. Priority: title > schema["title"] > "应用配置".
        frameless: If True, creates a borderless window (default True).
        corner_radius: Corner radius for frameless windows (default 32).

    Example:
        >>> options = WindowOptions(width=800, height=600, frameless=True, corner_radius=16)
    """

    width: int = 1000
    height: int = 800
    title: Optional[str] = None
    frameless: bool = True
    corner_radius: int = 32


@dataclass
class DisplayOptions:
    """
    Display behavior and appearance options.

    Attributes:
        debug: If True, enables DevTools and context menu (default False).
        theme: Color theme, either "light" or "dark" (default "light").
        locale: Locale for UI text, e.g., "zh" or "en" (default "zh").

    Example:
        >>> options = DisplayOptions(theme="dark", debug=True)
    """

    debug: bool = False
    theme: Literal["light", "dark"] = "light"
    locale: str = "zh"


@dataclass
class FormConfig:
    """
    Complete form configuration combining schema, window, and display options.

    This is the recommended way to configure complex forms with full type safety.

    Attributes:
        schema: The form schema (JSON Schema dict or FormSchema object).
        window: Window appearance options.
        display: Display behavior options.

    Example:
        >>> config = FormConfig(
        ...     schema={"type": "object", "properties": {"name": {"type": "string"}}},
        ...     window=WindowOptions(width=800, height=600),
        ...     display=DisplayOptions(theme="dark"),
        ... )
    """

    schema: Union[Dict[str, Any], FormSchema]
    window: WindowOptions = field(default_factory=WindowOptions)
    display: DisplayOptions = field(default_factory=DisplayOptions)

    def get_schema_dict(self) -> Optional[Dict[str, Any]]:
        """Get the schema as a dictionary."""
        if isinstance(self.schema, FormSchema):
            return self.schema.to_dict()
        return self.schema


class FormBuilder:
    """
    Builder pattern for constructing form configurations fluently.

    This provides a chainable API for building complex form configurations
    with better readability.

    Example:
        >>> from xbot_form import FormBuilder
        >>> result = (
        ...     FormBuilder()
        ...     .schema({"type": "object", "properties": {"name": {"type": "string"}}})
        ...     .ui_schema({"name": {"ui:placeholder": "Enter name"}})
        ...     .title("User Registration")
        ...     .size(800, 600)
        ...     .frameless(corner_radius=16)
        ...     .theme("dark")
        ...     .show()
        ... )
    """

    def __init__(self):
        """Initialize a new FormBuilder with default settings."""
        self._schema: Optional[Dict[str, Any]] = None
        self._ui_schema: Optional[Dict[str, Any]] = None
        self._form_data: Optional[Dict[str, Any]] = None
        self._title: Optional[str] = None
        self._width: int = 1000
        self._height: int = 800
        self._frameless: bool = True
        self._corner_radius: int = 32
        self._debug: bool = False
        self._theme: Literal["light", "dark"] = "light"
        self._locale: str = "zh"

    def schema(self, schema: Dict[str, Any]) -> Self:
        """
        Set the JSON Schema for the form.

        Args:
            schema: JSON Schema defining form structure.

        Returns:
            Self for method chaining.
        """
        self._schema = schema
        return self

    def ui_schema(self, ui_schema: Dict[str, Any]) -> Self:
        """
        Set the UI schema for customizing widget rendering.

        Args:
            ui_schema: UI schema dictionary.

        Returns:
            Self for method chaining.
        """
        self._ui_schema = ui_schema
        return self

    def form_data(self, data: Dict[str, Any]) -> Self:
        """
        Set initial form data.

        Args:
            data: Initial form values.

        Returns:
            Self for method chaining.
        """
        self._form_data = data
        return self

    def title(self, title: str) -> Self:
        """
        Set the window title (overrides schema["title"]).

        Args:
            title: Window title text.

        Returns:
            Self for method chaining.

        Note:
            If not called, window title is auto-extracted from schema["title"].
        """
        self._title = title
        return self

    def size(self, width: int, height: int) -> Self:
        """
        Set the window size.

        Args:
            width: Window width in pixels.
            height: Window height in pixels.

        Returns:
            Self for method chaining.
        """
        self._width = width
        self._height = height
        return self

    def frameless(self, corner_radius: int = 0) -> Self:
        """
        Enable frameless window mode.

        Args:
            corner_radius: Corner radius for rounded corners (default 0).

        Returns:
            Self for method chaining.
        """
        self._frameless = True
        self._corner_radius = corner_radius
        return self

    def theme(self, theme: Literal["light", "dark"]) -> Self:
        """
        Set the color theme.

        Args:
            theme: Either "light" or "dark".

        Returns:
            Self for method chaining.
        """
        self._theme = theme
        return self

    def locale(self, locale: str) -> Self:
        """
        Set the UI locale.

        Args:
            locale: Locale string (e.g., "zh", "en").

        Returns:
            Self for method chaining.
        """
        self._locale = locale
        return self

    def debug(self, enabled: bool = True) -> Self:
        """
        Enable or disable debug mode.

        Args:
            enabled: True to enable DevTools and context menu.

        Returns:
            Self for method chaining.
        """
        self._debug = enabled
        return self

    def build(self) -> FormConfig:
        """
        Build the FormConfig from current settings.

        Returns:
            A FormConfig instance with all configured options.

        Raises:
            ValueError: If schema is not set.
        """
        if self._schema is None:
            raise ValueError("Schema is required. Call .schema() before .build()")

        # Build the schema dict:
        # - If ui_schema or form_data is provided, wrap in RJSF config format:
        #   { "schema": {...}, "uiSchema": {...}, "formData": {...} }
        # - Otherwise, pass the raw JSON Schema directly
        schema_dict: Dict[str, Any]
        if self._ui_schema or self._form_data:
            # RJSF configuration format (schema + uiSchema + formData)
            schema_dict = {"schema": self._schema}
            if self._ui_schema:
                schema_dict["uiSchema"] = self._ui_schema
            if self._form_data:
                schema_dict["formData"] = self._form_data
            if self._title:
                schema_dict["title"] = self._title
        else:
            # Raw JSON Schema format
            schema_dict = self._schema

        return FormConfig(
            schema=schema_dict,
            window=WindowOptions(
                width=self._width,
                height=self._height,
                title=self._title,
                frameless=self._frameless,
                corner_radius=self._corner_radius,
            ),
            display=DisplayOptions(
                debug=self._debug,
                theme=self._theme,
                locale=self._locale,
            ),
        )

    def show(self) -> Dict[str, Any]:
        """
        Build the configuration and display the form.

        This is a convenience method that combines build() and show_form().

        Returns:
            Dictionary containing the form data submitted by user.

        Raises:
            FormCancelledError: User cancelled or closed the form.
            WebViewError: WebView2 initialization or runtime error.
            ValueError: If schema is not set.
        """
        from .api import show_form

        config = self.build()
        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,
        )
