"""
Unit tests for interaction command factory.
"""

import pytest
from unittest.mock import Mock
from pyPhotoAlbum.mixins.interaction_command_factory import InteractionCommandFactory, InteractionState
from pyPhotoAlbum.mixins.interaction_command_builders import CommandBuilder


class TestInteractionState:
    """Tests for InteractionState value object."""

    def test_initialization(self):
        """Test that InteractionState initializes correctly."""
        element = Mock()
        state = InteractionState(
            element=element, interaction_type="move", position=(0.0, 0.0), size=(100.0, 100.0), rotation=0.0
        )

        assert state.element == element
        assert state.interaction_type == "move"
        assert state.position == (0.0, 0.0)
        assert state.size == (100.0, 100.0)
        assert state.rotation == 0.0

    def test_to_dict(self):
        """Test that to_dict returns correct dictionary."""
        state = InteractionState(position=(0.0, 0.0), size=(100.0, 100.0))

        result = state.to_dict()

        assert result == {"position": (0.0, 0.0), "size": (100.0, 100.0)}

    def test_to_dict_excludes_none(self):
        """Test that to_dict excludes None values."""
        state = InteractionState(position=(0.0, 0.0), size=None)

        result = state.to_dict()

        assert "position" in result
        assert "size" not in result

    def test_is_valid_with_required_fields(self):
        """Test that is_valid returns True when required fields are present."""
        element = Mock()
        state = InteractionState(element=element, interaction_type="move")

        assert state.is_valid()

    def test_is_valid_without_element(self):
        """Test that is_valid returns False without element."""
        state = InteractionState(element=None, interaction_type="move")

        assert not state.is_valid()

    def test_is_valid_without_interaction_type(self):
        """Test that is_valid returns False without interaction_type."""
        element = Mock()
        state = InteractionState(element=element, interaction_type=None)

        assert not state.is_valid()

    def test_clear(self):
        """Test that clear resets all fields."""
        element = Mock()
        state = InteractionState(
            element=element, interaction_type="move", position=(0.0, 0.0), size=(100.0, 100.0), rotation=0.0
        )

        state.clear()

        assert state.element is None
        assert state.interaction_type is None
        assert state.position is None
        assert state.size is None
        assert state.rotation is None


class TestInteractionCommandFactory:
    """Tests for InteractionCommandFactory."""

    def test_initialization_registers_default_builders(self):
        """Test that factory initializes with default builders."""
        factory = InteractionCommandFactory()

        assert factory.has_builder("move")
        assert factory.has_builder("resize")
        assert factory.has_builder("rotate")
        assert factory.has_builder("image_pan")

    def test_register_builder(self):
        """Test registering a custom builder."""
        factory = InteractionCommandFactory()
        custom_builder = Mock(spec=CommandBuilder)

        factory.register_builder("custom", custom_builder)

        assert factory.has_builder("custom")

    def test_get_supported_types(self):
        """Test getting list of supported types."""
        factory = InteractionCommandFactory()

        types = factory.get_supported_types()

        assert "move" in types
        assert "resize" in types
        assert "rotate" in types
        assert "image_pan" in types

    def test_create_command_move(self):
        """Test creating a move command."""
        factory = InteractionCommandFactory()
        element = Mock()
        element.position = (10.0, 10.0)

        start_state = {"position": (0.0, 0.0)}

        command = factory.create_command("move", element, start_state)

        assert command is not None

    def test_create_command_resize(self):
        """Test creating a resize command."""
        factory = InteractionCommandFactory()
        element = Mock()
        element.position = (10.0, 10.0)
        element.size = (200.0, 200.0)

        start_state = {"position": (0.0, 0.0), "size": (100.0, 100.0)}

        command = factory.create_command("resize", element, start_state)

        assert command is not None

    def test_create_command_rotate(self):
        """Test creating a rotate command."""
        factory = InteractionCommandFactory()
        element = Mock()
        element.rotation = 45.0

        start_state = {"rotation": 0.0}

        command = factory.create_command("rotate", element, start_state)

        assert command is not None

    def test_create_command_unknown_type(self, capsys):
        """Test creating command with unknown type."""
        factory = InteractionCommandFactory()
        element = Mock()

        command = factory.create_command("unknown", element, {})

        assert command is None
        captured = capsys.readouterr()
        assert "No builder registered for interaction type 'unknown'" in captured.out

    def test_create_command_no_significant_change(self):
        """Test that no command is created for insignificant changes."""
        factory = InteractionCommandFactory()
        element = Mock()
        element.position = (0.05, 0.05)

        start_state = {"position": (0.0, 0.0)}

        command = factory.create_command("move", element, start_state)

        assert command is None

    def test_create_command_with_custom_builder(self):
        """Test using a custom builder."""
        factory = InteractionCommandFactory()

        # Create a mock builder that always returns a mock command
        custom_builder = Mock(spec=CommandBuilder)
        mock_command = Mock()
        custom_builder.can_build.return_value = True
        custom_builder.build.return_value = mock_command

        factory.register_builder("custom", custom_builder)

        element = Mock()
        start_state = {"position": (0.0, 0.0)}

        command = factory.create_command("custom", element, start_state)

        assert command == mock_command
        custom_builder.can_build.assert_called_once()
        custom_builder.build.assert_called_once()


class TestInteractionStateIntegration:
    """Integration tests for InteractionState with factory."""

    def test_state_to_dict_with_factory(self):
        """Test that state.to_dict() works with factory."""
        factory = InteractionCommandFactory()
        element = Mock()
        element.position = (10.0, 10.0)

        state = InteractionState(element=element, interaction_type="move", position=(0.0, 0.0))

        command = factory.create_command(state.interaction_type, state.element, state.to_dict())

        assert command is not None

    def test_state_lifecycle(self):
        """Test complete lifecycle of interaction state."""
        element = Mock()
        element.position = (0.0, 0.0)

        # Begin interaction
        state = InteractionState()
        state.element = element
        state.interaction_type = "move"
        state.position = element.position

        assert state.is_valid()

        # Simulate movement
        element.position = (10.0, 10.0)

        # Create command
        factory = InteractionCommandFactory()
        command = factory.create_command(state.interaction_type, state.element, state.to_dict())

        assert command is not None

        # Clear state
        state.clear()
        assert not state.is_valid()
