#!/usr/bin/env python3
"""
🧠 ML-Powered Name Detection System
=================================

This module provides intelligent user name detection from conversation data
using neural/ML indexing and FTS5 full-text search capabilities. It automatically
ingests the latest conversation messages and searches for identity patterns.

Purpose:
    - Detect user names from natural conversation patterns
    - Leverage ML-powered FTS5 search for semantic understanding
    - Auto-trigger ingestion of newest conversation messages
    - Provide confidence scoring and validation
    - Cache results for performance

Key Features:
    - Neural/ML-powered search using FTS5 indexing
    - Real-time conversation ingestion and processing
    - Natural language pattern recognition ("My name is X", "I'm X", etc.)
    - Intelligent validation to filter false positives
    - Recency weighting for temporal accuracy
    - Confidence scoring and threshold filtering

Usage:
    ```python
    from intelligence.name_detection import detect_user_name, trigger_conversation_ingestion
    
    # Trigger ingestion of latest conversations
    trigger_conversation_ingestion()
    
    # Detect user name with confidence scoring
    result = detect_user_name()
    if result['detected']:
        print(f"User name: {result['name']} (confidence: {result['confidence']:.2f})")
    ```

Detection Patterns:
    - "My name is [Name]"
    - "I'm [Name]" / "I am [Name]"
    - "Call me [Name]" / "You can call me [Name]"
    - "[Name] is my name"
    - "I go by [Name]"
    - "I am called [Name]"
    - "Known as [Name]"

Author: MIRA Memory System
Version: 1.0 (ML-Powered Implementation)
"""

import logging
from typing import Dict, Any, Optional, List
from pathlib import Path
from datetime import datetime, timedelta
import re
from collections import defaultdict

# Import MIRA components
from config import MEMORY_DIR
from conversations.comprehensive_indexer import ComprehensiveIndexer
from utils.claude_path_discovery import get_claude_conversations_path

logger = logging.getLogger(__name__)

