
from pathlib import Path
from typing import Any, Optional, Union, Dict, List, Set, Tuple, Callable
from types import ModuleType, FunctionType
from functools import cached_property, lru_cache, wraps, partial



import importlib
import shutil
import os
import time
import tracemalloc
import sys
import hashlib
import inspect
import threading
from concurrent.futures import ThreadPoolExecutor, as_completed, Future


from datetime import datetime
import tempfile
import zipfile
import json
import pickle
from contextlib import contextmanager
from dataclasses import dataclass
from enum import Enum
import weakref

try:
    import build
    from build import ProjectBuilder
except ImportError:
    build = None
    ProjectBuilder = None

from .ImporterException import PathExistsError, ModuleExistsError, PkgNotFoundError
from .path.utils import make
from .path.filetools import copy, move, read, write
from .path.metafile import getsitepath
from .text.tree import tree_project
from .pymodex import pmeX
from ._builtin import isbuiltin
from .importer import Modulex


class ModuleState(Enum):
    CREATED = "created"
    MODIFIED = "modified"
    MOVED = "moved"
    SYMLINKED = "symlinked"
    PUBLISHED = "published"
    BUILT = "built"
    REMOVED = "removed"


@dataclass(frozen=True)
class ModuleMetadata:
    name: str
    path: Path
    created_at: datetime
    modified_at: datetime
    size_bytes: int
    file_count: int
    dir_count: int
    hash_digest: str


class CryptographicHasher:
    """cryptographic hashing with multiple algorithms"""
    
    def __init__(self, algorithm: str = "blake2b"):
        self.algorithm = algorithm
        self._cache = {}
    
    def compute_hash(self, data: bytes, salt: bytes = b"") -> str:
        cache_key = f"{data}{salt}".encode()
        if cache_key in self._cache:
            return self._cache[cache_key]
        
        if self.algorithm == "blake2b":
            hasher = hashlib.blake2b(salt=salt)
        elif self.algorithm == "sha3_256":
            hasher = hashlib.sha3_256()
        else:
            hasher = hashlib.md5()
        
        hasher.update(data)
        digest = hasher.hexdigest()
        self._cache[cache_key] = digest
        return digest


class ThreadSafeRegistry:
    """Thread-safe registry for module operations"""
    
    def __init__(self):
        self._registry = {}
        self._lock = threading.RLock()
        self._observers = weakref.WeakSet()
    
    def register(self, key: str, value: Any) -> None:
        with self._lock:
            self._registry[key] = value
            self._notify_observers("register", key, value)
    
    def unregister(self, key: str) -> None:
        with self._lock:
            if key in self._registry:
                value = self._registry.pop(key)
                self._notify_observers("unregister", key, value)
    
    def get(self, key: str, default: Any = None) -> Any:
        with self._lock:
            return self._registry.get(key, default)
    
    def add_observer(self, observer: Callable) -> None:
        with self._lock:
            self._observers.add(observer)
    
    def _notify_observers(self, action: str, key: str, value: Any) -> None:
        for observer in list(self._observers):
            try:
                observer(action, key, value)
            except Exception:
                continue

class AdvancedCache:
    """Multi-level caching system with TTL and eviction policies"""
    
    def __init__(self, max_size: int = 1000, ttl: int = 3600):
        self._cache = {}
        self._access_times = {}
        self._max_size = max_size
        self._ttl = ttl
        self._lock = threading.Lock()
    
    def get(self, key: str) -> Any:
        with self._lock:
            if key not in self._cache:
                return None
            
            if time.time() - self._access_times[key] > self._ttl:
                self._evict(key)
                return None
            
            self._access_times[key] = time.time()
            return self._cache[key]
    
    def set(self, key: str, value: Any) -> None:
        with self._lock:
            if len(self._cache) >= self._max_size:
                self._evict_oldest()
            
            self._cache[key] = value
            self._access_times[key] = time.time()
    
    def _evict(self, key: str) -> None:
        self._cache.pop(key, None)
        self._access_times.pop(key, None)
    
    def _evict_oldest(self) -> None:
        if not self._access_times:
            return
        
        oldest_key = min(self._access_times.items(), key=lambda x: x[1])[0]
        self._evict(oldest_key)

