import pytest
import json
import os
from unittest.mock import MagicMock, AsyncMock, patch
from src.tools import LLMManager

# Load mock data
MOCK_DATA_PATH = os.path.join(os.path.dirname(__file__), "data", "round_table_simulation.json")
with open(MOCK_DATA_PATH) as f:
    SIMULATION = json.load(f)

class MockProviderStrategy:
    """
    Intelligent mock that returns responses based on the input 'state' (phase).
    """
    def __init__(self):
        self.sim = SIMULATION

    async def generate_response_side_effect(self, model, messages, system_prompt=None):
        # Determine caller identity implicitly or by model mapping if needed.
        # But here we are patching the *instance* or the class method. 
        # Ideally, we need to know WHICH provider is being called.
        # We'll handle this by looking at the model name passed in the call.
        
        # Identify Phase
        is_phase_1 = len(messages) == 1 and not system_prompt # User prompt only
        is_phase_2 = system_prompt and "Compare these solutions" in system_prompt
        is_phase_3 = system_prompt and "Moderator" in system_prompt
        
        provider_key = self._get_provider_key_from_model(model)

        if is_phase_1:
            return self.sim["phase_1_drafts"].get(provider_key, "Mock Draft")
            
        if is_phase_2:
            return self.sim["phase_2_critiques"].get(provider_key, "Mock Critique")
            
        if is_phase_3:
             # Moderator can be any key usually, but let's assume openai:gpt-4o for this test
            return self.sim["phase_3_synthesis"].get(provider_key, "Mock Consensus")

        return "Unknown Phase Response"

    def _get_provider_key_from_model(self, model):
        # Helper to map model/mock call back to our JSON keys
        if "gpt-4o" in model: return "openai:gpt-4o"
        if "claude" in model: return "anthropic:claude-3-5-sonnet"
        if "deepseek" in model: return "deepseek:deepseek-coder"
        return "unknown"

@pytest.mark.asyncio
async def test_round_table_integration_flow():
    """
    Verifies the full Round Table orchestration logic using deterministic mock data.
    """
    manager = LLMManager()
    
    # Strategy instance
    strategy = MockProviderStrategy()
    
    # We need to patch the internal providers generated by _get_provider.
    # Since _get_provider creates new instances or returns cached ones, 
    # the easiest way is to mock the `_get_provider` method OR the `LLMProvider.generate_response` method broadly.
    
    # Let's mock the `generate_response` on the BASE class or the specific classes so ANY provider uses our logic.
    # But strictly speaking, LLMProvider is abstract-ish. 
    # Let's patch `src.tools.LLMManager._get_provider` to return a MagicMock that has our side_effect.
    
    mock_provider_instance = AsyncMock()
    mock_provider_instance.generate_response.side_effect = strategy.generate_response_side_effect
    
    # We patch _get_provider to always return this master mock instance. 
    # This means 'openai', 'anthropic', 'deepseek' all become this one mock object.
    # The 'strategy' distinguishes them by the 'model' argument passed to generate_response.
    with patch.object(manager, '_get_provider', return_value=mock_provider_instance):
        
        prompt = "How to build a scalable API"
        panelists = [
            {"provider": "openai", "model": "gpt-4o"},
            {"provider": "anthropic", "model": "claude-3-5-sonnet"},
            {"provider": "deepseek", "model": "deepseek-coder"}
        ]
        
        result = await manager.round_table_debate(
            prompt=prompt,
            panelists=panelists,
            moderator_provider="openai"
        )
        
        # Assertions
        assert "Concensus" in str(result) or "Consensus" in str(result)
        assert "Use FastAPI" in str(result) # From the synthesis mock
        assert "Recommendations for Improvement" in str(result)
        
        # Verify calls
        # Phase 1: 3 calls
        # Phase 2: 3 calls
        # Phase 3: 1 call
        # Total = 7 calls
        assert mock_provider_instance.generate_response.call_count == 7
