"""
Unit tests for abstract functional elements.

Tests the Link, Button, Form, FormField, and related classes that handle
interactive functionality and user interface elements.
"""

import unittest
from unittest.mock import Mock
from pyWebLayout.abstract.functional import (
    Link, LinkType, Button, Form, FormField, FormFieldType
)


class TestLinkType(unittest.TestCase):
    """Test cases for LinkType enum."""

    def test_link_types(self):
        """Test that all expected link types exist."""
        expected_types = ['INTERNAL', 'EXTERNAL', 'API', 'FUNCTION']

        for type_name in expected_types:
            self.assertTrue(hasattr(LinkType, type_name))

        # Test specific values
        self.assertEqual(LinkType.INTERNAL.value, 1)
        self.assertEqual(LinkType.EXTERNAL.value, 2)
        self.assertEqual(LinkType.API.value, 3)
        self.assertEqual(LinkType.FUNCTION.value, 4)


class TestLink(unittest.TestCase):
    """Test cases for Link class."""

    def setUp(self):
        """Set up test fixtures."""
        self.mock_callback = Mock(return_value="callback_result")

    def test_link_creation_minimal(self):
        """Test link creation with minimal parameters."""
        link = Link("test-location")

        self.assertEqual(link.location, "test-location")
        self.assertEqual(link.link_type, LinkType.INTERNAL)  # Default
        self.assertEqual(link.params, {})
        self.assertIsNone(link.title)
        self.assertIsNone(link._callback)

    def test_link_creation_full(self):
        """Test link creation with all parameters."""
        params = {"param1": "value1", "param2": "value2"}
        link = Link(
            location="https://example.com",
            link_type=LinkType.EXTERNAL,
            callback=self.mock_callback,
            params=params,
            title="Example Link"
        )

        self.assertEqual(link.location, "https://example.com")
        self.assertEqual(link.link_type, LinkType.EXTERNAL)
        self.assertEqual(link.params, params)
        self.assertEqual(link.title, "Example Link")
        self.assertEqual(link._callback, self.mock_callback)

    def test_internal_link_execution(self):
        """Test executing internal links."""
        link = Link("#section1", LinkType.INTERNAL)
        result = link.execute()

        # Internal links should return the location
        self.assertEqual(result, "#section1")

    def test_external_link_execution(self):
        """Test executing external links."""
        link = Link("https://example.com", LinkType.EXTERNAL)
        result = link.execute()

        # External links should return the location
        self.assertEqual(result, "https://example.com")

    def test_api_link_execution(self):
        """Test executing API links with callback."""
        params = {"action": "save", "id": 123}
        link = Link(
            location="/api/save",
            link_type=LinkType.API,
            callback=self.mock_callback,
            params=params
        )

        result = link.execute()

        # Should call callback with location, point (None when not provided), and params
        self.mock_callback.assert_called_once_with(
            "/api/save", None, action="save", id=123)
        self.assertEqual(result, "callback_result")

    def test_function_link_execution(self):
        """Test executing function links with callback."""
        params = {"data": "test"}
        link = Link(
            location="save_document",
            link_type=LinkType.FUNCTION,
            callback=self.mock_callback,
            params=params
        )

        result = link.execute()

        # Should call callback with location, point (None when not provided), and params
        self.mock_callback.assert_called_once_with("save_document", None, data="test")
        self.assertEqual(result, "callback_result")

    def test_api_link_without_callback(self):
        """Test API link without callback returns location."""
        link = Link("/api/endpoint", LinkType.API)
        result = link.execute()

        # Without callback, should return location
        self.assertEqual(result, "/api/endpoint")

    def test_function_link_without_callback(self):
        """Test function link without callback returns location."""
        link = Link("function_name", LinkType.FUNCTION)
        result = link.execute()

        # Without callback, should return location
        self.assertEqual(result, "function_name")

    def test_link_properties(self):
        """Test link property access."""
        params = {"key": "value"}
        link = Link(
            location="test",
            link_type=LinkType.API,
            params=params,
            title="Test Title"
        )

        # Test all property getters
        self.assertEqual(link.location, "test")
        self.assertEqual(link.link_type, LinkType.API)
        self.assertEqual(link.params, params)
        self.assertEqual(link.title, "Test Title")