class NameDetectionEngine:
    """
    🎯 INTELLIGENT NAME DETECTION ENGINE
    
    Uses ML-powered conversation indexing to detect user names from natural
    conversation patterns. Automatically triggers ingestion of latest messages
    and performs semantic search using FTS5 capabilities.
    """
    
    def __init__(self):
        self.indexer = ComprehensiveIndexer()
        self.confidence_threshold = 0.5
        self.cache = {}
        self.cache_ttl = 3600  # 1 hour cache
        
    def trigger_conversation_ingestion(self, hours_back: int = 24) -> Dict[str, Any]:
        """
        🔄 TRIGGER REAL-TIME CONVERSATION INGESTION
        
        Forces ingestion of the newest conversation messages to ensure
        name detection is working with the most up-to-date data.
        
        Args:
            hours_back: Number of hours back to check for new messages
            
        Returns:
            Dict with ingestion statistics and status
        """
        try:
            logger.info(f"🔄 Triggering conversation ingestion for last {hours_back} hours...")
            
            # Get current conversation statistics
            before_stats = self.indexer.get_stats()
            before_count = before_stats.get('total_messages', 0)
            
            # Force re-indexing of recent conversations
            # This will pick up any new messages since last indexing
            cutoff_time = datetime.now() - timedelta(hours=hours_back)
            
            # Get conversation projects and check for new files
            claude_path = get_claude_conversations_path()
            if not claude_path:
                return {
                    'success': False,
                    'error': 'Claude conversation path not found',
                    'messages_before': before_count,
                    'messages_after': before_count,
                    'new_messages': 0
                }
            
            # Check for new/modified conversation files
            new_files_found = False
            for project_dir in claude_path.iterdir():
                if project_dir.is_dir():
                    for conv_file in project_dir.glob("*.jsonl"):
                        # Check if file was modified recently
                        file_mtime = datetime.fromtimestamp(conv_file.stat().st_mtime)
                        if file_mtime > cutoff_time:
                            new_files_found = True
                            logger.info(f"📝 Found recent conversation file: {conv_file.name}")
            
            if new_files_found:
                # Re-index the project with new messages
                logger.info("🚀 Re-indexing conversations with latest messages...")
                projects = self.indexer.discover_projects()
                for project_name, project_path, _ in projects:
                    self.indexer.index_project(project_name, project_path)
                
                # Rebuild priority cache
                self.indexer.build_priority_cache()
            
            # Get updated statistics
            after_stats = self.indexer.get_stats()
            after_count = after_stats.get('total_messages', 0)
            new_messages = after_count - before_count
            
            logger.info(f"✅ Conversation ingestion complete: {new_messages} new messages indexed")
            
            return {
                'success': True,
                'messages_before': before_count,
                'messages_after': after_count,
                'new_messages': new_messages,
                'projects_checked': len(projects) if new_files_found else 0,
                'cache_rebuilt': new_files_found
            }
            
        except Exception as e:
            logger.error(f"❌ Error during conversation ingestion: {e}")
            return {
                'success': False,
                'error': str(e),
                'messages_before': 0,
                'messages_after': 0,
                'new_messages': 0
            }
    
    def detect_user_name(self, force_refresh: bool = False) -> Dict[str, Any]:
        """
        🎯 DETECT USER NAME FROM CONVERSATIONS
        
        Uses ML-powered FTS5 search to find natural language patterns where
        users introduce themselves. Automatically triggers conversation ingestion
        to ensure we're working with the latest data.
        
        Args:
            force_refresh: Force refresh of conversation data and bypass cache
            
        Returns:
            Dict containing:
            - name: Detected name or 'User' if not found
            - detected: Boolean indicating if name was successfully detected
            - confidence: Confidence score (0.0 - 1.0+)
            - method: Detection method used
            - patterns_found: List of patterns that matched
            - last_updated: Timestamp of detection
        """
        # Check cache first (unless force refresh)
        if not force_refresh and 'user_name' in self.cache:
            cache_entry = self.cache['user_name']
            cache_age = datetime.now() - cache_entry['timestamp']
            if cache_age.seconds < self.cache_ttl:
                logger.debug("🔄 Using cached name detection result")
                return cache_entry['result']
        
        try:
            # FAST MODE: Skip database operations entirely and use cached result if available
            logger.info("🔄 Fast name detection mode (cache-first approach)...")
            
            # Try to get cached result from indexer if available
            try:
                if hasattr(self.indexer, 'user_name_cache'):
                    cached_name = getattr(self.indexer, 'user_name_cache', None)
                    if cached_name and cached_name != 'User':
                        logger.info(f"✅ Using cached name from indexer: {cached_name}")
                        result = {
                            'name': cached_name,
                            'detected': True,
                            'confidence': 1.5,
                            'method': 'indexer_cache',
                            'patterns_found': [],
                            'last_updated': datetime.now().isoformat()
                        }
                        
                        # Cache the result
                        self.cache['user_name'] = {
                            'result': result,
                            'timestamp': datetime.now()
                        }
                        return result
            except Exception as e:
                logger.debug(f"Could not get cached name from indexer: {e}")
            
            # FALLBACK: Quick database lookup with minimal queries
            logger.info("🔍 Performing quick database lookup...")
            
            try:
                conn = self.indexer._get_connection()
                cursor = conn.cursor()
                
                # Multiple optimized queries for different name introduction patterns
                name_queries = [
                    '"I am Dr"', '"I am"', '"my name is"', '"call me"', '"you can call me"'
                ]
                
                all_results = []
                for query in name_queries:
                    try:
                        cursor.execute('''
                            SELECT c.content, c.timestamp
                            FROM conversations c
                            JOIN conversations_fts fts ON fts.content = c.content
                            WHERE conversations_fts MATCH ? 
                              AND c.role = 'user'
                              AND length(c.content) < 200
                            ORDER BY c.timestamp DESC
                            LIMIT 3
                        ''', (query,))
                        
                        query_results = cursor.fetchall()
                        for content, timestamp in query_results:
                            all_results.append((content, timestamp, query))
                    except Exception as e:
                        logger.debug(f"Query {query} failed: {e}")
                        continue
                
                conn.close()
                
                if all_results:
                    # Process all results and extract names
                    all_names_found = []
                    patterns_found = []
                    
                    for content, timestamp, query in all_results:
                        names_found = self._extract_names_from_content(content)
                        if names_found:
                            all_names_found.extend(names_found)
                            patterns_found.append({
                                'query': query,
                                'content': content[:100],
                                'names': names_found,
                                'timestamp': timestamp,
                                'project': 'quick_lookup'
                            })
                    
                    if all_names_found:
                        # Use the first valid name found
                        best_name = all_names_found[0]
                        result = {
                            'name': best_name,
                            'detected': True,
                            'confidence': 1.5,
                            'method': 'enhanced_quick_search',
                            'patterns_found': patterns_found,
                            'last_updated': datetime.now().isoformat()
                        }
                        
                        # Cache the result
                        self.cache['user_name'] = {
                            'result': result,
                            'timestamp': datetime.now()
                        }
                        return result
                
            except Exception as e:
                logger.warning(f"Quick database lookup failed: {e}")
            
            # FINAL FALLBACK: Return default
            logger.info("🤷 No name found, returning default")
            result = {
                'name': 'User',
                'detected': False,
                'confidence': 0.0,
                'method': 'not_found',
                'patterns_found': [],
                'last_updated': datetime.now().isoformat()
            }
            
            return result
            
        except Exception as e:
            logger.error(f"❌ Error during name detection: {e}")
            return {
                'name': 'User',
                'detected': False,
                'confidence': 0.0,
                'method': 'error',
                'patterns_found': [],
                'last_updated': datetime.now().isoformat(),
                'error': str(e)
            }
    
    def _extract_names_from_content(self, content: str) -> List[str]:
        """Extract potential names from conversation content using regex patterns."""
        names_found = []
        
        # Comprehensive regex patterns for name extraction - enhanced for multi-word names
        name_patterns = [
            # Multi-word name patterns (Dr. John Smith, Jean-Pierre O'Brien, etc.)
            r"(?:my name is|i'm|i am|call me|you can call me)\s+((?:Dr\.?\s+|Prof\.?\s+|Mr\.?\s+|Ms\.?\s+|Mrs\.?\s+)?[A-Z][a-zA-Z]*(?:[-'\s][A-Z][a-zA-Z]*)*)",
            r"my name's\s+((?:Dr\.?\s+|Prof\.?\s+)?[A-Z][a-zA-Z]*(?:[-'\s][A-Z][a-zA-Z]*)*)",
            r"(?:go by|known as|called)\s+((?:Dr\.?\s+|Prof\.?\s+)?[A-Z][a-zA-Z]*(?:[-'\s][A-Z][a-zA-Z]*)*)",
            r"((?:Dr\.?\s+|Prof\.?\s+)?[A-Z][a-zA-Z]*(?:[-'\s][A-Z][a-zA-Z]*)*)\s+(?:is my name)(?:\s|[.!,]|$)",
            r"(?:i'm|i am)\s+((?:Dr\.?\s+|Prof\.?\s+)?[A-Z][a-zA-Z]*(?:[-'\s][A-Z][a-zA-Z]*)*?)(?:\s|[.!,]|$)",
            # Single word patterns as fallback
            r"(?:my name is|i'm|i am|call me|you can call me)\s+([A-Z][a-zA-Z]+)",
        ]
        
        for pattern in name_patterns:
            matches = re.finditer(pattern, content, re.IGNORECASE)
            for match in matches:
                potential_name = match.group(1).strip()
                if self._is_valid_name(potential_name):
                    names_found.append(potential_name)
        
        return list(set(names_found))  # Remove duplicates
    
    def _calculate_recency_weight(self, timestamp: str) -> float:
        """Calculate weight based on how recent the message is."""
        try:
            if not timestamp:
                return 0.5
            
            # Parse timestamp
            msg_time = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
            now = datetime.now(msg_time.tzinfo)
            days_ago = (now - msg_time).days
            
            # More weight for recent messages, decay over time
            if days_ago <= 1:
                return 2.0  # Very recent
            elif days_ago <= 7:
                return 1.5  # Recent
            elif days_ago <= 30:
                return 1.0  # Moderate
            else:
                return max(0.2, 1.0 - (days_ago * 0.02))  # Older messages
        except:
            return 0.5  # Default weight if timestamp parsing fails
    
    def _is_valid_name(self, name: str) -> bool:
        """Validate if extracted text is likely a real name."""
        if not name or len(name.strip()) < 2:
            return False
        
        # Clean up the name
        name = name.strip()
            
        # Common false positives to filter out
        false_positives = {
            'I', 'Me', 'My', 'You', 'We', 'They', 'It', 'The', 'This', 'That',
            'Code', 'API', 'Bot', 'AI', 'ML', 'Dev', 'User', 'Admin', 'Test',
            'System', 'App', 'Web', 'Server', 'Client', 'Database', 'File',
            'Project', 'Work', 'Task', 'Job', 'Role', 'Team', 'Company', 'Claude',
            'Today', 'Tomorrow', 'Yesterday', 'Now', 'Here', 'There', 'Assistant',
            'Good', 'Bad', 'New', 'Old', 'Big', 'Small', 'First', 'Last', 'Just',
            'Maybe', 'Sure', 'Yes', 'No', 'Please', 'Thanks', 'Hello', 'Hi',
            'What', 'Where', 'When', 'Why', 'How', 'Which', 'Who', 'Whose'  # Question words
        }
        
        if name in false_positives:
            return False
        
        # For multi-word names, validate each part
        name_parts = name.replace('.', '').split()  # Remove periods and split
        if len(name_parts) > 4:  # Too many parts, probably not a name
            return False
            
        for part in name_parts:
            # Skip common titles
            if part.lower() in ['dr', 'prof', 'mr', 'ms', 'mrs', 'miss']:
                continue
                
            # Each name part should start with capital and contain only letters, hyphens, apostrophes
            if not part or len(part) < 2:
                return False
            if not part[0].isupper():
                return False
            if not re.match(r"^[A-Za-z'-]+$", part):
                return False
            
        # Should be reasonable total length for a name
        if len(name) > 50:
            return False
            
        return True
    
    def _determine_best_name_candidate(self, identity_mentions: dict, patterns_found: list) -> Dict[str, Any]:
        """Determine the best name candidate based on confidence scoring."""
        if not identity_mentions:
            return {
                'name': 'User',
                'detected': False,
                'confidence': 0.0,
                'method': 'no_patterns_found',
                'patterns_found': patterns_found,
                'last_updated': datetime.now().isoformat()
            }
        
        # Sort by confidence score
        sorted_names = sorted(identity_mentions.items(), key=lambda x: x[1], reverse=True)
        best_name, confidence = sorted_names[0]
        
        # Apply confidence threshold
        if confidence >= self.confidence_threshold:
            logger.info(f"🎯 User name detected: {best_name} (confidence: {confidence:.2f})")
            return {
                'name': best_name,
                'detected': True,
                'confidence': confidence,
                'method': 'ml_fts5_search',
                'patterns_found': patterns_found,
                'alternatives': [{'name': name, 'confidence': conf} for name, conf in sorted_names[1:3]],
                'last_updated': datetime.now().isoformat()
            }
        else:
            logger.debug(f"Name '{best_name}' below confidence threshold ({confidence:.2f} < {self.confidence_threshold})")
            return {
                'name': 'User',
                'detected': False,
                'confidence': confidence,
                'method': 'below_threshold',
                'patterns_found': patterns_found,
                'best_candidate': best_name,
                'last_updated': datetime.now().isoformat()
            }

