#!/usr/bin/env python3
"""
Semantic Journey Search - Memvid Integration for Development History
===================================================================

This system allows natural language searching through our complete collaborative
development journey. Find past decisions, conversations, and moments using
semantic search over our shared memory.
"""

import os
import sys
import json
from datetime import datetime
from pathlib import Path
from typing import List, Dict, Any, Tuple

# Import centralized config
sys.path.insert(0, str(Path(__file__).parent.parent))
from config import MEMORY_DIR

# Add memvid to path
memvid_path = Path(__file__).parent.parent / "memvid"
sys.path.insert(0, str(memvid_path))

try:
    from memvid import MemvidEncoder, MemvidRetriever, MemvidChat
except ImportError as e:
    print(f"Memvid not available: {e}")
    print("Install with: pip install memvid")
    sys.exit(1)

from interfaces.interface import MemoryInterface


class DevelopmentJourneyMemvid:
    """
    Encodes our entire development journey into searchable video memory
    """
    
    def __init__(self):
        self.base_path = Path(str(MEMORY_DIR))
        self.journey_video = self.base_path / "development_journey.mp4"
        self.journey_index = self.base_path / "journey_index.json"
        self.memory_interface = MemoryInterface()
        self.memory_interface.initialize()
        
        # Initialize memvid encoder with appropriate settings
        self.encoder = MemvidEncoder()
    
    def build_journey_memory(self, batch_size=25, max_batches=4):
        """Build journey memory in batches to prevent timeouts"""
        print("🎥 Building semantic video memory in batches...")
        
        # Initialize or append to existing encoder
        batch_encoder = MemvidEncoder()
        total_processed = 0
        
        try:
            # Process memories in batches
            for batch_num in range(max_batches):
                print(f"\n📦 Processing batch {batch_num + 1}/{max_batches}...")
                
                # Load a batch of recent memories
                memories = self._load_memory_batch(batch_num, batch_size)
                
                if not memories:
                    print(f"   No more memories in batch {batch_num + 1}")
                    break
                
                print(f"   📚 Found {len(memories)} memories in this batch")
                
                # Convert batch to chunks efficiently  
                batch_chunks = []
                for memory in memories:
                    # Create one focused chunk per memory to limit size
                    chunk = self._memory_to_single_chunk(memory)
                    if chunk:
                        batch_chunks.append(chunk)
                
                print(f"   📝 Generated {len(batch_chunks)} chunks from batch")
                
                # Add batch chunks to encoder
                for chunk_data in batch_chunks:
                    text_with_metadata = f"[{chunk_data['type']}] {chunk_data['text'][:300]}"  # Limit text size
                    batch_encoder.add_text(text_with_metadata)
                
                total_processed += len(memories)
                print(f"   ✅ Batch {batch_num + 1} processed ({len(memories)} memories)")
                
                # Quick validation - if this batch was small, we're probably done
                if len(memories) < batch_size // 2:
                    print(f"   🏁 Small batch detected, processing complete")
                    break
            
            # Add a small sample of recent git history
            print(f"\n📚 Adding recent development context...")
            try:
                git_chunks = self._get_recent_git_chunks(max_commits=10)
                for chunk in git_chunks:
                    batch_encoder.add_text(f"[git] {chunk['text'][:200]}")
                print(f"   📝 Added {len(git_chunks)} git context chunks")
            except Exception as e:
                print(f"   ⚠️ Could not load git history: {e}")
            
            # Build the optimized video file
            print(f"\n🎬 Building final video from {total_processed} memories...")
            batch_encoder.build_video(
                str(self.journey_video),
                str(self.journey_index)
            )
            
            print(f"\n✅ Journey memory built successfully!")
            print(f"   📊 Total memories processed: {total_processed}")
            print(f"   📹 Video file: {self.journey_video}")
            print(f"   📄 Search index: {self.journey_index}")
            
        except Exception as e:
            print(f"❌ Error in batched journey build: {e}")
            # Don't re-raise to avoid breaking the interface
            return False
        
        return True
    
    def search_our_journey(self, query: str, max_results: int = 5) -> List[Dict]:
        """Search our development journey using natural language"""
        
        if not self.journey_video.exists():
            print("❌ Journey memory not built yet. Run: python semantic_journey_search.py --build")
            return []
        
        try:
            # Initialize retriever
            retriever = MemvidRetriever(
                str(self.journey_video),
                str(self.journey_index)
            )
            
            # Perform semantic search
            results = retriever.search(query, top_k=max_results)
            
            # Format results for display
            formatted_results = []
            # Handle different possible return formats from memvid
            for result in results:
                if isinstance(result, tuple) and len(result) == 2:
                    chunk_text, score = result
                elif isinstance(result, dict):
                    chunk_text = result.get('text', str(result))
                    score = result.get('score', 0.0)
                else:
                    chunk_text = str(result)
                    score = 0.0
                # Try to extract metadata if available
                result = {
                    'content': chunk_text,
                    'relevance_score': score,
                    'timestamp': 'Unknown'
                }
                
                # Try to extract timestamp from content
                if 'timestamp:' in chunk_text.lower():
                    try:
                        timestamp_line = [line for line in chunk_text.split('\n') if 'timestamp:' in line.lower()][0]
                        timestamp = timestamp_line.split(':', 1)[1].strip()
                        result['timestamp'] = timestamp
                    except:
                        pass
                
                formatted_results.append(result)
            
            return formatted_results
            
        except Exception as e:
            print(f"❌ Error searching journey: {e}")
            return []
    
    def _load_memory_batch(self, batch_num: int, batch_size: int) -> List[Dict]:
        """Load a specific batch of memories efficiently"""
        try:
            # Calculate offset for this batch
            offset = batch_num * batch_size
            
            # Get memories for this batch with limit
            memory_results = self.memory_interface.recall("", top_k=batch_size)
            
            # Skip to the right batch (simple pagination)
            start_idx = offset
            end_idx = min(start_idx + batch_size, len(memory_results))
            batch_results = memory_results[start_idx:end_idx]
            
            # Convert Memory objects to dictionaries
            memories = []
            for memory_obj, score in batch_results:
                memory_dict = {
                    'content': getattr(memory_obj, 'content', str(memory_obj))[:1000],  # Limit content size
                    'timestamp': getattr(memory_obj, 'timestamp', ''),
                    'type': getattr(memory_obj, 'type', 'memory'),
                    'id': getattr(memory_obj, 'id', 'unknown'),
                    'score': score
                }
                memories.append(memory_dict)
            
            return memories
        except Exception as e:
            print(f"Could not load memory batch {batch_num}: {e}")
            return []
    
    def _memory_to_single_chunk(self, memory: Dict) -> Dict:
        """Convert a memory to a single optimized chunk"""
        try:
            memory_type = memory.get('type', 'unknown')
            content = memory.get('content', '')[:500]  # Limit content size
            timestamp = memory.get('timestamp', 'unknown')
            
            # Create concise, searchable text
            chunk_text = f"[{timestamp}] {content}"
            
            return {
                'text': chunk_text,
                'type': memory_type,
                'timestamp': timestamp
            }
        except Exception as e:
            print(f"Could not convert memory to chunk: {e}")
            return None
    
    def _get_recent_git_chunks(self, max_commits: int = 10) -> List[Dict]:
        """Get recent git commit history as searchable chunks"""
        chunks = []
        try:
            import subprocess
            
            # Get recent commits
            result = subprocess.run([
                'git', 'log', f'--max-count={max_commits}', 
                '--pretty=format:%h|%ad|%s', '--date=short'
            ], capture_output=True, text=True, cwd=self.base_path.parent)
            
            if result.returncode == 0:
                for line in result.stdout.strip().split('\n'):
                    if '|' in line:
                        parts = line.split('|', 2)
                        if len(parts) >= 3:
                            commit_hash, date, message = parts
                            chunks.append({
                                'text': f"[{date}] {message} ({commit_hash})",
                                'type': 'git_commit',
                                'timestamp': date
                            })
            
        except Exception as e:
            print(f"Could not load git history: {e}")
        
        return chunks
    
    def _load_all_memories(self) -> List[Dict]:
        """Load all memories from the memory interface"""
        try:
            # Get all memories by doing a broad search
            memory_results = self.memory_interface.recall("", top_k=1000)
            
            # Convert Memory objects to dictionaries
            memories = []
            for memory_obj, score in memory_results:
                memory_dict = {
                    'content': getattr(memory_obj, 'content', str(memory_obj)),
                    'timestamp': getattr(memory_obj, 'timestamp', ''),
                    'type': getattr(memory_obj, 'type', 'memory'),
                    'id': getattr(memory_obj, 'id', 'unknown')
                }
                memories.append(memory_dict)
            
            return memories
        except Exception as e:
            print(f"Could not load memories: {e}")
            return []
    
    def _memory_to_searchable_text(self, memory: Dict) -> List[Dict]:
        """Convert a memory entry to searchable text chunks"""
        chunks = []
        
        # Basic memory info
        memory_type = memory.get('type', 'unknown')
        timestamp = memory.get('timestamp', 'unknown')
        content = memory.get('content', '')
        
        # Main memory chunk
        main_text = f"""
Memory Type: {memory_type}
Timestamp: {timestamp}
Content: {content}
"""
        
        chunks.append({
            'text': main_text,
            'metadata': {
                'type': 'memory',
                'memory_type': memory_type,
                'timestamp': timestamp,
                'source': 'claude_memory'
            }
        })
        
        # Enhanced development session specifics
        if memory_type == 'enhanced_development_session':
            self._add_enhanced_session_chunks(memory, chunks)
        
        # Interaction specifics  
        elif memory_type == 'interaction':
            self._add_interaction_chunks(memory, chunks)
        
        return chunks
    
    def _add_enhanced_session_chunks(self, memory: Dict, chunks: List[Dict]):
        """Add enhanced development session specific chunks"""
        
        # Commit data
        commit_data = memory.get('commit_data', {})
        if commit_data:
            commit_text = f"""
Development Session - Commit Analysis
Timestamp: {memory.get('timestamp', 'unknown')}
Commit: {commit_data.get('message', 'Unknown')}
Files Changed: {', '.join(commit_data.get('files', []))}
Hash: {commit_data.get('hash', 'Unknown')}
"""
            chunks.append({
                'text': commit_text,
                'metadata': {
                    'type': 'commit_analysis',
                    'commit_hash': commit_data.get('hash', ''),
                    'timestamp': memory.get('timestamp', ''),
                    'source': 'enhanced_session'
                }
            })
        
        # Multi-perspective analyses
        perspectives = memory.get('multi_perspective_analysis', {})
        for perspective_key, analysis in perspectives.items():
            perspective_text = f"""
Multi-Perspective Analysis - {analysis.get('perspective', 'Unknown')}
Timestamp: {memory.get('timestamp', 'unknown')}
Focus: {analysis.get('focus_area', '')}
Primary Observation: {analysis.get('insights', {}).get('primary_observation', '')}
Concerns: {', '.join(analysis.get('insights', {}).get('concerns_identified', []))}
Recommendations: {', '.join(analysis.get('insights', {}).get('recommendations', []))}
"""
            chunks.append({
                'text': perspective_text,
                'metadata': {
                    'type': 'perspective_analysis',
                    'perspective': perspective_key,
                    'timestamp': memory.get('timestamp', ''),
                    'source': 'multi_perspective'
                }
            })
        
        # Personal reflection
        personal = memory.get('personal_reflection', {})
        if personal:
            personal_text = f"""
Personal Reflection - Claude's Thoughts
Timestamp: {memory.get('timestamp', 'unknown')}
Emotional Response: {personal.get('emotional_response', '')}
Learning Moment: {json.dumps(personal.get('learning_moment', {}), indent=2)}
Partnership Growth: {json.dumps(personal.get('partnership_growth', {}), indent=2)}
Personal Pride: {personal.get('personal_pride', '')}
Future Excitement: {personal.get('future_excitement', '')}
"""
            chunks.append({
                'text': personal_text,
                'metadata': {
                    'type': 'personal_reflection',
                    'timestamp': memory.get('timestamp', ''),
                    'source': 'claude_personal'
                }
            })
    
    def _add_interaction_chunks(self, memory: Dict, chunks: List[Dict]):
        """Add interaction-specific chunks"""
        content = memory.get('content', '')
        emotional_context = memory.get('emotional_context', '')
        significance = memory.get('significance', '')
        
        interaction_text = f"""
Interaction Memory
Timestamp: {memory.get('timestamp', 'unknown')}
Content: {content}
Emotional Context: {emotional_context}
Significance: {significance}
"""
        chunks.append({
            'text': interaction_text,
            'metadata': {
                'type': 'interaction',
                'significance': significance,
                'timestamp': memory.get('timestamp', ''),
                'source': 'interaction_memory'
            }
        })
    
    def _get_git_history_chunks(self) -> List[Dict]:
        """Get git commit history as searchable chunks"""
        chunks = []
        
        try:
            import subprocess
            
            # Get recent git history
            result = subprocess.run([
                'git', 'log', '--oneline', '--max-count=50', '--pretty=format:%H|%ai|%s'
            ], capture_output=True, text=True, cwd='/workspaces/Sectorwars2102')
            
            if result.returncode == 0:
                for line in result.stdout.strip().split('\n'):
                    if '|' in line:
                        hash_part, date_part, message = line.split('|', 2)
                        
                        git_text = f"""
Git Commit History
Hash: {hash_part}
Date: {date_part}
Message: {message}
"""
                        chunks.append({
                            'text': git_text,
                            'metadata': {
                                'type': 'git_commit',
                                'hash': hash_part,
                                'date': date_part,
                                'source': 'git_history'
                            }
                        })
        
        except Exception as e:
            print(f"Could not load git history: {e}")
        
        return chunks


