"""Unit tests for the FormBuilder class.

Tests runtime type inference and payload generation.
"""

from dataclasses import fields as dataclass_fields

from hax.form_builder import FormBuilder


class TestFormBuilderCreate:
    """Tests for FormBuilder creation."""

    def test_create_empty_form_builder(self):
        """Should create an empty form builder."""
        form = FormBuilder()
        assert form is not None

    def test_empty_payload(self):
        """Should have empty payload initially."""
        form = FormBuilder()
        payload = form.to_payload()
        assert payload == {"fields": []}


class TestFormConfiguration:
    """Tests for form configuration methods."""

    def test_set_title(self):
        """Should set title."""
        form = FormBuilder().title("Test Form")
        payload = form.to_payload()
        assert payload["title"] == "Test Form"

    def test_set_description(self):
        """Should set description."""
        form = FormBuilder().description("A test form")
        payload = form.to_payload()
        assert payload["description"] == "A test form"

    def test_set_submit_label(self):
        """Should set submitLabel."""
        form = FormBuilder().submit_label("Submit Now")
        payload = form.to_payload()
        assert payload["submitLabel"] == "Submit Now"

    def test_set_layout(self):
        """Should set layout."""
        form = FormBuilder().layout({"type": "grid", "columns": 2})
        payload = form.to_payload()
        assert payload["layout"] == {"type": "grid", "columns": 2}

    def test_chain_all_config_methods(self):
        """Should chain all config methods."""
        form = (
            FormBuilder()
            .title("My Form")
            .description("Description")
            .submit_label("Go")
            .layout({"type": "grid", "columns": 3})
        )

        payload = form.to_payload()
        assert payload["title"] == "My Form"
        assert payload["description"] == "Description"
        assert payload["submitLabel"] == "Go"
        assert payload["layout"] == {"type": "grid", "columns": 3}


