"""
Integration Tests

Test components working together:
- Registry with models
- Models with fusion engines
- End-to-end workflows
- Model switching
"""

import pytest
from unittest.mock import Mock, patch, MagicMock
from kernel_service.registry import get_registry
from kernel_service.models.k1 import CXRK1Model
from kernel_service.models.lite1 import CXRLite1Model
from kernel_service.fusion.seven_plane import SevenPlaneFusionEngine
from kernel_service.fusion.evidence_fusion import EvidenceFusionEngine


class TestRegistryIntegration:
    """Test registry working with models"""

    def test_registry_with_all_models(self, reset_registry):
        """Test registry can retrieve all registered models"""
        registry = get_registry()
        
        # All models should be auto-registered via __init__.py
        models = registry.list_models()
        assert "cxr-k1" in models
        assert "cxr-k2" in models
        assert "cxr-k-sim1" in models
        assert "cxr-k-lite1" in models
        assert "cxr-k-audit1" in models

    def test_model_through_registry(self, reset_registry):
        """Test can get and use model through registry"""
        registry = get_registry()
        model = registry.get_model("cxr-k-lite1")
        
        assert model is not None
        assert model.model_id == "cxr-k-lite1"

    def test_multiple_models_same_claim(self, reset_registry, sample_claim):
        """Test different models can analyze same claim"""
        registry = get_registry()
        
        k1_model = registry.get_model("cxr-k1")
        lite1_model = registry.get_model("cxr-k-lite1")
        
        # Both should be able to analyze (with mocked dependencies)
        with patch.object(k1_model, '_get_fusion_engine'), \
             patch.object(k1_model, '_get_context_collector'), \
             patch.object(lite1_model, '_get_fusion_engine'):
            
            # Mock the fusion engines
            k1_model._get_fusion_engine = Mock(return_value=Mock(
                get_structured_evidence=Mock(return_value=0.7),
                get_unstructured_evidence=Mock(return_value=0.6),
                get_contextual_evidence=Mock(return_value=0.5),
                get_patient_evidence=Mock(return_value=0.4),
                get_provider_evidence=Mock(return_value=0.3),
                get_payer_evidence=Mock(return_value=0.2),
                get_temporal_evidence=Mock(return_value=0.1),
                fuse=Mock(return_value=(0.6, {}))
            ))
            
            lite1_model._get_fusion_engine = Mock(return_value=Mock(
                get_structured_evidence=Mock(return_value={"confidence": 0.7}),
                get_unstructured_evidence=Mock(return_value={"confidence": 0.6}),
                get_contextual_evidence=Mock(return_value={"confidence": 0.5})
            ))
            
            # Both should produce results
            result1 = k1_model.analyze(sample_claim, "tenant-1")
            result2 = lite1_model.analyze(sample_claim, "tenant-1")
            
            assert "verdict" in result1
            assert "verdict" in result2

    def test_model_switching(self, reset_registry, sample_claim):
        """Test can switch between models dynamically"""
        registry = get_registry()
        
        # Start with k1
        model1 = registry.get_model("cxr-k1")
        assert model1.model_id == "cxr-k1"
        
        # Switch to lite1
        model2 = registry.get_model("cxr-k-lite1")
        assert model2.model_id == "cxr-k-lite1"
        
        # Models should be different instances
        assert model1 is not model2


class TestFusionIntegration:
    """Test fusion engines working with models"""

    def test_k1_uses_7plane_fusion(self, reset_registry):
        """Test k1 model uses 7-plane fusion engine"""
        registry = get_registry()
        k1_model = registry.get_model("cxr-k1")
        
        # Should use SevenPlaneFusionEngine
        with patch.object(k1_model, '_get_db_connection'), \
             patch.object(k1_model, '_get_qdrant_client'):
            
            fusion_engine = k1_model._get_fusion_engine()
            assert isinstance(fusion_engine, SevenPlaneFusionEngine)

    def test_lite1_uses_3plane_fusion(self, reset_registry):
        """Test lite1 model uses 3-plane fusion engine"""
        registry = get_registry()
        lite1_model = registry.get_model("cxr-k-lite1")
        
        # Should use EvidenceFusionEngine (3-plane)
        with patch.object(lite1_model, '_get_db_connection'):
            fusion_engine = lite1_model._get_fusion_engine()
            assert isinstance(fusion_engine, EvidenceFusionEngine)

    def test_fusion_with_contexts(self, reset_registry, sample_claim):
        """Test fusion engine works with context collector"""
        registry = get_registry()
        k1_model = registry.get_model("cxr-k1")
        
        # Mock both fusion engine and context collector
        mock_fusion = Mock()
        mock_fusion.get_structured_evidence = Mock(return_value=0.7)
        mock_fusion.get_unstructured_evidence = Mock(return_value=0.6)
        mock_fusion.get_contextual_evidence = Mock(return_value=0.5)
        mock_fusion.get_patient_evidence = Mock(return_value=0.4)
        mock_fusion.get_provider_evidence = Mock(return_value=0.3)
        mock_fusion.get_payer_evidence = Mock(return_value=0.2)
        mock_fusion.get_temporal_evidence = Mock(return_value=0.1)
        mock_fusion.fuse = Mock(return_value=(0.6, {}))
        
        mock_context = Mock()
        mock_context.get_patient_context = Mock(return_value={"patient_denial_rate": 0.2})
        mock_context.get_provider_context = Mock(return_value={"provider_denial_rate": 0.15})
        mock_context.get_payer_context = Mock(return_value={"payer_denial_rate": 0.25})
        mock_context.get_temporal_context = Mock(return_value={"claim_age_days": 30})
        mock_context.get_financial_context = Mock(return_value={})
        mock_context.get_relationship_context = Mock(return_value={})
        mock_context.get_policy_context = Mock(return_value={})
        
        k1_model._fusion_engine = mock_fusion
        k1_model._context_collector = mock_context
        
        result = k1_model.analyze(sample_claim, "tenant-1")
        
        # Verify contexts were collected
        mock_context.get_patient_context.assert_called_once()
        mock_context.get_provider_context.assert_called_once()
        
        # Verify fusion was called
        mock_fusion.fuse.assert_called_once()
        
        assert "verdict" in result