class TestButton(unittest.TestCase):
    """Test cases for Button class."""

    def setUp(self):
        """Set up test fixtures."""
        self.mock_callback = Mock(return_value="button_clicked")

    def test_button_creation_minimal(self):
        """Test button creation with minimal parameters."""
        button = Button("Click Me", self.mock_callback)

        self.assertEqual(button.label, "Click Me")
        self.assertEqual(button._callback, self.mock_callback)
        self.assertEqual(button.params, {})
        self.assertTrue(button.enabled)

    def test_button_creation_full(self):
        """Test button creation with all parameters."""
        params = {"action": "submit", "form_id": "test_form"}
        button = Button(
            label="Submit",
            callback=self.mock_callback,
            params=params,
            enabled=False
        )

        self.assertEqual(button.label, "Submit")
        self.assertEqual(button._callback, self.mock_callback)
        self.assertEqual(button.params, params)
        self.assertFalse(button.enabled)

    def test_button_label_property(self):
        """Test button label getter and setter."""
        button = Button("Original", self.mock_callback)

        # Test getter
        self.assertEqual(button.label, "Original")

        # Test setter
        button.label = "New Label"
        self.assertEqual(button.label, "New Label")

    def test_button_enabled_property(self):
        """Test button enabled getter and setter."""
        button = Button("Test", self.mock_callback, enabled=True)

        # Test initial state
        self.assertTrue(button.enabled)

        # Test setter
        button.enabled = False
        self.assertFalse(button.enabled)

        button.enabled = True
        self.assertTrue(button.enabled)

    def test_button_execute_enabled(self):
        """Test executing enabled button."""
        params = {"data": "test_data"}
        button = Button("Test", self.mock_callback, params=params, enabled=True)

        result = button.execute()

        # Should call callback with point (None when not provided) and params
        self.mock_callback.assert_called_once_with(None, data="test_data")
        self.assertEqual(result, "button_clicked")

    def test_button_execute_disabled(self):
        """Test executing disabled button."""
        button = Button("Test", self.mock_callback, enabled=False)

        result = button.execute()

        # Should not call callback and return None
        self.mock_callback.assert_not_called()
        self.assertIsNone(result)

    def test_button_execute_no_callback(self):
        """Test executing button without callback."""
        button = Button("Test", None, enabled=True)

        result = button.execute()

        # Should return None
        self.assertIsNone(result)


class TestFormFieldType(unittest.TestCase):
    """Test cases for FormFieldType enum."""

    def test_form_field_types(self):
        """Test that all expected form field types exist."""
        expected_types = [
            'TEXT', 'PASSWORD', 'CHECKBOX', 'RADIO', 'SELECT', 'TEXTAREA',
            'NUMBER', 'DATE', 'TIME', 'EMAIL', 'URL', 'COLOR', 'RANGE', 'HIDDEN'
        ]

        for type_name in expected_types:
            self.assertTrue(hasattr(FormFieldType, type_name))

        # Test some specific values
        self.assertEqual(FormFieldType.TEXT.value, 1)
        self.assertEqual(FormFieldType.PASSWORD.value, 2)
        self.assertEqual(FormFieldType.HIDDEN.value, 14)


class TestFormField(unittest.TestCase):
    """Test cases for FormField class."""

    def test_form_field_creation_minimal(self):
        """Test form field creation with minimal parameters."""
        field = FormField("username", FormFieldType.TEXT)

        self.assertEqual(field.name, "username")
        self.assertEqual(field.field_type, FormFieldType.TEXT)
        self.assertEqual(field.label, "username")  # Default to name
        self.assertIsNone(field.value)
        self.assertFalse(field.required)
        self.assertEqual(field.options, [])
        self.assertIsNone(field.form)

    def test_form_field_creation_full(self):
        """Test form field creation with all parameters."""
        options = [("value1", "Label 1"), ("value2", "Label 2")]
        field = FormField(
            name="country",
            field_type=FormFieldType.SELECT,
            label="Country",
            value="value1",
            required=True,
            options=options
        )

        self.assertEqual(field.name, "country")
        self.assertEqual(field.field_type, FormFieldType.SELECT)
        self.assertEqual(field.label, "Country")
        self.assertEqual(field.value, "value1")
        self.assertTrue(field.required)
        self.assertEqual(field.options, options)

    def test_form_field_value_property(self):
        """Test form field value getter and setter."""
        field = FormField("test", FormFieldType.TEXT, value="initial")

        # Test getter
        self.assertEqual(field.value, "initial")

        # Test setter
        field.value = "new_value"
        self.assertEqual(field.value, "new_value")

    def test_form_field_form_property(self):
        """Test form field form getter and setter."""
        field = FormField("test", FormFieldType.TEXT)
        mock_form = Mock()

        # Initial state
        self.assertIsNone(field.form)

        # Test setter
        field.form = mock_form
        self.assertEqual(field.form, mock_form)

    def test_form_field_properties(self):
        """Test all form field property getters."""
        options = [("opt1", "Option 1")]
        field = FormField(
            name="test_field",
            field_type=FormFieldType.CHECKBOX,
            label="Test Field",
            value=True,
            required=True,
            options=options
        )

        # Test all getters
        self.assertEqual(field.name, "test_field")
        self.assertEqual(field.field_type, FormFieldType.CHECKBOX)
        self.assertEqual(field.label, "Test Field")
        self.assertTrue(field.value)
        self.assertTrue(field.required)
        self.assertEqual(field.options, options)


