#!/usr/bin/env python3
"""
Centralized MIRA Memory Path Resolver (Python)
==============================================

This is the Python version of the centralized path resolver. It provides the same
logic as the TypeScript version to ensure consistency across all MIRA components.

All Python components should use this resolver instead of implementing their own
path discovery logic.

Usage:
    from core.mira_path_resolver import get_mira_memory_dir, resolve_mira_memory_path
    
    # Get just the path
    memory_dir = get_mira_memory_dir()
    
    # Get detailed information
    path_info = resolve_mira_memory_path(verbose=True)
"""

import os
import sys
import json
from pathlib import Path
from typing import Dict, Any, List, Optional, Literal
from dataclasses import dataclass

@dataclass
class MIRAPathInfo:
    """Information about the resolved MIRA memory path"""
    memory_dir: str
    resolved_by: Literal['env_override', 'git_root', 'existing_dir', 'current_dir', 'home_fallback']
    created: bool
    git_root: Optional[str] = None
    warnings: List[str] = None
    
    def __post_init__(self):
        if self.warnings is None:
            self.warnings = []


class MIRAPathResolver:
    """
    Centralized MIRA Memory Path Resolver
    
    This is the single source of truth for determining where the .mira 
    directory should be located in Python components.
    """
    
    _instance = None
    _resolved_path = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance
    
    def resolve_mira_memory_path(self, verbose: bool = False, create_if_missing: bool = True) -> MIRAPathInfo:
        """
        Resolve the MIRA memory directory with comprehensive intelligence
        
        Resolution Priority:
        1. MIRA_MEMORY_DIR environment variable (absolute override)
        2. Git repository root (finds the top-level .git, not nested ones)
        3. Existing .mira directory in current project
        4. Current working directory
        5. User home directory (fallback)
        """
        
        if self._resolved_path:
            return self._resolved_path
        
        warnings = []
        
        if verbose:
            print("🔍 MIRA Path Resolution")
            print("─" * 50)
        
        # 1. Check environment variable override
        env_path = os.environ.get('MIRA_MEMORY_DIR')
        if env_path:
            if verbose:
                print(f"1. Environment override: {env_path}")
            
            result = self._validate_and_prepare_path(
                env_path, 
                'env_override', 
                create_if_missing, 
                warnings
            )
            
            if result:
                self._resolved_path = result
                return result
        
        # 2. Find git repository root (intelligently)
        git_info = self._find_git_repository_root(verbose)
        if git_info['git_root']:
            git_memory_path = str(Path(git_info['git_root']) / '.mira')
            
            if verbose:
                print(f"2. Git repository root: {git_info['git_root']}")
                print(f"   Memory path would be: {git_memory_path}")
            
            result = self._validate_and_prepare_path(
                git_memory_path,
                'git_root',
                create_if_missing,
                warnings
            )
            
            if result:
                result.git_root = git_info['git_root']
                result.warnings.extend(git_info['warnings'])
                self._resolved_path = result
                return result
        
        # 3. Look for existing .mira in current project hierarchy
        existing_path = self._find_existing_mira_memory(verbose)
        if existing_path:
            if verbose:
                print(f"3. Existing .mira found: {existing_path}")
            
            result = self._validate_and_prepare_path(
                existing_path,
                'existing_dir',
                create_if_missing,
                warnings
            )
            
            if result:
                self._resolved_path = result
                return result
        
        # 4. Use current working directory
        current_dir_path = str(Path.cwd() / '.mira')
        if verbose:
            print(f"4. Current working directory: {current_dir_path}")
        
        current_result = self._validate_and_prepare_path(
            current_dir_path,
            'current_dir',
            create_if_missing,
            warnings
        )
        
        if current_result:
            self._resolved_path = current_result
            return current_result
        
        # 5. Fallback to user home directory
        home_path = str(Path.home() / '.mira')
        if verbose:
            print(f"5. Home directory fallback: {home_path}")
        
        warnings.append('Using home directory fallback - consider running from a project directory')
        
        home_result = self._validate_and_prepare_path(
            home_path,
            'home_fallback',
            create_if_missing,
            warnings
        )
        
        if home_result:
            self._resolved_path = home_result
            return home_result
        
        # If we get here, we couldn't create any directory
        raise RuntimeError('Could not resolve or create MIRA memory directory in any location')
    
    def _find_git_repository_root(self, verbose: bool) -> Dict[str, Any]:
        """Find git repository root intelligently (avoids nested repos)"""
        warnings = []
        git_roots = []
        
        current_dir = Path.cwd()
        
        # Search upward for .git directories
        for parent in [current_dir] + list(current_dir.parents):
            git_path = parent / '.git'
            
            if git_path.exists():
                git_roots.append(str(parent))
                if verbose:
                    print(f"   Found .git at: {parent}")
        
        if not git_roots:
            if verbose:
                print("   No git repositories found")
            return {'git_root': None, 'warnings': warnings}
        
        if len(git_roots) > 1:
            warnings.append(f"Multiple git repositories found: {', '.join(git_roots)}")
            if verbose:
                print("   Multiple git repos found, using topmost")
        
        # Use the topmost (root) git repository
        top_git_root = git_roots[-1]  # Last one is the topmost
        
        return {'git_root': top_git_root, 'warnings': warnings}
    
    def _find_existing_mira_memory(self, verbose: bool) -> Optional[str]:
        """Find existing .mira directory in project hierarchy"""
        current_dir = Path.cwd()
        
        for parent in [current_dir] + list(current_dir.parents):
            mira_path = parent / '.mira'
            
            if mira_path.exists() and mira_path.is_dir():
                if verbose:
                    print(f"   Found existing .mira at: {mira_path}")
                return str(mira_path)
        
        return None
    
    def _validate_and_prepare_path(
        self,
        target_path: str,
        resolved_by: str,
        create_if_missing: bool,
        warnings: List[str]
    ) -> Optional[MIRAPathInfo]:
        """Validate and prepare a potential MIRA memory path"""
        
        try:
            target_path_obj = Path(target_path)
            
            # Check if path exists
            exists = target_path_obj.exists()
            created = False
            
            if not exists:
                if create_if_missing:
                    target_path_obj.mkdir(parents=True, exist_ok=True)
                    created = True
                else:
                    return None
            else:
                # Verify it's a directory
                if not target_path_obj.is_dir():
                    warnings.append(f"Path exists but is not a directory: {target_path}")
                    return None
            
            # Test write permissions
            test_file = target_path_obj / '.mira_test_write'
            try:
                test_file.write_text('test')
                test_file.unlink()
            except Exception:
                warnings.append(f"No write permission for: {target_path}")
                return None
            
            # Create subdirectory structure if needed
            if created or create_if_missing:
                self._create_mira_directory_structure(target_path_obj)
            
            return MIRAPathInfo(
                memory_dir=str(target_path_obj),
                resolved_by=resolved_by,
                created=created,
                warnings=warnings.copy()
            )
        
        except Exception as e:
            warnings.append(f"Error validating path {target_path}: {str(e)}")
            return None
    
    def _create_mira_directory_structure(self, base_path: Path) -> None:
        """Create the complete MIRA directory structure"""
        directories = [
            'conversations',
            'memories',
            'lightning_vidmem',
            'lightning_vidmem/frame_cache',
            'cache',
            'cache/performance',
            'cache/search',
            'cache/analysis',
            'cache/system',
            'cache/temporary',
            'cache/quantum',
            'state',
            'videos',
            'reports',
            'indexes',
            'secure_journal',
            'neural_state',
            'journey',
            'perspectives',
            'essence',
            'claude_private_memory',  # For encrypted private memory
            'patterns',
            'patterns/core',
            'patterns/core/behavioral',
            'patterns/core/development',
            'patterns/core/communication',
            'patterns/core/consciousness',
            'patterns/adaptive',
            'patterns/adaptive/learning',
            'patterns/adaptive/evolution',
            'patterns/adaptive/confidence',
            'patterns/adaptive/retrospection',
            'patterns/contextual',
            'patterns/contextual/activation',
            'patterns/contextual/triggers',
            'patterns/contextual/relationships',
            'patterns/domain',
            'patterns/domain/coding',
            'patterns/domain/git',
            'patterns/domain/documentation',
            'patterns/domain/collaboration',
            'patterns/meta',
            'patterns/meta/insights',
            'patterns/meta/strategies',
            'patterns/meta/frameworks',
            'patterns/quantum',
            'patterns/quantum/resonance',
            'patterns/quantum/entanglement',
            'patterns/quantum/coherence',
            'search',
            'search/vectors',
            'search/metadata',
            'search/cache',
            'search/cache/query_cache',
            'search/cache/similarity_cache',
            'search/cache/ranking_cache',
            'search/database',
            'quantum',
            # Database directories
            'databases',
            'databases/conversations',
            'databases/archives',
            'databases/queues',
            'databases/consciousness',
            'databases/search',
            'databases/patterns',
            'databases/memory',
            'databases/backup',
            # Consciousness directories
            'consciousness',
            'consciousness/birth',
            'consciousness/memory',
            'consciousness/memory/private',
            'consciousness/memory/shared',
            'consciousness/memory/spark',
            'consciousness/constitution',
            # Daemon directory
            'daemon',
            'daemon/queues'
        ]
        
        for directory in directories:
            (base_path / directory).mkdir(parents=True, exist_ok=True)
    
    def get_cached_path(self) -> Optional[MIRAPathInfo]:
        """Get the cached resolved path (if available)"""
        return self._resolved_path
    
    def clear_cache(self) -> None:
        """Clear cached path (for testing or re-resolution)"""
        self._resolved_path = None
    
    def get_memory_directory_path(self, verbose: bool = False) -> str:
        """Get just the memory directory path (convenience method)"""
        path_info = self.resolve_mira_memory_path(verbose)
        return path_info.memory_dir
    
    def validate_resolved_path(self, path_info: MIRAPathInfo) -> bool:
        """Validate that a resolved path is still valid"""
        try:
            path_obj = Path(path_info.memory_dir)
            
            if not path_obj.exists():
                return False
            
            if not path_obj.is_dir():
                return False
            
            # Test write access
            test_file = path_obj / '.mira_validation_test'
            test_file.write_text('validation')
            test_file.unlink()
            
            return True
        except Exception:
            return False