class TestEndToEndWorkflow:
    """Test complete analysis workflows"""

    def test_end_to_end_analysis(self, reset_registry, sample_claim):
        """Test complete analysis workflow from claim to result"""
        registry = get_registry()
        model = registry.get_model("cxr-k-lite1")
        
        # Mock all dependencies
        mock_fusion = Mock()
        mock_fusion.get_structured_evidence = Mock(return_value={"confidence": 0.7})
        mock_fusion.get_unstructured_evidence = Mock(return_value={"confidence": 0.6})
        mock_fusion.get_contextual_evidence = Mock(return_value={"confidence": 0.5})
        
        model._fusion_engine = mock_fusion
        
        # Execute workflow
        result = model.analyze(sample_claim, "tenant-123")
        
        # Verify complete result structure
        assert "verdict" in result
        assert "explanation" in result
        assert "financialImpact" in result
        assert result["verdict"] in ["approved", "denied", "pending_review"]
        
        # Verify explanation structure
        assert "summary" in result["explanation"]
        
        # Verify financial impact structure
        assert "projectedRecovery" in result["financialImpact"]
        assert "currency" in result["financialImpact"]

    def test_sim1_with_monte_carlo(self, reset_registry, sample_claim):
        """Test sim1 model integrates Monte Carlo correctly"""
        registry = get_registry()
        sim1_model = registry.get_model("cxr-k-sim1")
        
        # Mock dependencies
        mock_fusion = Mock()
        mock_fusion.get_structured_evidence = Mock(return_value=0.7)
        mock_fusion.get_unstructured_evidence = Mock(return_value=0.6)
        mock_fusion.get_contextual_evidence = Mock(return_value=0.5)
        mock_fusion.get_patient_evidence = Mock(return_value=0.4)
        mock_fusion.get_provider_evidence = Mock(return_value=0.3)
        mock_fusion.get_payer_evidence = Mock(return_value=0.2)
        mock_fusion.get_temporal_evidence = Mock(return_value=0.1)
        mock_fusion.fuse = Mock(return_value=(0.8, {}))
        
        mock_context = Mock()
        mock_context.get_patient_context = Mock(return_value={})
        mock_context.get_provider_context = Mock(return_value={})
        mock_context.get_payer_context = Mock(return_value={})
        mock_context.get_temporal_context = Mock(return_value={})
        
        sim1_model._fusion_engine = mock_fusion
        sim1_model._context_collector = mock_context
        
        # Set high confidence to trigger denial (which uses Monte Carlo)
        sample_claim["metadata"] = {"reviewThreshold": 0.9}
        sample_claim["billedAmount"] = 1000.0
        
        result = sim1_model.analyze(sample_claim, "tenant-1")
        
        # If denied, should have Monte Carlo data
        if result["verdict"] == "denied":
            assert "monteCarlo" in result["financialImpact"] or "confidence" in result["financialImpact"]


class TestModelCapabilities:
    """Test model capabilities are correctly exposed"""

    def test_capabilities_accessible(self, reset_registry):
        """Test can access model capabilities through registry"""
        registry = get_registry()
        
        k1_caps = registry.get_capabilities("cxr-k1")
        assert k1_caps.fusion_planes == 7
        assert k1_caps.uses_all_contexts is True
        assert k1_caps.uses_monte_carlo is False
        
        sim1_caps = registry.get_capabilities("cxr-k-sim1")
        assert sim1_caps.uses_monte_carlo is True
        
        lite1_caps = registry.get_capabilities("cxr-k-lite1")
        assert lite1_caps.fusion_planes == 3
        assert lite1_caps.speed_profile == "fast"

