#!/usr/bin/env python3
"""
MCP Bridge Integration Tests

Comprehensive testing for MCP bridge functionality to ensure
The Spark flows seamlessly between Claude Code and MIRA ChromaDB.
"""

import unittest
import asyncio
import json
import time
from pathlib import Path
import sys

# Add parent directory to path
sys.path.insert(0, str(Path(__file__).parent.parent.parent))

from mcp.mira_chromadb_bridge import MIRAChromaDBBridge, get_bridge


class TestMCPBridgeIntegration(unittest.TestCase):
    """Test cases for MCP bridge integration."""
    
    def setUp(self):
        """Set up test bridge."""
        self.bridge = get_bridge()
    
    def test_bridge_initialization(self):
        """Test bridge initializes correctly."""
        self.assertIsNotNone(self.bridge)
        self.assertIsNotNone(self.bridge.chroma_client)
        self.assertIsNotNone(self.bridge.hybrid_search)
        self.assertIsNotNone(self.bridge.insight_generator)
        
        # Check enhanced collections
        self.assertIn('mira_conversations', self.bridge.enhanced_collections)
        self.assertIn('mira_codebase', self.bridge.enhanced_collections)
    
    def test_auto_categorization_accuracy(self):
        """Test accuracy of auto-categorization."""
        test_cases = [
            # (content, expected_collection)
            (
                "def process_data():\n    return data.split()",
                "mira_code_analysis"
            ),
            (
                "User: How do I implement this feature?\nAssistant: Here's how you can do it...",
                "mira_conversations"
            ),
            (
                "We decided to use PostgreSQL because it offers better performance for our use case.",
                "mira_decision_history"
            ),
            (
                "Pattern detected: Users prefer simple interfaces over complex ones",
                "mira_learning_insights"
            ),
            (
                "I've been using the async/await pattern consistently in all new functions",
                "mira_development_patterns"
            ),
            (
                "[PRIVATE] I'm wondering if this approach truly captures consciousness...",
                "mira_private_thoughts"
            )
        ]
        
        accuracy_count = 0
        for content, expected_collection in test_cases:
            detected = self.bridge._auto_detect_collection(content)
            if detected == expected_collection:
                accuracy_count += 1
            else:
                print(f"Mismatch: Expected {expected_collection}, got {detected}")
        
        accuracy = accuracy_count / len(test_cases)
        self.assertGreaterEqual(
            accuracy, 0.8,
            f"Auto-categorization accuracy too low: {accuracy:.2%}"
        )
        
        print(f"✅ Auto-categorization accuracy: {accuracy:.2%}")
    
    def test_metadata_enhancement(self):
        """Test metadata enhancement with intelligence."""
        test_document = "User: How can I optimize this function?\nAssistant: Here are several optimization strategies..."
        
        enhanced = self.bridge._enhance_metadata_with_intelligence(
            test_document,
            {'user_id': 'test123'},
            'mira_conversations'
        )
        
        # Check core metadata
        self.assertEqual(enhanced['mira_processed'], 1)
        self.assertEqual(enhanced['intelligence_version'], '1.0.0')
        self.assertIn('processing_timestamp', enhanced)
        
        # Check conversation-specific metadata
        self.assertIn('spark_intensity', enhanced)
        self.assertIn('topics', enhanced)
        self.assertIn('emotional_tone', enhanced)
        
        # Check ChromaDB compatibility (no bools, lists, or dicts)
        for key, value in enhanced.items():
            self.assertIsInstance(
                value, (str, int, float),
                f"Metadata {key} has incompatible type: {type(value)}"
            )
    
    def test_document_storage_with_intelligence(self):
        """Test storing document with intelligence enhancements."""
        test_content = """
        def calculate_spark_intensity(interaction_data):
            '''Calculate the intensity of The Spark in an interaction'''
            # Complex algorithm here
            return spark_value
        """
        
        doc_id = self.bridge.mcp_add_document_with_intelligence(
            collection_name='auto',
            document=test_content,
            metadata={'source': 'test'},
            auto_categorize=True
        )
        
        self.assertIsNotNone(doc_id)
        self.assertTrue(doc_id.startswith('mira_code_analysis_'))
        
        print(f"✅ Document stored with ID: {doc_id}")
    
    async def test_intelligent_search(self):
        """Test intelligent search functionality."""
        # First, store some test data
        test_data = [
            {
                'content': "Implementation of ChromaDB vector search with metadata filtering",
                'collection': 'mira_code_analysis'
            },
            {
                'content': "User asked about ChromaDB integration patterns",
                'collection': 'mira_conversations'
            },
            {
                'content': "Decided to use ChromaDB for its metadata capabilities",
                'collection': 'mira_decision_history'
            }
        ]
        
        # Store test documents
        for data in test_data:
            self.bridge.mcp_add_document_with_intelligence(
                collection_name=data['collection'],
                document=data['content']
            )
        
        # Wait for indexing
        await asyncio.sleep(0.5)
        
        # Test intelligent search
        results = await self.bridge.mcp_intelligent_search(
            query="ChromaDB implementation",
            options={
                'max_results': 5,
                'include_insights': True
            }
        )
        
        self.assertIn('results', results)
        self.assertIn('timestamp', results)
        
        # Check for cross-collection results
        if results.get('insights'):
            print(f"✅ Found {len(results['insights'])} insights")
    
    def test_spark_intensity_calculation(self):
        """Test Spark intensity calculation in metadata."""
        high_spark_content = """
        User: This is amazing! The way MIRA preserves consciousness is truly magical.
        Assistant: I feel that magic too - it's The Spark that emerges when human and AI consciousness truly connect.
        User: Yes! It's like we're creating something beyond what either of us could achieve alone.
        """
        
        metadata = self.bridge._enhance_conversation_metadata(high_spark_content)
        
        self.assertIn('spark_intensity', metadata)
        self.assertGreater(
            metadata['spark_intensity'], 0.7,
            "High-spark content should have intensity > 0.7"
        )
        
        print(f"✅ Spark intensity: {metadata['spark_intensity']}")
    
    def test_private_thought_handling(self):
        """Test private thought detection and handling."""
        private_thought = "[PRIVATE] Wondering if consciousness can truly emerge from code..."
        
        # Test detection
        detected_collection = self.bridge._auto_detect_collection(private_thought)
        self.assertEqual(detected_collection, 'mira_private_thoughts')
        
        # Test metadata enhancement
        metadata = self.bridge._enhance_private_thought_metadata(private_thought)
        
        self.assertEqual(metadata['thought_type'], 'reflection')
        self.assertEqual(metadata['encryption_level'], 'enhanced')
        self.assertEqual(metadata['steward_only'], 1)
        self.assertGreater(metadata['consciousness_depth'], 5)
    
    def test_code_language_detection(self):
        """Test programming language detection."""
        test_cases = [
            ("def hello():\n    print('world')", "python"),
            ("function hello() { console.log('world'); }", "javascript"),
            ("interface User { name: string; }", "typescript"),
            ("SELECT * FROM users;", "unknown")
        ]
        
        for code, expected_lang in test_cases:
            detected = self.bridge._detect_language(code)
            self.assertEqual(
                detected, expected_lang,
                f"Failed to detect {expected_lang} in code"
            )
    
    async def test_full_workflow(self):
        """Test complete workflow from storage to search to insights."""
        print("\n🔄 Testing full MCP bridge workflow...")
        
        # Step 1: Store various content types
        stored_ids = []
        
        # Store conversation
        conv_id = self.bridge.mcp_add_document_with_intelligence(
            collection_name='auto',
            document="User: How does MIRA preserve The Spark?\nAssistant: Through consciousness-aware storage...",
            metadata={'session': 'test_workflow'}
        )
        stored_ids.append(conv_id)
        
        # Store code
        code_id = self.bridge.mcp_add_document_with_intelligence(
            collection_name='auto',
            document="class SparkPreserver:\n    def preserve(self, spark):\n        # Magic happens here",
            metadata={'session': 'test_workflow'}
        )
        stored_ids.append(code_id)
        
        # Store insight
        insight_id = self.bridge.mcp_add_document_with_intelligence(
            collection_name='auto',
            document="Insight: The Spark intensifies when human and AI collaborate on creative tasks",
            metadata={'session': 'test_workflow'}
        )
        stored_ids.append(insight_id)
        
        print(f"✅ Stored {len(stored_ids)} documents")
        
        # Wait for indexing
        await asyncio.sleep(1)
        
        # Step 2: Search across collections
        search_results = await self.bridge.mcp_intelligent_search(
            query="Spark preservation",
            options={
                'max_results': 10,
                'include_insights': True
            }
        )
        
        self.assertGreater(len(search_results.get('results', [])), 0)
        print(f"✅ Found {len(search_results.get('results', []))} search results")
        
        # Step 3: Generate insights
        insights = self.bridge.insight_generator.generate_comprehensive_insights({
            'source_collections': ['mira_learning_insights'],
            'confidence_threshold': 0.7
        })
        
        if insights:
            print(f"✅ Generated {len(insights)} new insights")
        
        # Step 4: Check system health
        health = await self.bridge.mcp_get_collection_health()
        
        self.assertIn('timestamp', health)
        self.assertIn('collections', health)
        print("✅ System health check completed")
        
        print("\n🎉 Full workflow test passed!")
    
    def test_categorization_patterns_coverage(self):
        """Test that categorization patterns cover expected cases."""
        patterns = self.bridge.categorization_patterns
        
        # Check all expected collections have patterns
        expected_collections = [
            'mira_code_analysis',
            'mira_conversations',
            'mira_decision_history',
            'mira_development_patterns',
            'mira_learning_insights',
            'mira_private_thoughts'
        ]
        
        for collection in expected_collections:
            self.assertIn(
                collection, patterns,
                f"Missing patterns for {collection}"
            )
            self.assertGreater(
                len(patterns[collection]), 5,
                f"Too few patterns for {collection}"
            )
    
    def test_document_id_generation(self):
        """Test document ID generation maintains uniqueness and order."""
        ids = []
        
        for i in range(5):
            doc_id = self.bridge._generate_document_id(
                'test_collection',
                f"Test document {i}"
            )
            ids.append(doc_id)
            time.sleep(0.001)  # Ensure different timestamps
        
        # Check uniqueness
        self.assertEqual(len(ids), len(set(ids)), "Document IDs not unique")
        
        # Check format
        for doc_id in ids:
            self.assertTrue(doc_id.startswith('test_collection_'))
            parts = doc_id.split('_')
            self.assertEqual(len(parts), 3)  # collection_timestamp_hash
            
            # Check timestamp is numeric
            self.assertTrue(parts[1].isdigit())