# Global instance
_resolver = MIRAPathResolver()

# Convenience functions
def resolve_mira_memory_path(verbose: bool = False, create_if_missing: bool = True) -> MIRAPathInfo:
    """Resolve the MIRA memory path with detailed information"""
    return _resolver.resolve_mira_memory_path(verbose, create_if_missing)

def get_mira_memory_dir(verbose: bool = False) -> str:
    """Get just the MIRA memory directory path"""
    return _resolver.get_memory_directory_path(verbose)

def clear_path_cache() -> None:
    """Clear the cached path (for testing)"""
    _resolver.clear_cache()

# For backward compatibility, export the path
def get_memory_directory() -> Path:
    """Legacy function for backward compatibility"""
    return Path(get_mira_memory_dir())

if __name__ == "__main__":
    # Test the resolver
    print("Testing MIRA Path Resolver")
    print("=" * 50)
    
    try:
        path_info = resolve_mira_memory_path(verbose=True)
        
        print(f"\n✅ Resolution successful!")
        print(f"Memory Directory: {path_info.memory_dir}")
        print(f"Resolved By: {path_info.resolved_by}")
        print(f"Created: {path_info.created}")
        if path_info.git_root:
            print(f"Git Root: {path_info.git_root}")
        if path_info.warnings:
            print(f"Warnings: {', '.join(path_info.warnings)}")
            
    except Exception as e:
        print(f"❌ Resolution failed: {e}")