"""
Test examples for v0.9 callable fields and layout system

These tests demonstrate:
1. BoundField.__call__() functionality
2. Custom layout classes
3. Duplicate field rendering prevention
4. Field rendering with kwargs
"""
import pytest
from textual.app import App
from textual.widgets import Button

from textual_wtf import Form, FormLayout, DefaultFormLayout, StringField, IntegerField
from textual_wtf.exceptions import FormError


# ============================================================================
# Test 1: BoundField is callable and returns widget
# ============================================================================

@pytest.mark.asyncio
class TestCallableFields:
    """Test that BoundField instances are callable"""
    
    async def test_field_call_returns_widget(self):
        """Calling a field should return a widget"""
        class TestForm(Form):
            name = StringField(label="Name")
        
        form = TestForm()
        
        # Field should be callable
        assert callable(form.name)
        
        # Calling should return a widget
        widget = form.name()
        assert widget is not None
        assert hasattr(widget, 'value')
    
    async def test_field_call_with_kwargs(self):
        """Field call should accept and apply kwargs"""
        class TestForm(Form):
            email = StringField(label="Email")
        
        form = TestForm()
        
        # Call with kwargs
        widget = form.email(placeholder="you@example.com", disabled=True)
        
        # Widget should have the kwargs applied
        assert widget.placeholder == "you@example.com"
        assert widget.disabled == True
    
    async def test_field_call_syncs_value(self):
        """Widget should have initial value synced"""
        class TestForm(Form):
            name = StringField(label="Name")
        
        form = TestForm(data={"name": "Alice"})
        widget = form.name()
        
        # Widget should have the initial value
        assert widget.value == "Alice"


# ============================================================================
# Test 2: Custom layouts work correctly
# ============================================================================

@pytest.mark.asyncio  
class TestCustomLayouts:
    """Test custom layout classes"""
    
    async def test_custom_layout_renders(self):
        """Custom layout should render correctly"""
        class TestForm(Form):
            name = StringField(label="Name")
            age = IntegerField(label="Age")
        
        class CustomLayout(FormLayout):
            def compose_form(self):
                yield self.form.name()
                yield self.form.age()
                yield Button("Submit", id="submit")
        
        form = TestForm(layout_class=CustomLayout)
        layout = form.render()
        
        assert isinstance(layout, CustomLayout)
        assert layout.form is form
    
    async def test_default_layout_used_when_none_specified(self):
        """Should use DefaultFormLayout when no layout specified"""
        class TestForm(Form):
            name = StringField(label="Name")
        
        form = TestForm()
        layout = form.render()
        
        assert isinstance(layout, DefaultFormLayout)
    
    async def test_layout_class_parameter_overrides_class_attribute(self):
        """Instance layout_class parameter should override class attribute"""
        class CustomLayout1(FormLayout):
            def compose_form(self):
                yield self.form.name()
        
        class CustomLayout2(FormLayout):
            def compose_form(self):
                yield self.form.name()
        
        class TestForm(Form):
            layout_class = CustomLayout1
            name = StringField(label="Name")
        
        # Without override - uses class attribute
        form1 = TestForm()
        layout1 = form1.render()
        assert isinstance(layout1, CustomLayout1)
        
        # With override - uses parameter
        form2 = TestForm(layout_class=CustomLayout2)
        layout2 = form2.render()
        assert isinstance(layout2, CustomLayout2)


# ============================================================================
# Test 3: Duplicate field rendering prevention
# ============================================================================

@pytest.mark.asyncio
class TestDuplicateFieldPrevention:
    """Test that rendering a field twice raises an error"""
    
    async def test_duplicate_field_rendering_raises_error(self):
        """Rendering same field twice should raise FormError"""
        class TestForm(Form):
            name = StringField(label="Name")
        
        class BadLayout(FormLayout):
            def compose_form(self):
                yield self.form.name()  # First render - OK
                yield self.form.name()  # Second render - should raise
        
        form = TestForm(layout_class=BadLayout)
        
        # Should raise FormError when trying to render
        with pytest.raises(FormError, match="already been rendered"):
            class TestApp(App):
                def compose(self):
                    yield form.render()
            
            app = TestApp()
            async with app.run_test():
                pass
    
    async def test_different_fields_can_be_rendered(self):
        """Different fields should be independently renderable"""
        class TestForm(Form):
            name = StringField(label="Name")
            email = StringField(label="Email")
        
        class GoodLayout(FormLayout):
            def compose_form(self):
                yield self.form.name()
                yield self.form.email()
                yield Button("Submit", id="submit")
        
        form = TestForm(layout_class=GoodLayout)
        
        # Should not raise
        class TestApp(App):
            def compose(self):
                yield form.render()
        
        app = TestApp()
        async with app.run_test():
            # Both fields should be present
            assert app.query("FormInput").count() == 2


