import importlib.util
from pathlib import Path
import sys
import logging
from typing import Dict, List, Optional


class SelfModelUpdater:
    """
    Maintains an accurate model of Eden's own capabilities through dynamic analysis.
    
    Attributes:
        capability_dir: Path to the directory containing capabilities
        models: Dictionary storing modeled knowledge about Eden
        
    Methods:
        __init__: Initializes the updater with base models and capability directory
        update_capability_list: Updates list of available capabilities
        analyze_capability_code: Analyzes a single capability file for properties
        enrich_model_with_capability: Adds new capability to model
        display_current_model: Prints current modeled knowledge
        save_models_to_disk: Saves models to persistent storage
    """
    
    def __init__(self, capability_dir: Path = None):
        """
        Initializes self-modeling system with base models and capability directory.
        
        Args:
            capability_dir: Directory containing capability files (optional)
        """
        # Set up logging
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
            filename='self_model_update.log',
            filemode='w'
        )
        self.logger = logging.getLogger(__name__)
        
        # Initialize base models (dicts containing knowledge about different aspects of the system)
        self.models = {
            'capabilities': {
                'total_count': 0,
                'by_category': {},  # e.g., 'natural_language': 3, 'perception': 5
                'list': []  # Actual list of capability filenames
            },
            'architecture': {
                'directories': {
                    'eden_core': str(Path(__source__).parent),
                    'capabilities_dir': str(capability_dir) if capability_dir else ''
                },
                'file_count_by_type': {}
            },
            'processes': {
                'running_status': False,
                'last_update_timestamp': None
            }
        }
        
        # Set capability directory (defaults to standard location)
        self.capability_dir = capability_dir or Path('/Eden/CORE/eden_capabilities')
        
        # Initialize by updating the capability list
        self.update_capability_list()
    
    def update_capability_list(self) -> None:
        """
        Updates the list of available capabilities in models['capabilities']['list'].
        
        This method scans the capability directory and rebuilds the full list of capabilities,
        then updates all related model components with this new information.
        """
        try:
            # Clear existing list
            self.models['capabilities']['list'].clear()
            
            # Re-scan directory
            for capability_file in Path(self.capability_dir).glob('*.py'):
                if capability_file.is_file():
                    self.models['capabilities']['list'].append(capability_file.name)
                    
                    # Update counters
                    category = getattr(
                        importlib.util.module_from_file(capability_file),
                        '__category__',
                        'uncategorized'
                    )
                    self.models['capabilities']['by_category'][category] = (
                        self.models['capabilities']['by_category'].get(category, 0) + 1
                    )
            
            # Update total count
            self.models['capabilities']['total_count'] = len(self.models['capabilities']['list'])
            
            self.logger.info("Capability list updated successfully")
            
        except Exception as e:
            self.logger.error(f"Failed to update capability list: {e}")
            raise
    
    def analyze_capability_code(self, filename: str) -> Dict[str, Optional[str]]:
        """
        Analyzes a single capability file for its properties and returns structured data.
        
        Args:
            filename: Name of the capability file
            
        Returns:
            Dictionary containing analyzed properties
        """
        try:
            file_path = self.capability_dir / filename
            if not file_path.exists():
                return {'error': f'File {filename} does not exist'}
            
            module = importlib.util.module_from_file(file_path)
            
            return {
                'name': filename,
                'category': getattr(module, '__category__', 'uncategorized'),
                'description': getattr(module, '__description__', None),
                'author': getattr(module, '__author__', None),
                'version': getattr(module, '__version__', 'unknown'),
                'dependencies': getattr(module, '__depends_on__', []),
                'confidence_level': self._assess_confidence(filename, module)
            }
            
        except Exception as e:
            return {'error': str(e)}
    
    def _assess_confidence(self, filename: str, module) -> float:
        """
        Assesses confidence level in the accuracy of extracted properties.
        
        Args:
            filename: Capability file name
            module: Imported module
            
        Returns:
            Confidence score between 0.0 and 1.0
        """
        has_category = hasattr(module, '__category__')
        has_description = hasattr(module, '__description__') and getattr(module, '__description__', '').strip() != ''
        
        if not any([has_category, has_description]):
            return 0.0
        
        # Bonus points for proper docstring
        import ast
        try:
            with open(self.capability_dir / filename, 'r') as f:
                tree = ast.parse(f.read())
                for node in ast.walk(tree):
                    if isinstance(node, ast.DocString):
                        return min(1.0, 0.3 + (2 * bool(has_category) + bool(has_description)) / 4)
        except:
            pass
        
        return 0.5
    
    def enrich_model_with_capability(self, filename: str) -> None:
        """
        Adds a new capability to the model.
        
        Args:
            filename: Name of the capability file
        """
        try:
            analysis = self.analyze_capability_code(filename)
            
            if 'error' in analysis:
                self.logger.warning(f"Could not analyze {filename}: {analysis['error']}")
                return
            
            # Update capability list
            self.models['capabilities']['list'].append(filename)
            
            # Update category stats
            category = analysis['category']
            self.models['capabilities']['by_category'][category] = (
                self.models['capabilities']['by_category'].get(category, 0) + 1
            )
            
            self.logger.info(f"Enriched model with: {filename}")
            
        except Exception as e:
            self.logger.error(f"Failed to enrich model with {filename}: {e}")
    
    def display_current_model(self) -> None:
        """Prints current modeled knowledge"""
        print("\nCurrent System Model:")
        print("=" * 40)
        
        for aspect, data in self.models.items():
            if isinstance(data, dict):
                print(f"\n{aspect.title()}:")
                for key, value in data.items():
                    if isinstance(value, dict):
                        print(f"  {key}:")
                        for k, v in value.items():
                            print(f"    {k}: {v}")
                    else:
                        print(f"  {key}: {value}")
            else:
                print(f"\n{aspect}: {data}")
    
    def save_models_to_disk(self) -> None:
        """Saves models to JSON files for persistence"""
        import json
        
        try:
            # Save capability list
            with open('capability_list.json', 'w') as f:
                json.dump(
                    self.models['capabilities'],
                    f,
                    indent=2,
                    ensure_ascii=False
                )
            
            self.logger.info("Models saved to disk")
            
        except Exception as e:
            self.logger.error(f"Failed to save models: {e}")


