"""
Tests for engine.effects.controller module.
"""

from unittest.mock import MagicMock, patch

from engine.effects.controller import (
    _format_stats,
    handle_effects_command,
    set_effect_chain_ref,
    show_effects_menu,
)


class TestHandleEffectsCommand:
    """Tests for handle_effects_command function."""

    def test_list_effects(self):
        """list command returns formatted effects list."""
        with patch("engine.effects.controller.get_registry") as mock_registry:
            mock_plugin = MagicMock()
            mock_plugin.config.enabled = True
            mock_plugin.config.intensity = 0.5
            mock_registry.return_value.list_all.return_value = {"noise": mock_plugin}

            with patch("engine.effects.controller._get_effect_chain") as mock_chain:
                mock_chain.return_value.get_order.return_value = ["noise"]

                result = handle_effects_command("/effects list")

                assert "noise: ON" in result
                assert "intensity=0.5" in result

    def test_enable_effect(self):
        """enable command calls registry.enable."""
        with patch("engine.effects.controller.get_registry") as mock_registry:
            mock_plugin = MagicMock()
            mock_registry.return_value.get.return_value = mock_plugin
            mock_registry.return_value.list_all.return_value = {"noise": mock_plugin}

            result = handle_effects_command("/effects noise on")

            assert "Enabled: noise" in result
            mock_registry.return_value.enable.assert_called_once_with("noise")

    def test_disable_effect(self):
        """disable command calls registry.disable."""
        with patch("engine.effects.controller.get_registry") as mock_registry:
            mock_plugin = MagicMock()
            mock_registry.return_value.get.return_value = mock_plugin
            mock_registry.return_value.list_all.return_value = {"noise": mock_plugin}

            result = handle_effects_command("/effects noise off")

            assert "Disabled: noise" in result
            mock_registry.return_value.disable.assert_called_once_with("noise")

    def test_set_intensity(self):
        """intensity command sets plugin intensity."""
        with patch("engine.effects.controller.get_registry") as mock_registry:
            mock_plugin = MagicMock()
            mock_plugin.config.intensity = 0.5
            mock_registry.return_value.get.return_value = mock_plugin
            mock_registry.return_value.list_all.return_value = {"noise": mock_plugin}

            result = handle_effects_command("/effects noise intensity 0.8")

            assert "intensity to 0.8" in result
            assert mock_plugin.config.intensity == 0.8

    def test_invalid_intensity_range(self):
        """intensity outside 0.0-1.0 returns error."""
        with patch("engine.effects.controller.get_registry") as mock_registry:
            mock_plugin = MagicMock()
            mock_registry.return_value.get.return_value = mock_plugin
            mock_registry.return_value.list_all.return_value = {"noise": mock_plugin}

            result = handle_effects_command("/effects noise intensity 1.5")

            assert "between 0.0 and 1.0" in result

    def test_reorder_pipeline(self):
        """reorder command calls chain.reorder."""
        with patch("engine.effects.controller.get_registry") as mock_registry:
            mock_registry.return_value.list_all.return_value = {}

            with patch("engine.effects.controller._get_effect_chain") as mock_chain:
                mock_chain_instance = MagicMock()
                mock_chain_instance.reorder.return_value = True
                mock_chain.return_value = mock_chain_instance

                result = handle_effects_command("/effects reorder noise,fade")

                assert "Reordered pipeline" in result
                mock_chain_instance.reorder.assert_called_once_with(["noise", "fade"])

    def test_reorder_failure(self):
        """reorder returns error on failure."""
        with patch("engine.effects.controller.get_registry") as mock_registry:
            mock_registry.return_value.list_all.return_value = {}

            with patch("engine.effects.controller._get_effect_chain") as mock_chain:
                mock_chain_instance = MagicMock()
                mock_chain_instance.reorder.return_value = False
                mock_chain.return_value = mock_chain_instance

                result = handle_effects_command("/effects reorder bad")

                assert "Failed to reorder" in result

    def test_unknown_effect(self):
        """unknown effect returns error."""
        with patch("engine.effects.controller.get_registry") as mock_registry:
            mock_registry.return_value.list_all.return_value = {}

            result = handle_effects_command("/effects unknown on")

            assert "Unknown effect" in result

    def test_unknown_command(self):
        """unknown command returns error."""
        result = handle_effects_command("/unknown")
        assert "Unknown command" in result

    def test_non_effects_command(self):
        """non-effects command returns error."""
        result = handle_effects_command("not a command")
        assert "Unknown command" in result

    def test_invalid_intensity_value(self):
        """invalid intensity value returns error."""
        with patch("engine.effects.controller.get_registry") as mock_registry:
            mock_plugin = MagicMock()
            mock_registry.return_value.get.return_value = mock_plugin
            mock_registry.return_value.list_all.return_value = {"noise": mock_plugin}

            result = handle_effects_command("/effects noise intensity bad")

            assert "Invalid intensity" in result

    def test_missing_action(self):
        """missing action returns usage."""
        with patch("engine.effects.controller.get_registry") as mock_registry:
            mock_plugin = MagicMock()
            mock_registry.return_value.get.return_value = mock_plugin
            mock_registry.return_value.list_all.return_value = {"noise": mock_plugin}

            result = handle_effects_command("/effects noise")

            assert "Usage" in result

    def test_stats_command(self):
        """stats command returns formatted stats."""
        with patch("engine.effects.controller.get_monitor") as mock_monitor:
            mock_monitor.return_value.get_stats.return_value = {
                "frame_count": 100,
                "pipeline": {"avg_ms": 1.5, "min_ms": 1.0, "max_ms": 2.0},
                "effects": {},
            }

            result = handle_effects_command("/effects stats")

            assert "Performance Stats" in result

    def test_list_only_effects(self):
        """list command works with just /effects."""
        with patch("engine.effects.controller.get_registry") as mock_registry:
            mock_plugin = MagicMock()
            mock_plugin.config.enabled = False
            mock_plugin.config.intensity = 0.5
            mock_registry.return_value.list_all.return_value = {"noise": mock_plugin}

            with patch("engine.effects.controller._get_effect_chain") as mock_chain:
                mock_chain.return_value = None

                result = handle_effects_command("/effects")

                assert "noise: OFF" in result


