from frame import Frame, fOp as SystemOP

class MetaGenerator:
    """
    A dynamic metaclass generator using Frame system for runtime class modification.
    
    This class enables creating custom metaclasses with predefined behaviors,
    attributes, and methods that are automatically applied to all classes
    using the generated metaclass. It provides a fluent interface for
    building complex metaclass configurations.
    
    Key Features:
    - Fluent interface for method chaining
    - Pre/post class creation hooks
    - Dynamic attribute and method injection
    - Frame-based safe code execution
    
    Example:
        >>> generator = MetaGenerator("MyMeta")
        >>> meta = (generator
        ...     .add_attribute("version", "1.0")
        ...     .add_method("get_info", "lambda self: f'Instance of {self.__class__.__name__}'")
        ...     .generate())
        >>> 
        >>> class Example(metaclass=meta):
        ...     pass
        >>> obj = Example()
        >>> print(obj.version)  # "1.0"
        >>> print(obj.get_info())  # "Instance of Example"
    """
    
    def __init__(self, name="DynamicMeta", base_meta=type):
        """
        Initialize the MetaGenerator.
        
        Args:
            name (str): Name for the generated metaclass. Defaults to "DynamicMeta".
            base_meta (type): Base metaclass to inherit from. Defaults to built-in 'type'.
            
        Note:
            The base_meta should be a valid metaclass. For most use cases, 
            the default 'type' is sufficient.
        """
        self.name = name
        self.base_meta = base_meta
        self._hooks = {
            'pre_new': [],    # Code executed BEFORE class creation
            'post_new': [],   # Code executed AFTER class creation  
            'pre_init': [],   # Reserved for future use
            'post_init': []   # Reserved for future use
        }
    
    def add_pre_new_hook(self, hook_code: str):
        """
        Add code to execute BEFORE class creation.
        
        Args:
            hook_code (str): Python code as string to execute before __new__.
                           The code has access to 'cls', 'name', 'bases', 'attrs'.
                           
        Returns:
            MetaGenerator: Self instance for method chaining.
            
        Example:
            >>> generator.add_pre_new_hook('print(f"Creating class: {name}")')
            >>> generator.add_pre_new_hook('attrs["timestamp"] = datetime.now()')
        """
        self._hooks['pre_new'].append(hook_code)
        return self
    
    def add_post_new_hook(self, hook_code: str):
        """
        Add code to execute AFTER class creation.
        
        Args:
            hook_code (str): Python code as string to execute after __new__.
                           The code has access to 'new_class' (the created class).
                           
        Returns:
            MetaGenerator: Self instance for method chaining.
            
        Example:
            >>> generator.add_post_new_hook('print(f"Class {new_class.__name__} created")')
            >>> generator.add_post_new_hook('setattr(new_class, "_registered", True)')
        """
        self._hooks['post_new'].append(hook_code)
        return self
    
    def add_attribute(self, name: str, value):
        """
        Add an attribute to all classes using this metaclass.
        
        Args:
            name (str): Name of the attribute to add.
            value: Value of the attribute. Must be representable with repr().
            
        Returns:
            MetaGenerator: Self instance for method chaining.
            
        Note:
            The attribute is added to the class, not instances. For instance
            attributes, consider using __init__ hooks instead.
            
        Example:
            >>> generator.add_attribute("__version__", "1.0.0")
            >>> generator.add_attribute("author", "MetaSystem")
        """
        hook = f"attrs['{name}'] = {repr(value)}"
        self.add_pre_new_hook(hook)
        return self
    
    def add_method(self, name: str, method_code: str):
        """
        Add a method to all classes using this metaclass.
        
        Args:
            name (str): Name of the method to add.
            method_code (str): Python code representing the method.
                             Can be a function, lambda, or any callable.
                             
        Returns:
            MetaGenerator: Self instance for method chaining.
            
        Example:
            >>> generator.add_method('get_version', 'lambda self: self.__version__')
            >>> generator.add_method('describe', '''lambda self: f"{self.__class__.__name__}"''')
        """
        hook = f"attrs['{name}'] = {method_code}"
        self.add_pre_new_hook(hook)
        return self
    
    def generate(self):
        """
        Generate the final metaclass using Frame system.
        
        Returns:
            type: The generated metaclass ready for use.
            
        Raises:
            FrameError: If code execution in Frame fails.
            SyntaxError: If generated metaclass code has syntax errors.
            
        Note:
            This method uses Frame with safemode=False, allowing dynamic
            code execution. Ensure all hooks come from trusted sources.
            
        Example:
            >>> meta = generator.generate()
            >>> class MyClass(metaclass=meta):
            ...     pass
        """
        
        with Frame(safemode=False) as meta_frame:
            # Generate metaclass code with all hooks
            pre_new_code = "\n        ".join(self._hooks['pre_new'])
            post_new_code = "\n        ".join(self._hooks['post_new'])
            
            meta_code = f'''
class {self.name}({self.base_meta.__name__}):
    """
    Auto-generated metaclass by MetaGenerator.
    Applies custom behaviors during class creation.
    """
    
    def __new__(cls, name, bases, attrs):
        """Create a new class with injected behaviors."""
        # Execute pre-creation hooks
        {pre_new_code if pre_new_code else "# No pre-new hooks"}
        
        # Create the class using parent metaclass
        new_class = super().__new__(cls, name, bases, attrs)
        
        # Execute post-creation hooks
        {post_new_code if post_new_code else "# No post-new hooks"}
        
        return new_class
            '''
            
            # Execute the metaclass code in Frame
            meta_frame.Code(meta_code)
            meta_frame.Exec(add_localscope_to_vars=True)
            
            # Return the generated metaclass
            return meta_frame[self.name]

# Example usage with comprehensive demonstration
if __name__ == "__main__":
    # Create a metaclass with logging, versioning, and utility methods
    generator = MetaGenerator("LoggedMeta")
    
    meta = (generator
        .add_attribute("__version__", "1.0.0")
        .add_attribute("created_by", "MetaGenerator")
        .add_attribute("creation_timestamp", "2025-11-17")
        .add_pre_new_hook('print(f"Creating class: {name} with bases: {bases}")')
        .add_post_new_hook('print(f"Successfully created class: {new_class.__name__}")')
        .add_method('class_info', 'lambda self: f"{self.__class__.__name__} v{self.__version__}"')
        .add_method('get_metadata', '''lambda self: {
            "version": self.__version__,
            "created_by": self.created_by,
            "timestamp": self.creation_timestamp
        }''')
        .generate())
    
    # Demonstrate usage
    class MyClass(metaclass=meta):
        """Example class using the generated metaclass."""
        
        def __init__(self, value):
            self.value = value
            
        def get_value(self):
            return self.value
    
    # Test the functionality
    print("=== Testing Generated Metaclass ===")
    obj = MyClass(42)
    print(f"Class info: {obj.class_info()}")
    print(f"Created by: {obj.created_by}")
    print(f"Metadata: {obj.get_metadata()}")
    print(f"Value: {obj.get_value()}")
    print(f"Object type: {type(obj)}")
    print(f"Metaclass: {type(obj).__class__}")