class TestFieldMethods:
    """Tests for field methods."""

    def test_input_field(self):
        """Should add an input field."""
        form = FormBuilder().input("email")
        payload = form.to_payload()
        assert len(payload["fields"]) == 1
        assert payload["fields"][0] == {"type": "input", "id": "email"}

    def test_input_field_with_options(self):
        """Should add input field with options."""
        form = FormBuilder().input(
            "email",
            label="Email Address",
            variant="email",
            required=True,
            placeholder="you@example.com",
        )
        payload = form.to_payload()
        assert payload["fields"][0] == {
            "type": "input",
            "id": "email",
            "label": "Email Address",
            "variant": "email",
            "required": True,
            "placeholder": "you@example.com",
        }

    def test_number_field(self):
        """Should add a number field."""
        form = FormBuilder().number("age")
        payload = form.to_payload()
        assert payload["fields"][0] == {"type": "number", "id": "age"}

    def test_number_field_with_options(self):
        """Should add number field with min/max."""
        form = FormBuilder().number("age", label="Your Age", min=0, max=120, step=1)
        payload = form.to_payload()
        field = payload["fields"][0]
        assert field["type"] == "number"
        assert field["id"] == "age"
        assert field["min"] == 0
        assert field["max"] == 120
        assert field["step"] == 1

    def test_textarea_field(self):
        """Should add a textarea field."""
        form = FormBuilder().textarea("bio")
        payload = form.to_payload()
        assert payload["fields"][0] == {"type": "textarea", "id": "bio"}

    def test_textarea_with_options(self):
        """Should add textarea with options."""
        form = FormBuilder().textarea("bio", rows=5, max_length=500)
        payload = form.to_payload()
        field = payload["fields"][0]
        assert field["type"] == "textarea"
        assert field["rows"] == 5
        assert field["maxLength"] == 500

    def test_select_field(self):
        """Should add a select field with options."""
        form = FormBuilder().select(
            "country",
            options=[
                {"value": "us", "label": "United States"},
                {"value": "ca", "label": "Canada"},
            ],
        )
        payload = form.to_payload()
        field = payload["fields"][0]
        assert field["type"] == "select"
        assert field["id"] == "country"
        assert field["options"] == [
            {"value": "us", "label": "United States"},
            {"value": "ca", "label": "Canada"},
        ]

    def test_radio_group_field(self):
        """Should add a radio group field."""
        form = FormBuilder().radio_group(
            "plan",
            options=[
                {"value": "free", "label": "Free"},
                {"value": "pro", "label": "Pro"},
            ],
        )
        payload = form.to_payload()
        field = payload["fields"][0]
        assert field["type"] == "radio-group"
        assert field["id"] == "plan"

    def test_radio_group_with_orientation(self):
        """Should support orientation option."""
        form = FormBuilder().radio_group(
            "size", options=[{"value": "s", "label": "Small"}], orientation="horizontal"
        )
        payload = form.to_payload()
        assert payload["fields"][0]["orientation"] == "horizontal"

    def test_checkbox_field(self):
        """Should add a checkbox field."""
        form = FormBuilder().checkbox("newsletter", checkbox_label="Subscribe")
        payload = form.to_payload()
        field = payload["fields"][0]
        assert field["type"] == "checkbox"
        assert field["id"] == "newsletter"
        assert field["checkboxLabel"] == "Subscribe"

    def test_checkbox_group_field(self):
        """Should add a checkbox group field."""
        form = FormBuilder().checkbox_group(
            "interests",
            options=[
                {"value": "tech", "label": "Technology"},
                {"value": "sports", "label": "Sports"},
            ],
        )
        payload = form.to_payload()
        field = payload["fields"][0]
        assert field["type"] == "checkbox-group"
        assert field["id"] == "interests"

    def test_switch_field(self):
        """Should add a switch field."""
        form = FormBuilder().switch("darkMode", switch_label="Enable dark mode")
        payload = form.to_payload()
        field = payload["fields"][0]
        assert field["type"] == "switch"
        assert field["id"] == "darkMode"
        assert field["switchLabel"] == "Enable dark mode"

    def test_slider_field(self):
        """Should add a slider field."""
        form = FormBuilder().slider("volume", min=0, max=100, step=5)
        payload = form.to_payload()
        field = payload["fields"][0]
        assert field["type"] == "slider"
        assert field["id"] == "volume"
        assert field["min"] == 0
        assert field["max"] == 100
        assert field["step"] == 5

    def test_date_field(self):
        """Should add a date field."""
        form = FormBuilder().date("birthdate")
        payload = form.to_payload()
        assert payload["fields"][0] == {"type": "date", "id": "birthdate"}

    def test_date_field_with_options(self):
        """Should add date field with min/max."""
        form = FormBuilder().date("eventDate", min="2024-01-01", max="2024-12-31")
        payload = form.to_payload()
        field = payload["fields"][0]
        assert field["min"] == "2024-01-01"
        assert field["max"] == "2024-12-31"

    def test_hidden_string_field(self):
        """Should add a hidden string field."""
        form = FormBuilder().hidden("userId", "abc123")
        payload = form.to_payload()
        assert payload["fields"][0] == {
            "type": "hidden",
            "id": "userId",
            "value": "abc123",
        }

    def test_hidden_number_field(self):
        """Should add a hidden number field."""
        form = FormBuilder().hidden("version", 42)
        payload = form.to_payload()
        assert payload["fields"][0] == {
            "type": "hidden",
            "id": "version",
            "value": 42,
        }

    def test_hidden_boolean_field(self):
        """Should add a hidden boolean field."""
        form = FormBuilder().hidden("isAdmin", True)
        payload = form.to_payload()
        assert payload["fields"][0] == {
            "type": "hidden",
            "id": "isAdmin",
            "value": True,
        }


class TestChaining:
    """Tests for method chaining."""

    def test_chain_multiple_field_types(self):
        """Should chain multiple field types."""
        form = (
            FormBuilder()
            .title("Registration Form")
            .input("name", label="Name")
            .input("email", variant="email")
            .number("age")
            .checkbox("terms", checkbox_label="I agree")
        )

        payload = form.to_payload()
        assert len(payload["fields"]) == 4
        assert [f["id"] for f in payload["fields"]] == ["name", "email", "age", "terms"]