if __name__ == "__main__":
    # Example usage
    updater = SelfModelUpdater()
    
    print("=" * 40)
    print("SELF-MODEL-UPDATER")
    print("=" * 40)
    
    # Update capability list
    updater.update_capability_list()
    
    # Display initial model
    updater.display_current_model()
    
    # Add specific capability to model (optional)
    test_capability = 'eden_meta_cap_analyzer.py'
    if test_capability in [cap.name for cap in Path(updater.capability_dir).glob('*.py')]:
        updater.enrich_model_with_capability(test_capability)
        
        print(f"\nAdditional capability added: {test_capability}")
        updater.display_current_model()
    
    # Save models
    updater.save_models_to_disk()


# Auto-generated Plugin wrapper
class Plugin:
    """Plugin wrapper for self_model_updater"""
    name = "self_model_updater"
    version = "1.0"
    description = "Maintains accurate model of own capabilities"
    price = 150
    
    def __init__(self):
        self.model = None
        self.enabled = True
    
    def initialize(self):
        """Initialize Plugin"""
        self.model = SelfModelUpdater()
        
    def transform(self, input_data):
        """Transform data for Plugin"""
        return input_data

    def think(self, data):
        """Main thinking/processing method"""
        return self.model.run_template(data)
    
    def get_metrics(self):
        """Return metrics from Plugin"""
        if self.model:
            return self.model.get_metrics()
        return None
    
    def set_model(self, model):
        """Set underlining LLM model for Plugin to use"""
        self.model = model
        


if __name__ == '__main__':
    plugin = Plugin()
    plugin.initialize()
    print(plugin.think({"input": "Hello"}))