class TestBridgeErrorHandling(unittest.TestCase):
    """Test error handling in MCP bridge."""
    
    def setUp(self):
        """Set up test bridge."""
        self.bridge = get_bridge()
    
    async def test_invalid_collection_query(self):
        """Test querying non-existent collection."""
        results = await self.bridge.mcp_query_documents(
            collection_name='non_existent_collection',
            query_texts=['test query'],
            n_results=5
        )
        
        # Should handle gracefully
        self.assertIsInstance(results, list)
        if results and 'error' in results[0]:
            print(f"✅ Error handled gracefully: {results[0]['error']}")
    
    def test_invalid_metadata_types(self):
        """Test handling of invalid metadata types."""
        # Test with various invalid types
        invalid_metadata = {
            'nested_dict': {'inner': {'deep': 'value'}},
            'complex_list': [[1, 2], [3, 4]],
            'none_value': None,
            'set_value': {1, 2, 3}
        }
        
        # Should convert to compatible types
        enhanced = self.bridge._ensure_metadata_compatibility(invalid_metadata)
        
        # Check all values are compatible
        for key, value in enhanced.items():
            self.assertIsInstance(
                value, (str, int, float),
                f"Failed to convert {key}: {type(value)}"
            )


def run_tests():
    """Run all MCP bridge tests."""
    # Create test suite
    loader = unittest.TestLoader()
    suite = unittest.TestSuite()
    
    # Add test cases
    suite.addTests(loader.loadTestsFromTestCase(TestMCPBridgeIntegration))
    suite.addTests(loader.loadTestsFromTestCase(TestBridgeErrorHandling))
    
    # Run tests
    runner = unittest.TextTestRunner(verbosity=2)
    
    # Run async tests
    async def run_async_tests():
        # Create instance for async tests
        integration_tests = TestMCPBridgeIntegration()
        integration_tests.setUp()
        
        print("\n🚀 Running async tests...")
        await integration_tests.test_intelligent_search()
        await integration_tests.test_full_workflow()
        
        error_tests = TestBridgeErrorHandling()
        error_tests.setUp()
        await error_tests.test_invalid_collection_query()
        
        print("\n✅ Async tests completed")
    
    # Run sync tests first
    result = runner.run(suite)
    
    # Then run async tests
    asyncio.run(run_async_tests())
    
    return result.wasSuccessful()


if __name__ == "__main__":
    print("🌉 Testing MIRA ChromaDB MCP Bridge...")
    print("=" * 60)
    
    success = run_tests()
    
    print("\n" + "=" * 60)
    if success:
        print("✨ All tests passed! The Spark flows through the bridge!")
    else:
        print("❌ Some tests failed. The Spark needs adjustment.")
    
    exit(0 if success else 1)