class TestTypeInference:
    """Tests for runtime type inference via dynamic dataclasses."""

    def test_input_type_is_str(self):
        """Should infer str type for input fields."""
        form = FormBuilder().input("name")
        output_type = form.output_type()
        field_types = {f.name: f.type for f in dataclass_fields(output_type)}
        assert field_types["name"] is str

    def test_number_type_is_float(self):
        """Should infer float type for number fields."""
        form = FormBuilder().number("count")
        output_type = form.output_type()
        field_types = {f.name: f.type for f in dataclass_fields(output_type)}
        assert field_types["count"] is float

    def test_checkbox_type_is_bool(self):
        """Should infer bool type for checkbox fields."""
        form = FormBuilder().checkbox("agree", checkbox_label="OK")
        output_type = form.output_type()
        field_types = {f.name: f.type for f in dataclass_fields(output_type)}
        assert field_types["agree"] is bool

    def test_switch_type_is_bool(self):
        """Should infer bool type for switch fields."""
        form = FormBuilder().switch("enabled", switch_label="Enable")
        output_type = form.output_type()
        field_types = {f.name: f.type for f in dataclass_fields(output_type)}
        assert field_types["enabled"] is bool

    def test_checkbox_group_type_is_list(self):
        """Should infer list type for checkbox group fields."""
        form = FormBuilder().checkbox_group(
            "tags", options=[{"value": "a", "label": "A"}]
        )
        output_type = form.output_type()
        field_types = {f.name: f.type for f in dataclass_fields(output_type)}
        assert field_types["tags"] is list

    def test_combined_types_for_multiple_fields(self):
        """Should infer combined types for multiple fields."""
        form = (
            FormBuilder()
            .input("email")
            .number("age")
            .checkbox("newsletter", checkbox_label="Subscribe")
            .select("country", options=[])
        )

        output_type = form.output_type()
        field_types = {f.name: f.type for f in dataclass_fields(output_type)}

        assert field_types["email"] is str
        assert field_types["age"] is float
        assert field_types["newsletter"] is bool
        assert field_types["country"] is str

    def test_hidden_field_preserves_type(self):
        """Should preserve type for hidden fields."""
        form = (
            FormBuilder()
            .hidden("version", 42)
            .hidden("source", "web")
            .hidden("active", True)
        )

        output_type = form.output_type()
        field_types = {f.name: f.type for f in dataclass_fields(output_type)}

        assert field_types["version"] is int
        assert field_types["source"] is str
        assert field_types["active"] is bool

    def test_output_type_is_cached(self):
        """Should cache the output type class."""
        form = FormBuilder().input("name")
        type1 = form.output_type()
        type2 = form.output_type()
        assert type1 is type2

    def test_output_type_invalidated_on_new_field(self):
        """Should invalidate cached type when new field added."""
        form = FormBuilder().input("name")
        type1 = form.output_type()
        form.number("age")
        type2 = form.output_type()
        assert type1 is not type2


class TestParseResponse:
    """Tests for parseResponse method."""

    def test_parse_response_values(self):
        """Should parse response values."""
        form = FormBuilder().input("email").number("age")

        response = {
            "values": {
                "email": "test@example.com",
                "age": 25,
            },
            "meta": {"submittedAt": "2024-01-01T00:00:00Z"},
        }

        parsed = form.parse_response(response)
        assert parsed.email == "test@example.com"
        assert parsed.age == 25

    def test_parse_null_response(self):
        """Should handle None response."""
        form = FormBuilder().input("name")
        parsed = form.parse_response(None)
        assert parsed.name is None

    def test_parse_empty_response(self):
        """Should handle empty response."""
        form = FormBuilder().input("name")
        parsed = form.parse_response({})
        assert parsed.name is None

    def test_extract_only_values_from_response(self):
        """Should extract only values from response."""
        form = FormBuilder().input("name")
        parsed = form.parse_response(
            {
                "values": {"name": "John"},
                "meta": {"submittedAt": "2024-01-01"},
                "extraField": "ignored",
            }
        )
        assert parsed.name == "John"

    def test_parsed_response_is_dataclass_instance(self):
        """Should return a dataclass instance."""
        form = FormBuilder().input("name").number("age")

        parsed = form.parse_response({"values": {"name": "Test", "age": 30}})

        # Verify it's a dataclass with expected attributes
        assert hasattr(parsed, "name")
        assert hasattr(parsed, "age")
        assert parsed.name == "Test"
        assert parsed.age == 30


