"""Tests for component management operations."""

import pytest
import tempfile
import yaml
from pathlib import Path

from src.generators.component_manager import ComponentManager
from src.analyzers.component_dependency_analyzer import ComponentDependencyAnalyzer
from src.validators.component_operations_validator import ComponentOperationsValidator
from src.dependencies.container import FileSystem


class TestComponentManager:
    """Test component management operations."""

    def setup_method(self):
        """Set up test environment."""
        self.temp_dir = Path(tempfile.mkdtemp())
        self.file_system = FileSystem()
        self.component_manager = ComponentManager(self.file_system)
        self.dependency_analyzer = ComponentDependencyAnalyzer()
        self.validator = ComponentOperationsValidator()

        # Create test form
        self.test_form = {
            "container": {
                "type": "ColumnPanel",
                "properties": {
                    "spacing": 10,
                    "padding": 10,
                },
            },
            "components": [
                {
                    "name": "button_submit",
                    "type": "Button",
                    "properties": {
                        "text": "Submit",
                        "role": "outlined-button",
                    },
                    "layout_properties": {
                        "grid_position": "0,0",
                    },
                    "event_bindings": {
                        "click": "button_submit_click",
                    },
                }
            ],
        }

        # Write test form
        self.test_form_path = self.temp_dir / "test_form.yaml"
        with open(self.test_form_path, "w") as f:
            yaml.dump(self.test_form, f)

    def teardown_method(self):
        """Clean up test environment."""
        import shutil

        shutil.rmtree(self.temp_dir)

    def test_add_component_to_form(self):
        """Test adding a component to a form."""
        properties = {"text": "Cancel", "role": "outlined-button"}
        layout_properties = {"grid_position": "0,1"}

        success, message = self.component_manager.add_component_to_form(
            form_path=str(self.test_form_path),
            component_type="Button",
            position="0,1",
            properties=properties,
            layout_properties=layout_properties,
        )

        assert success, f"Failed to add component: {message}"

        # Verify component was added
        with open(self.test_form_path, "r") as f:
            updated_form = yaml.safe_load(f)

        components = updated_form.get("components", [])
        assert len(components) == 2

        new_component = components[1]
        assert new_component["type"] == "Button"
        assert new_component["properties"]["text"] == "Cancel"
        assert new_component["layout_properties"]["grid_position"] == "0,1"

    def test_update_component_properties(self):
        """Test updating component properties."""
        new_properties = {
            "text": "Submit Form",
            "role": "filled-button",
            "enabled": False,
        }

        success, message = self.component_manager.update_component_properties(
            form_path=str(self.test_form_path),
            component_name="button_submit",
            properties=new_properties,
        )

        assert success, f"Failed to update component: {message}"

        # Verify properties were updated
        with open(self.test_form_path, "r") as f:
            updated_form = yaml.safe_load(f)

        components = updated_form.get("components", [])
        button = components[0]
        assert button["properties"]["text"] == "Submit Form"
        assert button["properties"]["role"] == "filled-button"
        assert button["properties"]["enabled"] is False

    def test_remove_component_from_form(self):
        """Test removing a component from a form."""
        success, message = self.component_manager.remove_component_from_form(
            form_path=str(self.test_form_path),
            component_name="button_submit",
        )

        assert success, f"Failed to remove component: {message}"

        # Verify component was removed
        with open(self.test_form_path, "r") as f:
            updated_form = yaml.safe_load(f)

        components = updated_form.get("components", [])
        assert len(components) == 0

    def test_move_component(self):
        """Test moving a component to a new position."""
        success, message = self.component_manager.move_component(
            form_path=str(self.test_form_path),
            component_name="button_submit",
            new_position="2,3",
        )

        assert success, f"Failed to move component: {message}"

        # Verify component was moved
        with open(self.test_form_path, "r") as f:
            updated_form = yaml.safe_load(f)

        components = updated_form.get("components", [])
        button = components[0]
        assert button["layout_properties"]["grid_position"] == "2,3"

    def test_list_form_components(self):
        """Test listing components in a form."""
        success, message, components = self.component_manager.list_form_components(
            form_path=str(self.test_form_path)
        )

        assert success, f"Failed to list components: {message}"
        assert len(components) == 1

        button = components[0]
        assert button["name"] == "button_submit"
        assert button["type"] == "Button"

    def test_list_form_components_with_filter(self):
        """Test listing components with type filter."""
        success, message, components = self.component_manager.list_form_components(
            form_path=str(self.test_form_path),
            component_type_filter="Button",
        )

        assert success, f"Failed to list components: {message}"
        assert len(components) == 1
        assert components[0]["type"] == "Button"

        # Test filter with no matches
        success, message, components = self.component_manager.list_form_components(
            form_path=str(self.test_form_path),
            component_type_filter="TextBox",
        )

        assert success, f"Failed to list components: {message}"
        assert len(components) == 0


