"""Test plugin system prompt additions."""

import unittest
from unittest.mock import MagicMock, patch
from typing import Dict, Any, Optional


class MockPlugin:
    """Mock plugin with system prompt addition."""

    def __init__(self, addition: Optional[str] = None):
        self.addition = addition

    def get_system_prompt_addition(self) -> Optional[str]:
        return self.addition


class MockPluginNoMethod:
    """Mock plugin without system prompt addition method."""
    pass


class TestPluginSystemPromptAdditions(unittest.TestCase):
    """Test _get_plugin_system_prompt_additions method."""

    def setUp(self):
        """Set up test fixtures."""
        # Create minimal mock dependencies with proper config responses
        self.mock_config = MagicMock()

        def config_get(key, default=None):
            config_values = {
                "core.llm.max_history": 90,
                "core.llm.task_management": {},
                "core.llm.terminal_timeout": 120,
                "core.llm.mcp_timeout": 120,
                "core.llm.native_tool_calling": True,
            }
            return config_values.get(key, default)

        self.mock_config.get.side_effect = config_get

        self.mock_event_bus = MagicMock()
        self.mock_renderer = MagicMock()

    def _create_llm_service_with_plugins(self, plugin_instances: Dict[str, Any]):
        """Create LLMService and set plugin instances."""
        from core.llm.llm_service import LLMService

        # Patch heavy dependencies
        with patch('core.llm.llm_service.APICommunicationService'), \
             patch('core.llm.llm_service.KollaborConversationLogger'), \
             patch('core.llm.llm_service.ConversationManager'), \
             patch('core.llm.llm_service.LLMHookSystem'), \
             patch('core.llm.llm_service.MCPIntegration'), \
             patch('core.llm.llm_service.ToolExecutor'), \
             patch('core.llm.llm_service.MessageDisplayService'), \
             patch('core.llm.llm_service.Path'):

            service = LLMService(
                config=self.mock_config,
                event_bus=self.mock_event_bus,
                renderer=self.mock_renderer,
            )
            service.set_plugin_instances(plugin_instances)
            return service

    def test_no_plugins_returns_empty_list(self):
        """Test with no plugins set."""
        service = self._create_llm_service_with_plugins({})
        # Clear plugin instances to simulate no plugins
        service._plugin_instances = None

        additions = service._get_plugin_system_prompt_additions()

        self.assertEqual(additions, [])

    def test_plugin_with_addition(self):
        """Test plugin that returns an addition."""
        plugin_instances = {
            "test_plugin": MockPlugin("## Test Instructions\nDo something.")
        }
        service = self._create_llm_service_with_plugins(plugin_instances)

        additions = service._get_plugin_system_prompt_additions()

        self.assertEqual(len(additions), 1)
        self.assertIn("Test Instructions", additions[0])

    def test_plugin_without_method(self):
        """Test plugin without get_system_prompt_addition method."""
        plugin_instances = {
            "no_method_plugin": MockPluginNoMethod()
        }
        service = self._create_llm_service_with_plugins(plugin_instances)

        additions = service._get_plugin_system_prompt_additions()

        self.assertEqual(additions, [])

    def test_plugin_returns_none(self):
        """Test plugin that returns None."""
        plugin_instances = {
            "none_plugin": MockPlugin(None)
        }
        service = self._create_llm_service_with_plugins(plugin_instances)

        additions = service._get_plugin_system_prompt_additions()

        self.assertEqual(additions, [])

    def test_multiple_plugins_mixed(self):
        """Test multiple plugins with mixed behavior."""
        plugin_instances = {
            "plugin_with_addition": MockPlugin("## Agent Orchestration\nSpawn agents."),
            "plugin_none": MockPlugin(None),
            "plugin_no_method": MockPluginNoMethod(),
            "plugin_with_addition2": MockPlugin("## Another Feature\nDo things."),
        }
        service = self._create_llm_service_with_plugins(plugin_instances)

        additions = service._get_plugin_system_prompt_additions()

        self.assertEqual(len(additions), 2)
        # Check both additions are present
        combined = "\n".join(additions)
        self.assertIn("Agent Orchestration", combined)
        self.assertIn("Another Feature", combined)

    def test_plugin_exception_handled(self):
        """Test that plugin exceptions are handled gracefully."""
        class FailingPlugin:
            def get_system_prompt_addition(self):
                raise RuntimeError("Plugin error!")

        plugin_instances = {
            "failing_plugin": FailingPlugin(),
            "good_plugin": MockPlugin("## Good Plugin\nWorks fine."),
        }
        service = self._create_llm_service_with_plugins(plugin_instances)

        # Should not raise, should return the good plugin's addition
        additions = service._get_plugin_system_prompt_additions()

        self.assertEqual(len(additions), 1)
        self.assertIn("Good Plugin", additions[0])


class TestAgentOrchestratorSystemPrompt(unittest.TestCase):
    """Test AgentOrchestratorPlugin system prompt addition."""

    def test_startup_mode_returns_instructions(self):
        """Test that startup mode returns agent instructions."""
        from plugins.agent_orchestrator.plugin import AgentOrchestratorPlugin, AGENT_INSTRUCTIONS

        mock_config = MagicMock()
        mock_config.get.side_effect = lambda key, default=None: {
            "plugins.agent_orchestrator.enable_mode": "startup",
        }.get(key, default)

        plugin = AgentOrchestratorPlugin(config=mock_config)
        plugin.config = mock_config

        addition = plugin.get_system_prompt_addition()

        self.assertEqual(addition, AGENT_INSTRUCTIONS)

    def test_keyword_mode_returns_none(self):
        """Test that keyword mode returns None (injected via event)."""
        from plugins.agent_orchestrator.plugin import AgentOrchestratorPlugin

        mock_config = MagicMock()
        mock_config.get.side_effect = lambda key, default=None: {
            "plugins.agent_orchestrator.enable_mode": "keyword",
        }.get(key, default)

        plugin = AgentOrchestratorPlugin(config=mock_config)
        plugin.config = mock_config

        addition = plugin.get_system_prompt_addition()

        self.assertIsNone(addition)

    def test_disabled_mode_returns_none(self):
        """Test that disabled mode returns None."""
        from plugins.agent_orchestrator.plugin import AgentOrchestratorPlugin

        mock_config = MagicMock()
        mock_config.get.side_effect = lambda key, default=None: {
            "plugins.agent_orchestrator.enable_mode": "disabled",
        }.get(key, default)

        plugin = AgentOrchestratorPlugin(config=mock_config)
        plugin.config = mock_config

        addition = plugin.get_system_prompt_addition()

        self.assertIsNone(addition)


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