class TestForm(unittest.TestCase):
    """Test cases for Form class."""

    def setUp(self):
        """Set up test fixtures."""
        self.mock_callback = Mock(return_value="form_submitted")

    def test_form_creation_minimal(self):
        """Test form creation with minimal parameters."""
        form = Form("test_form")

        self.assertEqual(form.form_id, "test_form")
        self.assertIsNone(form.action)
        self.assertIsNone(form._callback)
        self.assertEqual(len(form._fields), 0)

    def test_form_creation_full(self):
        """Test form creation with all parameters."""
        form = Form(
            form_id="contact_form",
            action="/submit",
            callback=self.mock_callback
        )

        self.assertEqual(form.form_id, "contact_form")
        self.assertEqual(form.action, "/submit")
        self.assertEqual(form._callback, self.mock_callback)

    def test_form_field_management(self):
        """Test adding and retrieving form fields."""
        form = Form("test_form")

        # Create fields
        field1 = FormField("username", FormFieldType.TEXT, value="john")
        field2 = FormField("password", FormFieldType.PASSWORD, value="secret")
        field3 = FormField("email", FormFieldType.EMAIL, value="john@example.com")

        # Add fields
        form.add_field(field1)
        form.add_field(field2)
        form.add_field(field3)

        # Test that fields are stored correctly
        self.assertEqual(len(form._fields), 3)

        # Test field retrieval
        self.assertEqual(form.get_field("username"), field1)
        self.assertEqual(form.get_field("password"), field2)
        self.assertEqual(form.get_field("email"), field3)
        self.assertIsNone(form.get_field("nonexistent"))

        # Test that fields have form reference
        self.assertEqual(field1.form, form)
        self.assertEqual(field2.form, form)
        self.assertEqual(field3.form, form)

    def test_form_get_values(self):
        """Test getting form values."""
        form = Form("test_form")

        # Add fields with values
        form.add_field(FormField("name", FormFieldType.TEXT, value="John Doe"))
        form.add_field(FormField("age", FormFieldType.NUMBER, value=30))
        form.add_field(FormField("subscribe", FormFieldType.CHECKBOX, value=True))

        # Get values
        values = form.get_values()

        expected = {
            "name": "John Doe",
            "age": 30,
            "subscribe": True
        }

        self.assertEqual(values, expected)

    def test_form_get_values_empty(self):
        """Test getting values from empty form."""
        form = Form("empty_form")
        values = form.get_values()

        self.assertEqual(values, {})

    def test_form_execute_with_callback(self):
        """Test executing form with callback."""
        form = Form("test_form", callback=self.mock_callback)

        # Add some fields
        form.add_field(FormField("field1", FormFieldType.TEXT, value="value1"))
        form.add_field(FormField("field2", FormFieldType.TEXT, value="value2"))

        result = form.execute()

        # Should call callback with form_id and values
        expected_values = {"field1": "value1", "field2": "value2"}
        self.mock_callback.assert_called_once_with("test_form", expected_values)
        self.assertEqual(result, "form_submitted")

    def test_form_execute_without_callback(self):
        """Test executing form without callback."""
        form = Form("test_form")

        # Add a field
        form.add_field(FormField("test", FormFieldType.TEXT, value="test_value"))

        result = form.execute()

        # Should return the form values
        expected = {"test": "test_value"}
        self.assertEqual(result, expected)

    def test_form_properties(self):
        """Test form property getters."""
        form = Form("test_form", action="/submit")

        self.assertEqual(form.form_id, "test_form")
        self.assertEqual(form.action, "/submit")


class TestFormIntegration(unittest.TestCase):
    """Integration tests for form functionality."""

    def test_complete_form_workflow(self):
        """Test a complete form creation and submission workflow."""
        # Create form
        form = Form("registration_form", action="/register")

        # Add various field types
        form.add_field(FormField(
            "username", FormFieldType.TEXT,
            label="Username", required=True, value="testuser"
        ))

        form.add_field(FormField(
            "password", FormFieldType.PASSWORD,
            label="Password", required=True, value="secret123"
        ))

        form.add_field(FormField(
            "email", FormFieldType.EMAIL,
            label="Email", required=True, value="test@example.com"
        ))

        form.add_field(
            FormField(
                "country", FormFieldType.SELECT, label="Country", value="US", options=[
                    ("US", "United States"), ("CA", "Canada"), ("UK", "United Kingdom")]))

        form.add_field(FormField(
            "newsletter", FormFieldType.CHECKBOX,
            label="Subscribe to newsletter", value=True
        ))

        # Test form state
        self.assertEqual(len(form._fields), 5)

        # Test individual field access
        username_field = form.get_field("username")
        self.assertEqual(username_field.value, "testuser")
        self.assertTrue(username_field.required)

        # Test getting all values
        values = form.get_values()
        expected = {
            "username": "testuser",
            "password": "secret123",
            "email": "test@example.com",
            "country": "US",
            "newsletter": True
        }
        self.assertEqual(values, expected)

        # Test form submission
        result = form.execute()
        self.assertEqual(result, expected)

    def test_form_field_modification(self):
        """Test modifying form fields after creation."""
        form = Form("test_form")

        # Add field
        field = FormField("test", FormFieldType.TEXT, value="initial")
        form.add_field(field)

        # Modify field value
        field.value = "modified"

        # Test that form reflects the change
        values = form.get_values()
        self.assertEqual(values["test"], "modified")

        # Test getting the modified field
        retrieved_field = form.get_field("test")
        self.assertEqual(retrieved_field.value, "modified")


if __name__ == '__main__':
    unittest.main()