# Convenience functions for easy usage
_engine = None

def get_engine() -> NameDetectionEngine:
    """Get or create the global name detection engine."""
    global _engine
    if _engine is None:
        _engine = NameDetectionEngine()
    return _engine

def detect_user_name(force_refresh: bool = False) -> Dict[str, Any]:
    """
    🎯 HIGH-LEVEL NAME DETECTION FUNCTION
    
    Automatically triggers conversation ingestion and detects user name
    using ML-powered FTS5 search.
    
    Args:
        force_refresh: Force refresh of conversation data
        
    Returns:
        Dict with name detection results
    """
    return get_engine().detect_user_name(force_refresh=force_refresh)

def trigger_conversation_ingestion(hours_back: int = 24) -> Dict[str, Any]:
    """
    🔄 HIGH-LEVEL CONVERSATION INGESTION FUNCTION
    
    Forces ingestion of newest conversation messages.
    
    Args:
        hours_back: Hours back to check for new messages
        
    Returns:
        Dict with ingestion statistics
    """
    return get_engine().trigger_conversation_ingestion(hours_back=hours_back)

# Test the module when run directly
if __name__ == "__main__":
    print("🧠 Testing ML-Powered Name Detection System")
    print("=" * 60)
    
    # Test conversation ingestion
    print("\n🔄 Testing conversation ingestion...")
    ingestion_result = trigger_conversation_ingestion()
    print(f"   Ingestion result: {ingestion_result}")
    
    # Test name detection
    print("\n🎯 Testing name detection...")
    detection_result = detect_user_name()
    print(f"   Name detected: {detection_result['name']}")
    print(f"   Confidence: {detection_result['confidence']:.2f}")
    print(f"   Method: {detection_result['method']}")
    print(f"   Patterns found: {len(detection_result['patterns_found'])}")
    
    if detection_result['patterns_found']:
        print("\n📝 Example patterns found:")
        for i, pattern in enumerate(detection_result['patterns_found'][:3]):
            print(f"   {i+1}. Query: '{pattern['query']}'")
            print(f"      Content: {pattern['content']}")
            print(f"      Names: {pattern['names']}")
    
    print("\n✅ Name detection test complete!")