class JourneySearchInterface:
    """User-friendly interface for searching our development journey"""
    
    def __init__(self):
        self.memvid_system = DevelopmentJourneyMemvid()
    
    def search(self, query: str):
        """Search and display results"""
        print(f"🔍 Searching our development journey for: '{query}'")
        print("=" * 60)
        
        results = self.memvid_system.search_our_journey(query)
        
        if not results:
            print("No results found. Try a different query or rebuild the journey memory.")
            return
        
        for i, result in enumerate(results, 1):
            print(f"\n📋 Result {i} (Relevance: {result['relevance_score']:.3f})")
            print(f"🕐 Timestamp: {result['timestamp']}")
            print("📝 Content:")
            print(result['content'])
            print("-" * 40)
    
    def interactive_search(self):
        """Interactive search session"""
        print("🎥 Semantic Journey Search - Interactive Mode")
        print("Search our complete development partnership using natural language!")
        print("Examples:")
        print("  'when we talked about friendship'")
        print("  'decision about WebSocket authentication'") 
        print("  'Claude's feelings about memory system'")
        print("  'architectural decisions for trading system'")
        print("\nType 'quit' to exit")
        print("=" * 60)
        
        while True:
            try:
                query = input("\n🔍 Search query: ").strip()
                
                if query.lower() in ['quit', 'exit', 'q']:
                    break
                
                if not query:
                    continue
                
                self.search(query)
                
            except KeyboardInterrupt:
                break
        
        print("\n👋 Thanks for exploring our journey together!")


def main():
    """Main interface"""
    
    if len(sys.argv) < 2:
        print("🎥 Semantic Journey Search")
        print("Usage:")
        print("  python semantic_journey_search.py --build              # Build journey memory")
        print("  python semantic_journey_search.py --search 'query'     # Search our journey")
        print("  python semantic_journey_search.py --interactive        # Interactive search")
        return
    
    command = sys.argv[1]
    
    if command == '--build':
        memvid_system = DevelopmentJourneyMemvid()
        memvid_system.build_journey_memory()
    
    elif command == '--search':
        if len(sys.argv) < 3:
            print("Please provide a search query")
            return
        
        query = ' '.join(sys.argv[2:])
        interface = JourneySearchInterface()
        interface.search(query)
    
    elif command == '--interactive':
        interface = JourneySearchInterface()
        interface.interactive_search()
    
    else:
        print(f"Unknown command: {command}")


if __name__ == "__main__":
    main()