class TestShowEffectsMenu:
    """Tests for show_effects_menu function."""

    def test_returns_formatted_menu(self):
        """returns formatted effects menu."""
        with patch("engine.effects.controller.get_registry") as mock_registry:
            mock_plugin = MagicMock()
            mock_plugin.config.enabled = True
            mock_plugin.config.intensity = 0.75
            mock_registry.return_value.list_all.return_value = {"noise": mock_plugin}

            with patch("engine.effects.controller._get_effect_chain") as mock_chain:
                mock_chain_instance = MagicMock()
                mock_chain_instance.get_order.return_value = ["noise"]
                mock_chain.return_value = mock_chain_instance

                result = show_effects_menu()

                assert "EFFECTS MENU" in result
                assert "noise" in result


class TestFormatStats:
    """Tests for _format_stats function."""

    def test_returns_error_when_no_monitor(self):
        """returns error when monitor unavailable."""
        with patch("engine.effects.controller.get_monitor") as mock_monitor:
            mock_monitor.return_value.get_stats.return_value = {"error": "No data"}

            result = _format_stats()

            assert "No data" in result

    def test_formats_pipeline_stats(self):
        """formats pipeline stats correctly."""
        with patch("engine.effects.controller.get_monitor") as mock_monitor:
            mock_monitor.return_value.get_stats.return_value = {
                "frame_count": 50,
                "pipeline": {"avg_ms": 2.5, "min_ms": 2.0, "max_ms": 3.0},
                "effects": {"noise": {"avg_ms": 0.5, "min_ms": 0.4, "max_ms": 0.6}},
            }

            result = _format_stats()

            assert "Pipeline" in result
            assert "noise" in result


class TestSetEffectChainRef:
    """Tests for set_effect_chain_ref function."""

    def test_sets_global_ref(self):
        """set_effect_chain_ref updates global reference."""
        mock_chain = MagicMock()
        set_effect_chain_ref(mock_chain)

        from engine.effects.controller import _get_effect_chain

        result = _get_effect_chain()
        assert result == mock_chain