# ============================================================================
# Test 4: Layout data operations
# ============================================================================

@pytest.mark.asyncio
class TestLayoutDataOperations:
    """Test that layout correctly delegates to form"""
    
    async def test_layout_get_data(self):
        """Layout.get_data() should return form data"""
        class TestForm(Form):
            name = StringField(label="Name")
        
        form = TestForm(data={"name": "Bob"})
        layout = form.render()
        
        data = layout.get_data()
        assert data["name"] == "Bob"
    
    async def test_layout_set_data(self):
        """Layout.set_data() should update form data"""
        class TestForm(Form):
            name = StringField(label="Name")
        
        form = TestForm()
        layout = form.render()
        
        layout.set_data({"name": "Charlie"})
        
        assert form.name.value == "Charlie"
    
    async def test_layout_validate(self):
        """Layout.validate() should validate form"""
        class TestForm(Form):
            name = StringField(label="Name", required=True)
        
        class TestApp(App):
            def compose(self):
                self.form = TestForm()
                yield self.form.render()
        
        app = TestApp()
        async with app.run_test():
            layout = app.query_one(DefaultFormLayout)
            
            # Empty required field should fail validation
            is_valid = await layout.validate()
            assert not is_valid


# ============================================================================
# Test 5: Message compatibility
# ============================================================================

@pytest.mark.asyncio
class TestMessageCompatibility:
    """Test Form.Submitted and Form.Cancelled messages"""
    
    async def test_submitted_message_has_layout(self):
        """Form.Submitted should contain layout reference"""
        class TestForm(Form):
            name = StringField(label="Name", required=True)
        
        class TestApp(App):
            def __init__(self):
                super().__init__()
                self.submitted_layout = None
            
            def compose(self):
                self.form = TestForm(data={"name": "Test"})
                yield self.form.render()
            
            def on_form_submitted(self, event: Form.Submitted):
                self.submitted_layout = event.layout
        
        app = TestApp()
        async with app.run_test() as pilot:
            # Submit the form
            await pilot.click("#submit")
            
            # Check message received
            assert app.submitted_layout is not None
            assert isinstance(app.submitted_layout, DefaultFormLayout)
    
    async def test_message_backward_compatibility(self):
        """event.form should work for backward compatibility"""
        class TestForm(Form):
            name = StringField(label="Name", required=True)
        
        class TestApp(App):
            def __init__(self):
                super().__init__()
                self.event_form = None
            
            def compose(self):
                self.form = TestForm(data={"name": "Test"})
                yield self.form.render()
            
            def on_form_submitted(self, event: Form.Submitted):
                # Old code using event.form should still work
                self.event_form = event.form
        
        app = TestApp()
        async with app.run_test() as pilot:
            await pilot.click("#submit")
            
            # event.form should be the layout (backward compat)
            assert app.event_form is not None
            assert isinstance(app.event_form, DefaultFormLayout)


# ============================================================================
# Test 6: Field kwargs merging
# ============================================================================

@pytest.mark.asyncio
class TestFieldKwargsMerging:
    """Test that field kwargs are properly merged"""
    
    async def test_runtime_kwargs_override_field_kwargs(self):
        """Runtime kwargs should override field-level kwargs"""
        class TestForm(Form):
            name = StringField(
                label="Name",
                placeholder="Default placeholder"
            )
        
        form = TestForm()
        
        # Call with different placeholder
        widget = form.name(placeholder="Override placeholder")
        
        assert widget.placeholder == "Override placeholder"
    
    async def test_field_kwargs_preserved_when_no_override(self):
        """Field-level kwargs should be used when no override"""
        class TestForm(Form):
            name = StringField(
                label="Name",
                placeholder="Default placeholder",
                max_length=50
            )
        
        form = TestForm()
        
        # Call without overriding
        widget = form.name()
        
        assert widget.placeholder == "Default placeholder"
        assert widget.max_length == 50


# Notes for test migration:
# 
# 1. All tests importing RenderedForm should import DefaultFormLayout instead
# 2. Tests querying for RenderedForm should query for DefaultFormLayout
# 3. Message handler tests need to check event.layout instead of event.form
#    (though event.form still works for backward compatibility)
# 4. Tests that construct RenderedForm directly need to use DefaultFormLayout
# 5. Any test checking isinstance(x, RenderedForm) needs updating
