"""
Toxo Package Manager.

This module handles the creation, loading, and management of .toxo packages,
which are the deployable artifacts containing trained Toxo layers.
"""

import os
import json
import zipfile
import tempfile
import shutil
from pathlib import Path
from typing import Dict, Any, Optional, List, Union
from dataclasses import dataclass, asdict
from datetime import datetime
import numpy as np
import pickle
import hashlib

from .config import ToxoConfig
from .toxo_layer import ToxoLayer, ToxoLayerMetadata
from ..utils.logger import get_logger
from ..utils.exceptions import ToxoError, PackageError


def safe_json_serialize(obj: Any) -> Any:
    """Safely serialize objects to JSON-compatible format."""
    if isinstance(obj, (str, int, float, bool, type(None))):
        return obj
    elif isinstance(obj, (Path, type(Path()))):
        return str(obj)
    elif isinstance(obj, datetime):
        return obj.isoformat()
    elif isinstance(obj, np.ndarray):
        return obj.tolist()
    elif isinstance(obj, dict):
        return {key: safe_json_serialize(value) for key, value in obj.items()}
    elif isinstance(obj, (list, tuple)):
        return [safe_json_serialize(item) for item in obj]
    elif hasattr(obj, '__dict__'):
        # For custom objects, try to serialize their dict representation
        try:
            return safe_json_serialize(obj.__dict__)
        except (AttributeError, TypeError):
            return str(obj)
    elif hasattr(obj, 'to_dict'):
        # For objects with to_dict method
        try:
            return safe_json_serialize(obj.to_dict())
        except (AttributeError, TypeError):
            return str(obj)
    else:
        # Fallback to string representation
        return str(obj)


@dataclass
class PackageManifest:
    """Manifest for a .toxo package."""
    name: str
    version: str
    description: str
    domain: str
    task_type: str
    created_at: str
    created_by: str
    
    # Component information
    has_soft_prompt: bool = False
    has_memory: bool = False
    has_reranker: bool = False
    has_postprocessor: bool = False
    
    # Training information
    training_examples: int = 0
    training_method: str = "unknown"
    performance_metrics: Dict[str, float] = None
    
    # Dependencies
    toxo_version: str = "0.1.0"
    gemini_model: str = "gemini-2.5-flash-lite"
    required_packages: List[str] = None
    
    # Metadata
    tags: List[str] = None
    license: str = "proprietary"
    author: str = ""
    author_email: str = ""
    homepage: str = ""
    
    # File checksums for integrity
    file_checksums: Dict[str, str] = None
    
    def __post_init__(self):
        """Initialize default values."""
        if self.performance_metrics is None:
            self.performance_metrics = {}
        if self.required_packages is None:
            self.required_packages = []
        if self.tags is None:
            self.tags = []
        if self.file_checksums is None:
            self.file_checksums = {}