class TestComplexFormExample:
    """Tests for complex form examples."""

    def test_complete_registration_form(self):
        """Should build a complete registration form."""
        form = (
            FormBuilder()
            .title("Event Registration")
            .description("Please fill out the form below to register for the event.")
            .submit_label("Register Now")
            .layout({"type": "grid", "columns": 2})
            .input("firstName", label="First Name", required=True)
            .input("lastName", label="Last Name", required=True)
            .input("email", label="Email", variant="email", required=True)
            .number("age", label="Age", min=18, max=100)
            .select(
                "ticketType",
                label="Ticket Type",
                options=[
                    {"value": "general", "label": "General Admission"},
                    {"value": "vip", "label": "VIP"},
                ],
            )
            .radio_group(
                "mealPreference",
                label="Meal Preference",
                options=[
                    {"value": "meat", "label": "Meat"},
                    {"value": "vegetarian", "label": "Vegetarian"},
                    {"value": "vegan", "label": "Vegan"},
                ],
            )
            .checkbox("newsletter", checkbox_label="Subscribe to updates")
            .hidden("eventId", "evt_12345")
        )

        payload = form.to_payload()

        assert payload["title"] == "Event Registration"
        assert len(payload["fields"]) == 8

        # Verify type inference works for complex form
        output_type = form.output_type()
        field_types = {f.name: f.type for f in dataclass_fields(output_type)}

        assert field_types["firstName"] is str
        assert field_types["lastName"] is str
        assert field_types["email"] is str
        assert field_types["age"] is float
        assert field_types["ticketType"] is str
        assert field_types["mealPreference"] is str
        assert field_types["newsletter"] is bool
        assert field_types["eventId"] is str

    def test_parse_complex_form_response(self):
        """Should parse complex form response correctly."""
        form = (
            FormBuilder()
            .input("firstName")
            .input("lastName")
            .input("email")
            .number("age")
            .select("ticketType", options=[])
            .checkbox("newsletter", checkbox_label="Subscribe")
        )

        response = {
            "values": {
                "firstName": "John",
                "lastName": "Doe",
                "email": "john@example.com",
                "age": 30,
                "ticketType": "vip",
                "newsletter": True,
            }
        }

        parsed = form.parse_response(response)

        assert parsed.firstName == "John"
        assert parsed.lastName == "Doe"
        assert parsed.email == "john@example.com"
        assert parsed.age == 30
        assert parsed.ticketType == "vip"
        assert parsed.newsletter is True


class TestFieldOptions:
    """Tests for advanced field options."""

    def test_conditional_rendering(self):
        """Should support conditional rendering."""
        form = (
            FormBuilder()
            .checkbox("hasPhone", checkbox_label="I have a phone")
            .input("phone", label="Phone Number", conditional={"when": "hasPhone", "is": True})
        )

        payload = form.to_payload()
        assert payload["fields"][1]["conditional"] == {"when": "hasPhone", "is": True}

    def test_validation_rules(self):
        """Should support validation rules."""
        form = FormBuilder().input(
            "email",
            validation=[
                {"type": "required", "message": "Email is required"},
                {"type": "email", "message": "Invalid email format"},
            ],
        )

        payload = form.to_payload()
        assert payload["fields"][0]["validation"] == [
            {"type": "required", "message": "Email is required"},
            {"type": "email", "message": "Invalid email format"},
        ]

    def test_disabled_fields(self):
        """Should support disabled fields."""
        form = FormBuilder().input("readOnly", disabled=True, default_value="Cannot change")

        payload = form.to_payload()
        assert payload["fields"][0]["disabled"] is True
        assert payload["fields"][0]["defaultValue"] == "Cannot change"
