"""FormBuilder - Fluent API for building form payloads with typed outputs.

The FormBuilder provides runtime type inference for form responses.
As you add fields, the output type is automatically tracked.

Example:
    >>> from hax.form_builder import FormBuilder
    >>>
    >>> form = (FormBuilder()
    ...     .title("Event Registration")
    ...     .input("email", variant="email", required=True)
    ...     .number("age", min=0, max=120)
    ...     .checkbox("newsletter", checkbox_label="Subscribe"))
    >>>
    >>> # Use with client.create_form_request(form) for typed responses
"""

from __future__ import annotations

from dataclasses import make_dataclass
from typing import Any


def _snake_to_camel(name: str) -> str:
    """Convert snake_case to camelCase."""
    components = name.split("_")
    return components[0] + "".join(x.title() for x in components[1:])


def _convert_options(options: dict[str, Any]) -> dict[str, Any]:
    """Convert snake_case option keys to camelCase for the API."""
    return {_snake_to_camel(k): v for k, v in options.items()}


class FormBuilder:
    """Fluent builder for form payloads with runtime type inference.

    The FormBuilder tracks field types as you add them, providing typed
    access to response values via dynamically generated dataclasses.

    Field Type Mappings:
        Each field method adds a field to the form and registers its output type.
        The response values will have attributes matching the field IDs:

        .input(id)           -> str       Text input (variants: text, email, url, tel)
        .textarea(id)        -> str       Multi-line text input
        .select(id)          -> str       Dropdown (requires options=[{value, label}])
        .radio_group(id)     -> str       Radio group (requires options=[{value, label}])
        .date(id)            -> str       Date picker (ISO format: "2024-01-15")
        .number(id)          -> float     Numeric input (supports min, max, step)
        .slider(id)          -> float     Slider control (requires min, max)
        .checkbox(id)        -> bool      Single checkbox (requires checkbox_label)
        .switch(id)          -> bool      Toggle switch (requires switch_label)
        .checkbox_group(id)  -> list[str] Multi-select (requires options)
        .hidden(id, value)   -> type(value)  Hidden field, preserves value type

    Common Options (snake_case, converted to camelCase for API):
        label: str           - Field label text
        description: str     - Helper text below the field
        required: bool       - Whether the field is required
        disabled: bool       - Whether the field is disabled
        default_value: Any   - Default value for the field
        placeholder: str     - Placeholder text (input, textarea, select, number)

    Example:
        >>> form = (FormBuilder()
        ...     .title("Registration")
        ...     .input("email", variant="email", required=True)
        ...     .number("age", min=0, max=120)
        ...     .checkbox("newsletter", checkbox_label="Subscribe"))
        >>>
        >>> handle = client.create_form_request(form)
        >>> response = handle.wait_for_response()
        >>>
        >>> # Response values are typed based on fields added:
        >>> response.values.email      # str
        >>> response.values.age        # float
        >>> response.values.newsletter # bool
    """

    def __init__(self) -> None:
        """Initialize an empty FormBuilder."""
        self._fields: list[dict[str, Any]] = []
        self._config: dict[str, Any] = {}
        self._type_map: dict[str, type] = {}
        self._output_class: type | None = None

    # ─────────────────────────────────────────────────────────────────────────────
    # Form Configuration
    # ─────────────────────────────────────────────────────────────────────────────

    def title(self, title: str) -> FormBuilder:
        """Set the form title displayed at the top."""
        self._config["title"] = title
        return self

    def description(self, description: str) -> FormBuilder:
        """Set the form description displayed below the title."""
        self._config["description"] = description
        return self

    def submit_label(self, label: str) -> FormBuilder:
        """Set the submit button label."""
        self._config["submitLabel"] = label
        return self

    def layout(self, layout: dict[str, Any]) -> FormBuilder:
        """Set the form layout."""
        self._config["layout"] = layout
        return self

    # ─────────────────────────────────────────────────────────────────────────────
    # Field Methods
    # ─────────────────────────────────────────────────────────────────────────────

    def input(self, id: str, **options: Any) -> FormBuilder:
        """Add a text input field. Output type: str"""
        self._fields.append({"type": "input", "id": id, **_convert_options(options)})
        self._type_map[id] = str
        self._output_class = None  # Invalidate cached class
        return self

    def number(self, id: str, **options: Any) -> FormBuilder:
        """Add a number input field. Output type: float"""
        self._fields.append({"type": "number", "id": id, **_convert_options(options)})
        self._type_map[id] = float
        self._output_class = None
        return self

    def textarea(self, id: str, **options: Any) -> FormBuilder:
        """Add a textarea field. Output type: str"""
        self._fields.append({"type": "textarea", "id": id, **_convert_options(options)})
        self._type_map[id] = str
        self._output_class = None
        return self

    def select(self, id: str, **options: Any) -> FormBuilder:
        """Add a select dropdown field. Output type: str"""
        self._fields.append({"type": "select", "id": id, **_convert_options(options)})
        self._type_map[id] = str
        self._output_class = None
        return self

    def radio_group(self, id: str, **options: Any) -> FormBuilder:
        """Add a radio group field. Output type: str"""
        self._fields.append({"type": "radio-group", "id": id, **_convert_options(options)})
        self._type_map[id] = str
        self._output_class = None
        return self

    def checkbox(self, id: str, **options: Any) -> FormBuilder:
        """Add a single checkbox field. Output type: bool"""
        self._fields.append({"type": "checkbox", "id": id, **_convert_options(options)})
        self._type_map[id] = bool
        self._output_class = None
        return self

    def checkbox_group(self, id: str, **options: Any) -> FormBuilder:
        """Add a checkbox group field for multiple selections. Output type: list[str]"""
        self._fields.append({"type": "checkbox-group", "id": id, **_convert_options(options)})
        self._type_map[id] = list  # list[str] at runtime
        self._output_class = None
        return self

    def switch(self, id: str, **options: Any) -> FormBuilder:
        """Add a toggle switch field. Output type: bool"""
        self._fields.append({"type": "switch", "id": id, **_convert_options(options)})
        self._type_map[id] = bool
        self._output_class = None
        return self

    def slider(self, id: str, **options: Any) -> FormBuilder:
        """Add a slider field. Output type: float"""
        self._fields.append({"type": "slider", "id": id, **_convert_options(options)})
        self._type_map[id] = float
        self._output_class = None
        return self

    def date(self, id: str, **options: Any) -> FormBuilder:
        """Add a date picker field. Output type: str (ISO date format)"""
        self._fields.append({"type": "date", "id": id, **_convert_options(options)})
        self._type_map[id] = str
        self._output_class = None
        return self

    def hidden(self, id: str, value: str | int | bool) -> FormBuilder:
        """Add a hidden field with a preset value. Output type: type of value"""
        self._fields.append({"type": "hidden", "id": id, "value": value})
        self._type_map[id] = type(value)
        self._output_class = None
        return self

    # ─────────────────────────────────────────────────────────────────────────────
    # Output Methods
    # ─────────────────────────────────────────────────────────────────────────────

    def to_payload(self) -> dict[str, Any]:
        """Convert the builder to a FormBuilderPayload dict for the API."""
        return {**self._config, "fields": self._fields}

    def output_type(self) -> type:
        """Get or create the output dataclass type.

        Returns a dynamically generated dataclass with fields matching
        the form fields and their expected types.
        """
        if self._output_class is None:
            fields = [(name, typ) for name, typ in self._type_map.items()]
            self._output_class = make_dataclass("FormOutput", fields)
        return self._output_class

    def parse_response(self, response: dict[str, Any] | None) -> Any:
        """Parse a raw API response into the typed dataclass.

        Args:
            response: The response dict from the API.

        Returns:
            An instance of the dynamically generated output dataclass.
        """
        output_class = self.output_type()
        values = (response or {}).get("values", {})
        return output_class(**{k: values.get(k) for k in self._type_map})