class ToxoPackageManager:
    """
    Manager for creating, loading, and validating Toxo packages.
    
    Handles the .toxo package format which includes:
    - Trained components (soft prompts, memory, etc.)
    - Configuration and metadata
    - Dependencies and requirements
    - Integrity verification
    """
    
    def __init__(self):
        """Initialize package manager."""
        self.logger = get_logger("toxo.package_manager")
        
        # Package format version
        self.format_version = "1.0"
        
        # Supported file types
        self.supported_extensions = {".toxo", ".zip"}
        
        self.logger.info("Toxo package manager initialized")
    
    def create_package(
        self,
        layer: ToxoLayer,
        output_path: Union[str, Path],
        manifest: Optional[PackageManifest] = None,
        include_training_data: bool = False
    ) -> str:
        """
        Create a .toxo package from a trained layer.
        
        Args:
            layer: Trained ToxoLayer instance
            output_path: Path to save the package
            manifest: Package manifest (optional)
            include_training_data: Whether to include training data
            
        Returns:
            Path to created package
        """
        output_path = Path(output_path)
        if not output_path.suffix:
            output_path = output_path.with_suffix(".toxo")
        
        self.logger.info(f"Creating package: {output_path}")
        
        # Create temporary directory for package contents
        with tempfile.TemporaryDirectory() as temp_dir:
            package_dir = Path(temp_dir) / "package"
            package_dir.mkdir()
            
            # Create directory structure
            self._create_package_structure(package_dir)
            
            # Generate manifest if not provided
            if manifest is None:
                manifest = self._generate_manifest(layer)
            
            # Save layer components
            self._save_layer_components(layer, package_dir, manifest)
            
            # Save configuration
            self._save_configuration(layer, package_dir)
            
            # Save training data if requested
            if include_training_data:
                self._save_training_data(layer, package_dir)
            
            # Calculate file checksums
            self._calculate_checksums(package_dir, manifest)
            
            # Save manifest
            self._save_manifest(manifest, package_dir)
            
            # Create ZIP archive
            self._create_archive(package_dir, output_path)
        
        self.logger.info(f"Package created successfully: {output_path}")
        return str(output_path)
    
    def load_package(
        self,
        package_path: Union[str, Path],
        validate_integrity: bool = True
    ) -> ToxoLayer:
        """
        Load a .toxo package and reconstruct the layer.
        
        Args:
            package_path: Path to the .toxo package
            validate_integrity: Whether to validate file integrity
            
        Returns:
            Loaded ToxoLayer instance
        """
        package_path = Path(package_path)
        
        if not package_path.exists():
            raise PackageError(f"Package not found: {package_path}")
        
        self.logger.info(f"Loading package: {package_path}")
        
        # Create temporary directory for extraction
        with tempfile.TemporaryDirectory() as temp_dir:
            extract_dir = Path(temp_dir) / "extracted"
            
            # Extract package
            self._extract_archive(package_path, extract_dir)
            
            # Load manifest
            manifest = self._load_manifest(extract_dir)
            
            # Validate integrity if requested and manifest has checksums
            if validate_integrity and manifest.file_checksums:
                self._validate_package_integrity(extract_dir, manifest)
            
            # Load configuration
            config = self._load_configuration(extract_dir)
            
            # Load ownership metadata from manifest.json file first (to pass to constructor)
            manifest_path = extract_dir / "manifest.json"
            user_id = "anonymous"
            user_email = ""
            is_public = True
            
            if manifest_path.exists():
                with open(manifest_path, 'r') as f:
                    raw_manifest = json.load(f)
                    user_id = raw_manifest.get('user_id', 'anonymous')
                    user_email = raw_manifest.get('user_email', '')
                    is_public = raw_manifest.get('is_public', True)
            
            # Create layer instance with ownership information
            layer = ToxoLayer(
                domain=manifest.domain,
                task_type=manifest.task_type,
                config=config,
                user_id=user_id,
                user_email=user_email,
                is_public=is_public
            )
            
            # Load layer components (soft prompt, memory, reranker)
            self._load_layer_components(layer, extract_dir, manifest)
            # Load prompt_config and memory_state (training_contexts, key_rules, example_phrases)
            self._load_prompt_and_memory_rules(layer, extract_dir)
            
            # Set metadata including ownership information
            layer.metadata = ToxoLayerMetadata(
                name=manifest.name,
                version=manifest.version,
                description=manifest.description,
                domain=manifest.domain,
                task_type=manifest.task_type,
                created_at=manifest.created_at,
                updated_at=manifest.created_at,
                training_examples=manifest.training_examples,
                performance_metrics=manifest.performance_metrics,
                tags=manifest.tags,
                user_id=user_id,
                user_email=user_email,
                is_public=is_public
            )
            
            # Set additional deployment fields that aren't in ToxoLayerMetadata class
            if manifest_path.exists():
                with open(manifest_path, 'r') as f:
                    raw_manifest = json.load(f)
                    
                # Set deployment fields dynamically (these aren't in ToxoLayerMetadata class)
                if 'is_deployed' in raw_manifest:
                    layer.metadata.is_deployed = raw_manifest['is_deployed']
                if 'deployment_name' in raw_manifest:
                    layer.metadata.deployment_name = raw_manifest['deployment_name']
        
        self.logger.info(f"Package loaded successfully: {manifest.name} v{manifest.version}")
        return layer
    
    def validate_package(self, package_path: Union[str, Path]) -> Dict[str, Any]:
        """
        Validate a .toxo package without loading it.
        
        Args:
            package_path: Path to the .toxo package
            
        Returns:
            Validation results
        """
        package_path = Path(package_path)
        results = {
            "valid": False,
            "errors": [],
            "warnings": [],
            "manifest": None,
            "file_integrity": True
        }
        
        try:
            # Check file exists
            if not package_path.exists():
                results["errors"].append(f"Package file not found: {package_path}")
                return results
            
            # Check file extension
            if package_path.suffix not in self.supported_extensions:
                results["warnings"].append(f"Unusual file extension: {package_path.suffix}")
            
            # Extract and validate
            with tempfile.TemporaryDirectory() as temp_dir:
                extract_dir = Path(temp_dir) / "extracted"
                
                # Extract package
                try:
                    self._extract_archive(package_path, extract_dir)
                except Exception as e:
                    results["errors"].append(f"Failed to extract package: {str(e)}")
                    return results
                
                # Load manifest
                try:
                    manifest = self._load_manifest(extract_dir)
                    results["manifest"] = asdict(manifest)
                except Exception as e:
                    results["errors"].append(f"Failed to load manifest: {str(e)}")
                    return results
                
                # Validate structure
                structure_errors = self._validate_package_structure(extract_dir)
                results["errors"].extend(structure_errors)
                
                # Validate integrity
                try:
                    self._validate_package_integrity(extract_dir, manifest)
                except Exception as e:
                    results["file_integrity"] = False
                    results["warnings"].append(f"File integrity check failed: {str(e)}")
                
                # Check for required components
                if manifest.has_soft_prompt:
                    soft_prompt_path = extract_dir / "components" / "soft_prompt.npy"
                    if not soft_prompt_path.exists():
                        results["errors"].append("Manifest indicates soft prompt but file not found")
                
                if manifest.has_memory:
                    memory_path = extract_dir / "memory"
                    if not memory_path.exists():
                        results["errors"].append("Manifest indicates memory but directory not found")
            
            # Package is valid if no errors
            results["valid"] = len(results["errors"]) == 0
            
        except Exception as e:
            results["errors"].append(f"Unexpected validation error: {str(e)}")
        
        return results
    
    def _create_package_structure(self, package_dir: Path) -> None:
        """Create the standard .toxo package directory structure."""
        directories = [
            "components",
            "memory", 
            "config",
            "training_data",
            "scripts"
        ]
        
        for directory in directories:
            (package_dir / directory).mkdir(exist_ok=True)
    
    def _generate_manifest(self, layer: ToxoLayer) -> PackageManifest:
        """Generate a manifest from a layer."""
        metadata = layer.metadata or ToxoLayerMetadata()
        
        return PackageManifest(
            name=metadata.name or f"toxo_layer_{layer.domain}",
            version=metadata.version or "1.0.0",
            description=metadata.description or f"Toxo layer for {layer.domain}",
            domain=layer.domain,
            task_type=layer.task_type,
            created_at=datetime.now().isoformat(),
            created_by="toxo-package-manager",
            has_soft_prompt=layer.soft_prompt_engine is not None,
            has_memory=layer.memory is not None,
            has_reranker=layer.reranker is not None,
            training_examples=metadata.training_examples or 0,
            performance_metrics=metadata.performance_metrics or {},
            tags=metadata.tags or []
        )
    
    def _save_layer_components(
        self, 
        layer: ToxoLayer, 
        package_dir: Path, 
        manifest: PackageManifest
    ) -> None:
        """Save layer components to package."""
        components_dir = package_dir / "components"
        
        # Save soft prompt if present
        if layer.soft_prompt_engine and manifest.has_soft_prompt:
            soft_prompt = layer.soft_prompt_engine.get_current_prompt()
            if soft_prompt is not None:
                np.save(components_dir / "soft_prompt.npy", soft_prompt)
                
                # Save soft prompt metadata
                soft_prompt_meta = {
                    "shape": soft_prompt.shape,
                    "dtype": str(soft_prompt.dtype),
                    "training_stats": layer.soft_prompt_engine.get_training_stats()
                }
                with open(components_dir / "soft_prompt_meta.json", "w") as f:
                    json.dump(soft_prompt_meta, f, indent=2)
        
        # Save memory if present
        if layer.memory and manifest.has_memory:
            memory_dir = package_dir / "memory"
            
            # Save memory state
            memory_state = layer.memory.get_state()
            with open(memory_dir / "memory_state.json", "w") as f:
                json.dump(memory_state, f, indent=2)
            
            # Save memory embeddings if they exist
            embeddings = layer.memory.get_all_embeddings()
            if embeddings:
                np.save(memory_dir / "embeddings.npy", embeddings)
        
        # Save reranker if present
        if layer.reranker and manifest.has_reranker:
            reranker_state = layer.reranker.get_state()
            with open(components_dir / "reranker_state.pickle", "wb") as f:
                pickle.dump(reranker_state, f)
    
    def _save_configuration(self, layer: ToxoLayer, package_dir: Path) -> None:
        """Save layer configuration."""
        config_dir = package_dir / "config"
        
        # Save Toxo configuration
        config_dict = {
            "domain": layer.domain,
            "task_type": layer.task_type,
            "is_trained": layer.is_trained,
            "config": layer.config.__dict__ if layer.config else {}
        }
        
        with open(config_dir / "toxo_config.json", "w") as f:
            json.dump(safe_json_serialize(config_dict), f, indent=2)
        
        # Save prompt configuration if available
        if hasattr(layer, 'rich_prompt_engine') and layer.rich_prompt_engine:
            # Safely serialize master_prompt
            master_prompt_dict = None
            if hasattr(layer, 'master_prompt') and layer.master_prompt:
                try:
                    master_prompt_dict = safe_json_serialize(layer.master_prompt)
                except Exception as e:
                    self.logger.warning(f"Could not serialize master_prompt: {e}")
                    master_prompt_dict = {"serialization_error": str(e)}
            
            prompt_config = {
                "master_prompt": master_prompt_dict,
                "template_format": getattr(layer.rich_prompt_engine, 'template_format', 'professional')
            }
            with open(config_dir / "prompt_config.json", "w") as f:
                json.dump(safe_json_serialize(prompt_config), f, indent=2)
    
    def _save_training_data(self, layer: ToxoLayer, package_dir: Path) -> None:
        """Save training data if available."""
        training_dir = package_dir / "training_data"
        
        # This would save training examples if the layer stores them
        # For now, we'll create a placeholder
        training_info = {
            "note": "Training data not included in this package",
            "training_method": "SPSA + evolutionary optimization",
            "examples_used": getattr(layer.metadata, 'training_examples', 0) if layer.metadata else 0
        }
        
        with open(training_dir / "training_info.json", "w") as f:
            json.dump(safe_json_serialize(training_info), f, indent=2)
    
    def _calculate_checksums(self, package_dir: Path, manifest: PackageManifest) -> None:
        """Calculate SHA256 checksums for all files."""
        checksums = {}
        
        for file_path in package_dir.rglob("*"):
            if file_path.is_file():
                relative_path = file_path.relative_to(package_dir)
                
                with open(file_path, "rb") as f:
                    file_hash = hashlib.sha256(f.read()).hexdigest()
                    checksums[str(relative_path)] = file_hash
        
        manifest.file_checksums = checksums
    
    def _save_manifest(self, manifest: PackageManifest, package_dir: Path) -> None:
        """Save package manifest."""
        manifest_dict = asdict(manifest)
        
        with open(package_dir / "manifest.json", "w") as f:
            json.dump(safe_json_serialize(manifest_dict), f, indent=2)
    
    def _create_archive(self, package_dir: Path, output_path: Path) -> None:
        """Create ZIP archive from package directory."""
        with zipfile.ZipFile(output_path, "w", zipfile.ZIP_DEFLATED) as zf:
            for file_path in package_dir.rglob("*"):
                if file_path.is_file():
                    arcname = file_path.relative_to(package_dir)
                    zf.write(file_path, arcname)
    
    def _extract_archive(self, package_path: Path, extract_dir: Path) -> None:
        """Extract ZIP archive to directory."""
        extract_dir.mkdir(parents=True, exist_ok=True)
        
        with zipfile.ZipFile(package_path, "r") as zf:
            zf.extractall(extract_dir)
    
    def _load_manifest(self, package_dir: Path) -> PackageManifest:
        """Load package manifest, handling both old and new formats."""
        manifest_path = package_dir / "manifest.json"
        
        if not manifest_path.exists():
            raise PackageError("Package manifest not found")
        
        with open(manifest_path, "r") as f:
            manifest_dict = json.load(f)
        
        # Handle old format with nested structure
        if "format_version" in manifest_dict and "package_info" in manifest_dict:
            # Old format - extract and restructure
            package_info = manifest_dict["package_info"]
            components = manifest_dict.get("components", {})
            metadata = manifest_dict.get("metadata", {})
            
            # Create new format manifest
            new_manifest = {
                "name": package_info.get("name", "Unknown Layer"),
                "version": package_info.get("version", "1.0.0"),
                "description": package_info.get("description", ""),
                "domain": package_info.get("domain", "general"),
                "task_type": package_info.get("task_type", "general"),
                "created_at": package_info.get("created_at", ""),
                "created_by": "legacy-import",
                "has_soft_prompt": components.get("has_soft_prompt", False),
                "has_memory": components.get("has_memory", False),
                "has_reranker": components.get("has_reranker", False),
                "has_postprocessor": False,
                "training_examples": metadata.get("training_examples", 0),
                "training_method": metadata.get("training_method", "unknown"),
                "performance_metrics": metadata.get("performance_metrics", {}),
                "toxo_version": "0.2.0",
                "gemini_model": "gemini-2.5-flash-lite",
                "required_packages": [],
                "tags": metadata.get("tags", []),
                "license": "proprietary",
                "author": "",
                "author_email": "",
                "homepage": "",
                "file_checksums": {}
            }
            
            return PackageManifest(**new_manifest)
        else:
            # New format - use directly (but filter out any invalid fields)
            valid_fields = {
                'name', 'version', 'description', 'domain', 'task_type', 'created_at', 'created_by',
                'has_soft_prompt', 'has_memory', 'has_reranker', 'has_postprocessor',
                'training_examples', 'training_method', 'performance_metrics',
                'toxo_version', 'gemini_model', 'required_packages', 'tags',
                'license', 'author', 'author_email', 'homepage', 'file_checksums'
            }
            
            filtered_dict = {k: v for k, v in manifest_dict.items() if k in valid_fields}
            return PackageManifest(**filtered_dict)
    
    def _load_configuration(self, package_dir: Path) -> ToxoConfig:
        """Load layer configuration, handling both JSON and YAML formats."""
        config_json_path = package_dir / "config" / "toxo_config.json"
        config_yaml_path = package_dir / "config" / "toxo_config.yaml"
        
        config_dict = None
        
        # Try JSON first, then YAML (for backward compatibility)
        if config_json_path.exists():
            with open(config_json_path, "r") as f:
                config_dict = json.load(f)
        elif config_yaml_path.exists():
            import yaml
            with open(config_yaml_path, "r") as f:
                config_dict = yaml.safe_load(f)
        else:
            # Return default config if not found
            return ToxoConfig()
        
        # Handle old YAML format (direct config structure)
        if config_dict and not "config" in config_dict:
            # Old YAML format - config is at top level
            saved_config = config_dict
        elif "config" in config_dict:
            # New JSON format - config is nested
            saved_config = config_dict["config"]
        else:
            return ToxoConfig()
        
        # Reconstruct ToxoConfig from saved data
        config = ToxoConfig()
        
        # Define valid ToxoConfig attributes to avoid setting invalid ones
        valid_config_attrs = {
            'data_dir', 'config_path'  # Only simple attributes that ToxoConfig accepts
        }

        # Set simple attributes
        for key, value in saved_config.items():
            if key in valid_config_attrs and hasattr(config, key):
                try:
                    setattr(config, key, value)
                except Exception:
                    # Skip attributes that can't be set
                    pass

        # Reconstruct complex nested objects if they exist in saved config
        if hasattr(config, 'gemini') and isinstance(saved_config.get('gemini'), dict):
            # Reconstruct gemini config
            gemini_dict = saved_config['gemini']
            for key, value in gemini_dict.items():
                if hasattr(config.gemini, key):
                    try:
                        setattr(config.gemini, key, value)
                    except Exception:
                        pass

        if hasattr(config, 'training') and isinstance(saved_config.get('training'), dict):
            # Reconstruct training config
            training_dict = saved_config['training']
            for key, value in training_dict.items():
                if hasattr(config.training, key):
                    try:
                        setattr(config.training, key, value)
                    except Exception:
                        pass

        if hasattr(config, 'memory') and isinstance(saved_config.get('memory'), dict):
            # Reconstruct memory config
            memory_dict = saved_config['memory']
            for key, value in memory_dict.items():
                if hasattr(config.memory, key):
                    try:
                        setattr(config.memory, key, value)
                    except Exception:
                        pass
        
        return config
    
    def _load_layer_components(
        self, 
        layer: ToxoLayer, 
        package_dir: Path, 
        manifest: PackageManifest
    ) -> None:
        """Load layer components from package."""
        components_dir = package_dir / "components"
        
        # Load soft prompt if present
        if manifest.has_soft_prompt:
            soft_prompt_path = components_dir / "soft_prompt.npy"
            if soft_prompt_path.exists():
                soft_prompt = np.load(soft_prompt_path)
                length, dimension = soft_prompt.shape
                # Recreate SoftPromptEngine with saved dimensions if mismatch (e.g. saved 20x768 vs default 10x768)
                if layer.soft_prompt_engine and (
                    layer.soft_prompt_engine.length != length or layer.soft_prompt_engine.dimension != dimension
                ):
                    from ..training.soft_prompt import SoftPromptEngine
                    layer.soft_prompt_engine = SoftPromptEngine(
                        dimension=int(dimension),
                        length=int(length),
                        config=layer.config.training,
                        prompt_config=getattr(layer.config, 'prompt', None),
                        existing_prompt=soft_prompt,
                    )
                    self.logger.info(f"Recreated SoftPromptEngine with saved shape ({length}, {dimension})")
                elif layer.soft_prompt_engine:
                    layer.soft_prompt_engine.set_prompt(soft_prompt)
                layer.is_trained = True
        
        # Load memory if present
        if manifest.has_memory:
            memory_dir = package_dir / "memory"
            memory_state_path = memory_dir / "memory_state.json"
            
            if memory_state_path.exists() and layer.memory:
                with open(memory_state_path, "r") as f:
                    memory_state = json.load(f)
                layer.memory.load_state(memory_state)
                
                # Load embeddings if they exist
                embeddings_path = memory_dir / "embeddings.npy"
                if embeddings_path.exists():
                    embeddings = np.load(embeddings_path)
                    # This would need to be implemented in the memory class
                    # layer.memory.load_embeddings(embeddings)
        
        # Load reranker if present (support both .pickle and .json)
        if manifest.has_reranker and layer.reranker:
            for ext in (".pickle", ".json"):
                reranker_path = components_dir / f"reranker_state{ext}"
                if reranker_path.exists():
                    try:
                        if ext == ".pickle":
                            with open(reranker_path, "rb") as f:
                                reranker_state = pickle.load(f)
                        else:
                            with open(reranker_path, "r") as f:
                                reranker_state = json.load(f)
                        layer.reranker.load_state(reranker_state)
                        break
                    except Exception as e:
                        self.logger.warning(f"Failed to load reranker from {reranker_path}: {e}")
    
    def _load_prompt_and_memory_rules(self, layer: ToxoLayer, package_dir: Path) -> None:
        """Load prompt_config.json and memory_state.json (training_contexts, key_rules, example_phrases)."""
        config_dir = package_dir / "config"
        memory_dir = package_dir / "memory"
        
        # Load prompt_config.json
        prompt_config_path = config_dir / "prompt_config.json"
        if prompt_config_path.exists():
            try:
                with open(prompt_config_path, "r") as f:
                    layer._prompt_config = json.load(f)
                self.logger.info("Loaded prompt_config.json")
            except Exception as e:
                self.logger.warning(f"Failed to load prompt_config.json: {e}")
                layer._prompt_config = {}
        else:
            layer._prompt_config = {}
        
        # Load memory_state.json (training_contexts, key_rules, example_phrases for static injection)
        memory_state_path = memory_dir / "memory_state.json"
        if memory_state_path.exists():
            try:
                with open(memory_state_path, "r") as f:
                    ms = json.load(f)
                layer._static_memory_rules = {
                    "training_contexts": ms.get("training_contexts", []),
                    "key_rules": ms.get("key_rules", {}),
                    "example_phrases": ms.get("example_phrases", {}),
                }
                # Also pass to DomainMemory for compatibility
                if layer.memory:
                    layer.memory.load_state(ms)
                self.logger.info("Loaded memory_state.json (training_contexts, key_rules, example_phrases)")
            except Exception as e:
                self.logger.warning(f"Failed to load memory_state.json: {e}")
                layer._static_memory_rules = {}
        else:
            layer._static_memory_rules = {}
    
    def _validate_package_integrity(
        self, 
        package_dir: Path, 
        manifest: PackageManifest
    ) -> None:
        """Validate package file integrity using checksums."""
        if not manifest.file_checksums:
            raise PackageError("No checksums found in manifest")
        
        for relative_path, expected_hash in manifest.file_checksums.items():
            file_path = package_dir / relative_path
            
            if not file_path.exists():
                raise PackageError(f"Missing file: {relative_path}")
            
            with open(file_path, "rb") as f:
                actual_hash = hashlib.sha256(f.read()).hexdigest()
            
            if actual_hash != expected_hash:
                raise PackageError(f"Checksum mismatch for {relative_path}")
    
    def _validate_package_structure(self, package_dir: Path) -> List[str]:
        """Validate package directory structure."""
        errors = []
        
        # Check for required files
        required_files = ["manifest.json"]
        for file_name in required_files:
            if not (package_dir / file_name).exists():
                errors.append(f"Missing required file: {file_name}")
        
        # Check for required directories
        required_dirs = ["components", "config"]
        for dir_name in required_dirs:
            if not (package_dir / dir_name).exists():
                errors.append(f"Missing required directory: {dir_name}")
        
        return errors
    
    def list_package_contents(self, package_path: Union[str, Path]) -> Dict[str, Any]:
        """List contents of a .toxo package."""
        package_path = Path(package_path)
        
        contents = {
            "manifest": None,
            "files": [],
            "size_bytes": package_path.stat().st_size,
            "components": {}
        }
        
        with tempfile.TemporaryDirectory() as temp_dir:
            extract_dir = Path(temp_dir) / "extracted"
            
            # Extract package
            self._extract_archive(package_path, extract_dir)
            
            # Load manifest
            manifest = self._load_manifest(extract_dir)
            contents["manifest"] = asdict(manifest)
            
            # List all files
            for file_path in extract_dir.rglob("*"):
                if file_path.is_file():
                    relative_path = file_path.relative_to(extract_dir)
                    file_info = {
                        "path": str(relative_path),
                        "size": file_path.stat().st_size,
                        "type": file_path.suffix or "unknown"
                    }
                    contents["files"].append(file_info)
            
            # Analyze components
            components_dir = extract_dir / "components"
            if components_dir.exists():
                for component_file in components_dir.iterdir():
                    if component_file.is_file():
                        component_name = component_file.stem
                        contents["components"][component_name] = {
                            "file": component_file.name,
                            "size": component_file.stat().st_size
                        }
        
        return contents
    
    def get_package_info(self, package_path: Union[str, Path]) -> Dict[str, Any]:
        """Get basic information about a .toxo package."""
        package_path = Path(package_path)
        
        info = {
            "path": str(package_path),
            "exists": package_path.exists(),
            "size_mb": 0,
            "manifest": None,
            "valid": False
        }
        
        if not package_path.exists():
            return info
        
        info["size_mb"] = round(package_path.stat().st_size / (1024 * 1024), 2)
        
        try:
            validation = self.validate_package(package_path)
            info["valid"] = validation["valid"]
            info["manifest"] = validation["manifest"]
            info["errors"] = validation.get("errors", [])
            info["warnings"] = validation.get("warnings", [])
        except Exception as e:
            info["errors"] = [str(e)]
        
        return info 