class MakeModule:
    """
    A sophisticated utility class for creating and managing Python modules programmatically
    with features including cryptographic hashing, thread-safe operations,
    multi-level caching, and comprehensive metadata management.
    """

    _global_registry = ThreadSafeRegistry()
    _cache_system = AdvancedCache(max_size=2000, ttl=7200)
    _hasher = CryptographicHasher("blake2b")

    def __init__(self, name: str, exist_ok: bool = False, enable_validation: bool = True) -> None:
        """
        Initialize a new module creator with enhanced validation and tracking.

        Args:
            name: Name of the module to create
            exist_ok: If True, don't raise error if module directory already exists
            enable_validation: Enable comprehensive module validation

        Raises:
            TypeError: If name is not a string
            ModuleExistsError: If module already exists and exist_ok is False
        """
        if not isinstance(name, str):
            raise TypeError(f"Expected str for 'name', got {type(name).__name__}")

        self._validation_enabled = enable_validation
        self._state_history = []
        self._operation_lock = threading.RLock()
        self._metadata_cache = None
        self._observers = weakref.WeakSet()

        self.path = Path(name)
        self._original_path = self.path.resolve()

        # Advanced existence check with caching
        cache_key = f"exists_{self.path}"
        cached_exists = self._cache_system.get(cache_key)
        
        if cached_exists is None:
            cached_exists = self.path.exists()
            self._cache_system.set(cache_key, cached_exists)

        if cached_exists and not exist_ok:
            raise ModuleExistsError(f"Module '{name}' already exists in '{Path.cwd()}'")

        # Create module directory with enhanced error handling
        with self._operation_lock:
            self._create_module_directory()
            self.name = name
            self.location = self.path.resolve()
            self.site = SITE_PATH / self.path if SITE_PATH else None
            self.history_file = self.path / Path('.history_data')
            self.metadata_file = self.path / Path('.module_metadata')
            
            self._initialize_module_infrastructure()
            self._update_state(ModuleState.CREATED)
            self._global_registry.register(f"module_{self.name}", self)

    def _create_module_directory(self) -> None:
        """Create module directory with comprehensive error handling"""
        try:
            self.path.mkdir(exist_ok=True, parents=True)
        except OSError as e:
            raise OSError(f"Failed to create module directory '{self.path}': {e}") from e

    def _initialize_module_infrastructure(self) -> None:
        """Initialize all module infrastructure components"""
        self._write_history(f"CREATR MODULE {self.name}")
        self._update_metadata()
        self._setup_file_watchers()

    def _setup_file_watchers(self) -> None:
        """Setup virtual file system watchers for monitoring changes"""
        # Placeholder for actual file watching implementation
        pass

    def _update_state(self, state: ModuleState, details: str = "") -> None:
        """Update module state with comprehensive tracking"""
        state_entry = {
            "state": state,
            "timestamp": datetime.now(),
            "details": details,
            "location": str(self.location)
        }
        self._state_history.append(state_entry)
        self._notify_observers("state_change", state_entry)

    def _compute_module_hash(self) -> str:
        """Compute cryptographic hash of entire module contents"""
        module_data = b""
        for file_path in sorted(self.location.rglob("*")):
            if file_path.is_file():
                try:
                    module_data += file_path.read_bytes()
                    module_data += str(file_path.relative_to(self.location)).encode()
                except Exception:
                    continue
        
        return self._hasher.compute_hash(module_data, salt=self.name.encode())

    def _update_metadata(self) -> None:
        """Update comprehensive module metadata"""
        file_count = len([f for f in self.location.rglob("*") if f.is_file()])
        dir_count = len([d for d in self.location.rglob("*") if d.is_dir()])
        total_size = sum(f.stat().st_size for f in self.location.rglob("*") if f.is_file())
        
        metadata = ModuleMetadata(
            name=self.name,
            path=self.location,
            created_at=datetime.now(),
            modified_at=datetime.now(),
            size_bytes=total_size,
            file_count=file_count,
            dir_count=dir_count,
            hash_digest=self._compute_module_hash()
        )
        
        self.metadata_cache = metadata
        self._save_metadata_to_file(metadata)

    def _save_metadata_to_file(self, metadata: ModuleMetadata) -> None:
        """Save metadata to file using multiple serialization formats"""
        try:
            metadata_dict = {
                "name": metadata.name,
                "path": str(metadata.path),
                "created_at": metadata.created_at.isoformat(),
                "modified_at": metadata.modified_at.isoformat(),
                "size_bytes": metadata.size_bytes,
                "file_count": metadata.file_count,
                "dir_count": metadata.dir_count,
                "hash_digest": metadata.hash_digest
            }
            
            # Save as JSON for readability
            json_data = json.dumps(metadata_dict, indent=2)
            write(self.metadata_file, json_data)
            
            # Save as pickle for efficient loading
            pickle_file = self.metadata_file.with_suffix('.pickle')
            with open(pickle_file, 'wb') as f:
                pickle.dump(metadata, f)
                
        except Exception as e:
            raise

    def _load_metadata_from_file(self) -> Optional[ModuleMetadata]:
        """Load metadata from file with fallback strategies"""
        try:
            if self.metadata_file.with_suffix('.pickle').exists():
                with open(self.metadata_file.with_suffix('.pickle'), 'rb') as f:
                    return pickle.load(f)
            elif self.metadata_file.exists():
                json_data = read(self.metadata_file)
                if json_data:
                    metadata_dict = json.loads(json_data)
                    return ModuleMetadata(
                        name=metadata_dict["name"],
                        path=Path(metadata_dict["path"]),
                        created_at=datetime.fromisoformat(metadata_dict["created_at"]),
                        modified_at=datetime.fromisoformat(metadata_dict["modified_at"]),
                        size_bytes=metadata_dict["size_bytes"],
                        file_count=metadata_dict["file_count"],
                        dir_count=metadata_dict["dir_count"],
                        hash_digest=metadata_dict["hash_digest"]
                    )
        except Exception as e:
            raise
        
        return None

    @property
    def metadata_cache(self) -> Optional[ModuleMetadata]:
        """Get cached metadata with lazy loading"""
        if self._metadata_cache is None:
            self._metadata_cache = self._load_metadata_from_file()
        return self._metadata_cache

    @metadata_cache.setter
    def metadata_cache(self, value: ModuleMetadata) -> None:
        """Set metadata cache with validation"""
        if isinstance(value, ModuleMetadata):
            self._metadata_cache = value
            cache_key = f"metadata_{self.name}"
            self._cache_system.set(cache_key, value)

    def _create(self, relative_path: Path, create_func: Callable, overwrite: bool = False) -> None:
        """
        Internal method to create files/folders with enhanced safety checks
        and transactional semantics.
        """
        target = self.location / relative_path

        if target.exists() and not overwrite:
            raise PathExistsError(f"'{relative_path}' already exists in '{self.location}'")

        # Create temporary location for atomic operations
        with tempfile.NamedTemporaryFile(delete=False) as temp_file:
            temp_path = Path(temp_file.name)
            try:
                # Perform creation in temporary location
                create_func(temp_path)
                
                # Move to final location atomically
                if target.exists() and overwrite:
                    target.unlink()
                
                shutil.move(str(temp_path), str(target))
                self._update_metadata()
                
            except Exception as e:
                # Cleanup on failure
                if temp_path.exists():
                    temp_path.unlink()
                raise e
            finally:
                # Ensure temporary file is cleaned up
                if temp_path.exists():
                    temp_path.unlink()

    def _read_history(self) -> str:
        """Enhanced history reading with caching and validation"""
        cache_key = f"history_{self.name}"
        cached_history = self._cache_system.get(cache_key)
        
        if cached_history is not None:
            return cached_history

        if not self.history_file.exists():
            self.history_file.touch()
            return ""

        history_content = read(self.history_file) or ""
        self._cache_system.set(cache_key, history_content)
        return history_content

    def _write_history(self, data: str) -> None:
        """Enhanced history writing with atomic operations and caching"""
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
        entry = f"[{timestamp}] {data}\n"
        
        # Atomic write operation
        temp_file = self.history_file.with_suffix('.tmp')
        try:
            existing_content = self._read_history()
            new_content = existing_content + entry
            
            with open(temp_file, 'w') as f:
                f.write(new_content)
            
            temp_file.replace(self.history_file)
            
            # Update cache
            cache_key = f"history_{self.name}"
            self._cache_system.set(cache_key, new_content)
            
        finally:
            if temp_file.exists():
                temp_file.unlink()

    def add_file(self, filename: str, data: Any = None, encoding: str = 'utf-8', overwrite: bool = False) -> None:
        """Enhanced file addition with encoding support and transactional safety"""
        
        def create_file(p: Path):
            if data is None:
                p.touch()
            else:
                # Handle different data types intelligently
                if isinstance(data, (dict, list)):
                    import yaml
                    processed_data = yaml.dump(data, indent=2, default_flow_style=False)
                elif hasattr(data, '__dict__'):
                    processed_data = str(data)
                else:
                    processed_data = str(data)
                
                p.write_text(processed_data, encoding=encoding)

        self._create(Path(filename), create_file, overwrite)
        self._write_history(f"ADD FILE {filename}")
        self._update_state(ModuleState.MODIFIED, f"Added file: {filename}")

    def requirements(self, builtins: bool = False) -> str:
        """Enhanced requirements generation with filtering options"""
        try:
  	      imports = Modulex(self.name).structure().imports()
        except TypeError:
           raise FileNotFoundError(f'no such file for \'{self.name}\'') from None
        list_imports = list(imports['from']) + list(imports['import'])
        
        if builtins:
            filtered_imports = list_imports
        else:
            filtered_imports = [imp for imp in list_imports if not isbuiltin(imp)]
        
        return "\n".join(filtered_imports)

    def add_folder(self, foldername: str, recursive: bool = False, init_py: bool = True) -> None:
        """Enhanced folder addition with recursive structure and __init__.py options"""
        
        def create_folder(p: Path):
            p.mkdir(parents=True, exist_ok=True)
            if init_py:
                init_file = p / "__init__.py"
                if not init_file.exists():
                    init_file.touch()

        self._create(Path(foldername), create_folder)
        self._write_history(f"ADD FOLDER {foldername}")
        self._update_state(ModuleState.MODIFIED, f"Added folder: {foldername}")

    def to_site(self, editable: bool = False, force: bool = False) -> None:
        """
        Enhanced site installation with comprehensive error handling and rollback support.
        """
        if not SITE_PATH:
            raise RuntimeError("Cannot determine site-packages path")

        target = SITE_PATH / self.name
        original = self.location

        if target.exists() and not force:
            raise PathExistsError(f"Module already exists in site-packages: {target}")

        # Create backup for rollback capability
        backup_path = None
        if target.exists() and force:
            backup_path = target.with_suffix('.backup')
            shutil.move(str(target), str(backup_path))

        try:
            # Move folder to site-packages
            shutil.move(str(original), str(target))

            if editable:
                try:
                    os.symlink(target, original)
                    self._write_history(f"CREATE SYMLINK FOR {self.name}")
                except OSError as e:
                    # Rollback on symlink failure
                    shutil.move(str(target), str(original))
                    if backup_path and backup_path.exists():
                        shutil.move(str(backup_path), str(target))
                    raise OSError(f"Failed to create symlink at {original}: {e}")

            # Update internal state
            self.location = target
            self.site = target
            self._write_history(f"MOVE {original} TO {target}")
            self._update_state(ModuleState.MOVED, f"To site: {target}")

        except Exception as e:
            # Comprehensive rollback
            if backup_path and backup_path.exists():
                shutil.move(str(backup_path), str(target))
            raise e
        finally:
            # Cleanup backup
            if backup_path and backup_path.exists():
                shutil.rmtree(backup_path)

    def build(self, errors: bool = True, build_dir: str = "dist", clean: bool = True) -> Union[bool, Exception]:
        """Enhanced build process with additional options"""
        if build is None:
            raise PkgNotFoundError("Module 'build' is not installed") 

        self._write_history(f"BUILD WHEEL FOR {self.name}")
        
        try:
            builder = build.ProjectBuilder(self.get_location())
            
            # Clean previous builds if requested
            if clean:
                build_path = Path(build_dir)
                if build_path.exists():
                    shutil.rmtree(build_path)
            
            # Build both wheel and sdist
            wheel_path = builder.build("wheel", build_dir)
            sdist_path = builder.build("sdist", build_dir)
            
            self._update_state(ModuleState.BUILT, f"Built: {wheel_path}, {sdist_path}")
            return True
            
        except Exception as e:
            if errors:
                raise 
            return e

    def publish(self, dist_path: str = "dist/*", username: str = "__token__", 
                password: str = None, test: bool = False, skip_build: bool = False) -> None:
        """Enhanced publishing with pre-build checks and validation"""
        
        if not skip_build:
            self.build()

        try:
            from twine.settings import Settings
            from twine.commands import upload
        except ImportError:
            raise ImportError("Twine must be installed to use publish(). Install with: pip install twine") from None

        from glob import glob
        files = glob(dist_path)
        if not files:
            raise FileNotFoundError(f"No distribution files found at path: {dist_path}")

        if username == "__token__" and not password:
            raise ValueError("API token must be provided when username='__token__'")

        repo_url = "https://test.pypi.org/legacy/" if test else "https://upload.pypi.org/legacy/"

        settings = Settings(
            repository_url=repo_url,
            username=username,
            password=password
        )

        # Validate files before upload
        for file_path in files:
            if not Path(file_path).exists():
                raise FileNotFoundError(f"Distribution file not found: {file_path}")

        upload.main(args=files)

        target = "TEST PYPI" if test else "PYPI"
        self._write_history(f"PUBLISH MODULE {self.name} TO {target}")
        self._update_state(ModuleState.PUBLISHED, f"Published to: {target}")

    def copy_to_site(self, overwrite: bool = False) -> None:
        """Enhanced copy operation with overwrite control"""
        if self.site and self.site.exists() and not overwrite:
            raise PathExistsError("Site path exists and cannot be copied")

        copy(self.get_location(), SITE_PATH, copy_function=shutil.copy2)
        self._write_history(f"COPY {self.location} TO {self.site}")

    def remove(self, force: bool = False, backup: bool = True) -> None:
        """Enhanced removal with backup and force options"""
        removed_locations = []

        # Create backup if requested
        backup_path = None
        if backup and self.location.exists():
            backup_path = self.location.with_suffix(f'.backup.{int(time.time())}')
            shutil.copytree(self.location, backup_path)

        try:
            # Remove from site-packages if present
            if self.site and self.site.exists():
                if force or self._confirm_removal(self.site):
                    shutil.rmtree(self.site)
                    removed_locations.append(("site", self.site))

            # Delete from original location
            if self.location.exists():
                if self.location.is_symlink():
                    self.location.unlink()
                    removed_locations.append(("symlink", self.location))
                elif force or self._confirm_removal(self.location):
                    shutil.rmtree(self.location)
                    removed_locations.append(("original", self.location))

            if not removed_locations and not force:
                raise FileNotFoundError("Module not found in any known location")

            self._update_state(ModuleState.REMOVED, f"Removed from: {removed_locations}")

        except Exception as e:
            # Restore from backup if removal failed
            if backup_path and backup_path.exists():
                if self.location.exists():
                    shutil.rmtree(self.location)
                shutil.move(backup_path, self.location)
            raise e

    def _confirm_removal(self, path: Path) -> bool:
        """Confirm removal of important paths"""
        # In a real implementation, this might involve user interaction
        # For now, return True for automation
        return True

    def call(self, subname: str = None, reload: bool = False) -> Optional[Any]:
        """Enhanced module import with reload capability and caching"""
        cache_key = f"import_{self.name}_{subname}"
        
        if not reload:
            cached_module = self._cache_system.get(cache_key)
            if cached_module is not None:
                return cached_module

        try:
            fullname = self.name if not subname else f"{self.name}.{subname}"
            
            if reload and fullname in sys.modules:
                module = importlib.reload(sys.modules[fullname])
            else:
                module = importlib.import_module(fullname)
            
            self._cache_system.set(cache_key, module)
            return module
            
        except ImportError:
            return None

    def exists(self) -> bool:
        """Enhanced existence check with multiple location verification"""
        locations_to_check = [self.location]
        if self.site:
            locations_to_check.append(self.site)
        
        return any(loc.exists() for loc in locations_to_check)

    def get_location(self) -> Path:
        """Get current location with fallback strategy"""
        locations = [self.site, self.location]
        for loc in locations:
            if loc and loc.exists():
                return loc
        return self.location

    def init(self) -> None:
        """Enhanced initialization"""
        pmeX(self.name).init_module()

    def isvalidmodule(self, strict: bool = False) -> bool:
        """Enhanced validation with strict mode"""
        base = self.get_location()
        init_file = base / "__init__.py"
        
        basic_validation = init_file.exists() and base.is_dir()
        
        if not strict or not basic_validation:
            return basic_validation
        
        # Strict validation includes additional checks
        try:
            # Try to import the module
            module = self.call()
            if module is None:
                return False
            
            # Check for basic module attributes
            required_attrs = {'__name__', '__file__', '__package__'}
            return all(hasattr(module, attr) for attr in required_attrs)
            
        except Exception:
            return False

    def rename(self, target: str, update_references: bool = True) -> None:
        """Enhanced renaming with reference updating"""
        old_name = self.name
        base = self.get_location()
        new_path = base.parent / Path(target)
        
        # Update global registry
        self._global_registry.unregister(f"module_{old_name}")
        
        base.rename(new_path)
        self.name = target
        self.location = new_path
        
        if SITE_PATH:
            self.site = SITE_PATH / Path(target)
        
        if update_references:
            self._update_module_references(old_name, target)
        
        self._write_history(f"RENAME {old_name} TO {target}")
        self._global_registry.register(f"module_{self.name}", self)

    def _update_module_references(self, old_name: str, new_name: str) -> None:
        """Update internal references after renaming"""
        # Placeholder for reference updating logic
        pass

    def list_content(self, fullpath: bool = False, pattern: str = "*", 
                    recursive: bool = True, include_dirs: bool = True, 
                    include_files: bool = True) -> List[str]:
        """Enhanced content listing with filtering options"""
        base = self.get_location()
        glob_pattern = "**/" + pattern if recursive else pattern
        
        items = []
        for item in base.glob(glob_pattern):
            if (item.is_dir() and not include_dirs) or (item.is_file() and not include_files):
                continue
            
            items.append(str(item) if fullpath else item.name)
        
        return sorted(items)

    def tree(self, max_depth: int = None, show_files: bool = True, 
             show_sizes: bool = False, **kwargs) -> str:
        """Enhanced tree display with additional options"""
        return tree_project(
            path=self.get_location(), 
            max_depth=max_depth,
            show_files=show_files,
            **kwargs
        )

    def reload(self) -> Union[ModuleType, None]:
        """Enhanced reload with dependency tracking"""
        mod = self.call()
        if mod:
            # Clear related caches
            cache_keys_to_clear = [key for key in self._cache_system._cache.keys() 
                                 if key.startswith(f"import_{self.name}")]
            for key in cache_keys_to_clear:
                self._cache_system._evict(key)
            
            return importlib.reload(mod)
        return None

    @lru_cache(maxsize=512)
    def search(self, keyword: str, target: str = "*.py", case_sensitive: bool = False, 
               use_regex: bool = False, max_workers: int = None) -> List[str]:
        """Enhanced search with regex support and performance optimization"""
        results = []
        base = self.get_location()
        files = list(base.rglob(target))
        
        if not files:
            return []

        def check(file_path: Path) -> Optional[str]:
            try:
                text = file_path.read_text(errors="ignore")
                
                if use_regex:
                    import re
                    pattern = re.compile(keyword) if case_sensitive else re.compile(keyword, re.IGNORECASE)
                    if pattern.search(text):
                        return str(file_path)
                else:
                    if not case_sensitive:
                        text = text.lower()
                        search_keyword = keyword.lower()
                    else:
                        search_keyword = keyword
                    
                    if search_keyword in text:
                        return str(file_path)
                        
            except Exception:
                return None
            return None

        with ThreadPoolExecutor(max_workers=max_workers) as executor:
            futures = [executor.submit(check, f) for f in files]
            for future in as_completed(futures):
                result = future.result()
                if result:
                    results.append(result)

        return sorted(results)

    def profile(self, iterations: int = 1, warmup: int = 0) -> Dict[str, Any]:
        """Enhanced profiling with multiple iterations and warmup"""
        # Warmup runs
        for _ in range(warmup):
            self.call()
            if self.call() in sys.modules:
                importlib.reload(sys.modules[self.name])

        load_times = []
        memory_usage = []
        sizes = []

        for _ in range(iterations):
            start_time = time.perf_counter()
            tracemalloc.start()
            
            mod = self.call()
            
            current, peak = tracemalloc.get_traced_memory()
            tracemalloc.stop()
            end_time = time.perf_counter()

            load_times.append(end_time - start_time)
            memory_usage.append(peak)
            sizes.append(sys.getsizeof(mod) if mod else 0)

        mod = self.call()
        funcs = len([attr for attr in dir(mod) if callable(getattr(mod, attr))]) if mod else 0

        return {
            "load_time_avg": sum(load_times) / len(load_times),
            "load_time_min": min(load_times),
            "load_time_max": max(load_times),
            "peak_memory_avg": sum(memory_usage) / len(memory_usage),
            "peak_memory_max": max(memory_usage),
            "size_object_avg": sum(sizes) / len(sizes),
            "functions": funcs,
            "iterations": iterations
        }

    @cached_property
    def stats(self) -> Dict[str, Any]:
        """Enhanced statistics with comprehensive metrics"""
        base = self.get_location()
        files = list(base.rglob("*"))
        
        file_list = [f for f in files if f.is_file()]
        dir_list = [d for d in files if d.is_dir()]
        
        total_size = sum(f.stat().st_size for f in file_list)

        return {
            "name": self.name,
            "path": str(base),
            "files": len(file_list),
            "dirs": len(dir_list),
            "size": total_size,
        }

    def add_observer(self, observer: Callable) -> None:
        """Add observer for module events"""
        self._observers.add(observer)

    def _notify_observers(self, event_type: str, data: Any) -> None:
        """Notify all observers of an event"""
        for observer in list(self._observers):
            try:
                observer(event_type, data, self)
            except Exception:
                continue

    @classmethod
    def get_registered_modules(cls) -> Dict[str, 'MakeModule']:
        """Get all registered modules"""
        return cls._global_registry._registry.copy()

# Get system site-packages path safely
try:
    SITE_PATH = Path(getsitepath()[0]) if getsitepath() else None
except (TypeError, IndexError):
    SITE_PATH = None