class TestComponentDependencyAnalyzer:
    """Test component dependency analysis."""

    def setup_method(self):
        """Set up test environment."""
        self.analyzer = ComponentDependencyAnalyzer()

        self.test_form = {
            "container": {"type": "ColumnPanel"},
            "components": [
                {
                    "name": "button_submit",
                    "type": "Button",
                    "properties": {"text": "Submit"},
                    "event_bindings": {
                        "click": "self.text_input.text",
                    },
                },
                {
                    "name": "text_input",
                    "type": "TextBox",
                    "properties": {"text": ""},
                    "data_bindings": [
                        {
                            "property": "text",
                            "code": "self.button_submit.enabled",
                        }
                    ],
                },
            ],
        }

    def test_analyze_form_dependencies(self):
        """Test analyzing dependencies in a form."""
        inventory = self.analyzer.analyze_form_dependencies(
            self.test_form, "test_form.yaml"
        )

        assert inventory.form_path == "test_form.yaml"
        assert len(inventory.components) == 2
        assert len(inventory.dependencies) == 2

        # Check dependency types
        event_deps = [
            dep for dep in inventory.dependencies if dep.dependency_type == "event"
        ]
        data_deps = [
            dep
            for dep in inventory.dependencies
            if dep.dependency_type == "data_binding"
        ]

        assert len(event_deps) == 1
        assert len(data_deps) == 1
        assert event_deps[0].source_component == "button_submit"
        assert event_deps[0].target_component == "text_input"
        assert data_deps[0].source_component == "text_input"
        assert data_deps[0].target_component == "button_submit"

    def test_get_component_impact_analysis(self):
        """Test impact analysis for component removal."""
        impact = self.analyzer.analyze_dependency_impact(
            self.test_form, "button_submit"
        )

        assert impact["component_name"] == "button_submit"
        assert impact["direct_dependents"] == 1  # text_input depends on button_submit
        assert impact["risk_level"] == "low"
        assert "Data binding" in impact["affected_functionality"]

        # Test impact for component with dependents
        impact = self.analyzer.analyze_dependency_impact(self.test_form, "text_input")

        assert impact["component_name"] == "text_input"
        assert impact["direct_dependents"] == 1  # button_submit depends on text_input
        assert impact["risk_level"] == "low"
        assert "Event handling" in impact["affected_functionality"]


class TestComponentOperationsValidator:
    """Test component operations validation."""

    def setup_method(self):
        """Set up test environment."""
        self.validator = ComponentOperationsValidator()

        self.valid_component = {
            "name": "button_test",
            "type": "Button",
            "properties": {
                "text": "Test",
                "role": "outlined-button",
            },
            "layout_properties": {
                "grid_position": "0,0",
            },
        }

        self.test_form = {
            "container": {"type": "ColumnPanel"},
            "components": [],  # Empty form for add validation test
        }

    def test_validate_add_operation(self):
        """Test validation of add operation."""
        is_valid, errors, warnings = self.validator.validate_add_operation(
            self.test_form, self.valid_component
        )

        assert is_valid, f"Valid component should pass validation: {errors}"
        assert len(warnings) == 0

    def test_validate_add_operation_invalid(self):
        """Test validation of invalid add operation."""
        invalid_component = {
            "name": "button_test",
            "type": "InvalidType",  # Invalid type
            "properties": {},
        }

        is_valid, errors, warnings = self.validator.validate_add_operation(
            self.test_form, invalid_component
        )

        assert not is_valid, "Invalid component should fail validation"
        assert any("Invalid component type" in error for error in errors)

    def test_validate_update_operation(self):
        """Test validation of update operation."""
        # Create form with component for update test
        form_with_component = {
            "container": {"type": "ColumnPanel"},
            "components": [self.valid_component],
        }

        new_properties = {
            "text": "Updated Text",
            "role": "filled-button",
        }

        is_valid, errors, warnings, impact = self.validator.validate_update_operation(
            form_with_component, "button_test", new_properties
        )

        assert is_valid, f"Valid update should pass validation: {errors}"
        assert "affected_bindings" in impact

    def test_validate_remove_operation(self):
        """Test validation of remove operation."""
        # Create form with component for remove test
        form_with_component = {
            "container": {"type": "ColumnPanel"},
            "components": [self.valid_component],
        }

        is_valid, errors, warnings, impact = self.validator.validate_remove_operation(
            form_with_component, "button_test"
        )

        assert is_valid, f"Valid remove should pass validation: {errors}"
        assert "lost_functionality" in impact

    def test_validate_move_operation(self):
        """Test validation of move operation."""
        # Create form with component for move test
        form_with_component = {
            "container": {"type": "ColumnPanel"},
            "components": [self.valid_component],
        }

        is_valid, errors, warnings = self.validator.validate_move_operation(
            form_with_component, "button_test", "1,1"
        )

        assert is_valid, f"Valid move should pass validation: {errors}"
        assert len(warnings) == 0


def test_validate_move_operation_invalid_position(sample_form_config):
    """Test validation of move operation with invalid position."""
    validator = ComponentOperationsValidator()

    # Test invalid position
    is_valid, errors, warnings = validator.validate_move_operation(
        form_config=sample_form_config,
        component_name="button_submit",
        new_position="invalid",
    )

    assert not is_valid, "Invalid position should fail validation"
    assert len(errors) > 0, "Should have validation errors"
    assert any(
        "position" in error.lower() or "grid" in error.lower() for error in errors
    ), f"Expected position error but got: {errors}"


if __name__ == "__main__":
    pytest.main([__file__])
