import os
import sys
import json
from pathlib import Path
from typing import Dict, List, Any, Optional
import importlib.util
from dataclasses import dataclass


@dataclass
class PluginPermissions:
    """Plugin permission model"""
    allowed_modules: List[str]
    can_write_files: bool = False
    can_network: bool = False
    can_database: bool = True
    can_execute_commands: bool = False
    max_memory_mb: Optional[int] = None
    max_cpu_percent: Optional[int] = None


class PluginExecutor:
    """Execute plugins in a sandboxed environment with permission enforcement"""
    
    def __init__(self, plugins_dir: str = None):
        self.plugins_dir = Path(plugins_dir or os.path.expanduser('~/.feather/plugins'))
        self.loaded_plugins = {}
        self.permissions = {}
    
    def load_plugin(self, plugin_name: str) -> bool:
        """Load a plugin with permission checking"""
        plugin_path = self.plugins_dir / plugin_name
        
        if not plugin_path.exists():
            raise ValueError(f"Plugin {plugin_name} not found")
        
        permissions_file = plugin_path / 'permissions.json'
        if permissions_file.exists():
            with open(permissions_file, 'r') as f:
                perms_data = json.load(f)
                self.permissions[plugin_name] = PluginPermissions(**perms_data)
        else:
            self.permissions[plugin_name] = PluginPermissions(
                allowed_modules=['feather', 'datetime', 'json']
            )
        
        manifest_file = plugin_path / 'plugin.json'
        if not manifest_file.exists():
            raise ValueError(f"Plugin {plugin_name} missing plugin.json manifest")
        
        with open(manifest_file, 'r') as f:
            manifest = json.load(f)
        
        entry_point = manifest.get('entry_point', '__init__.py')
        plugin_file = plugin_path / entry_point
        
        if not plugin_file.exists():
            raise ValueError(f"Plugin entry point {entry_point} not found")
        
        spec = importlib.util.spec_from_file_location(
            f"feather_plugin_{plugin_name}",
            plugin_file
        )
        module = importlib.util.module_from_spec(spec)
        
        restricted_globals = self._create_restricted_globals(plugin_name)
        module.__dict__.update(restricted_globals)
        
        spec.loader.exec_module(module)
        
        self.loaded_plugins[plugin_name] = module
        return True
    
    def _create_restricted_globals(self, plugin_name: str) -> Dict[str, Any]:
        """Create restricted global environment for plugin"""
        permissions = self.permissions[plugin_name]
        
        restricted = {
            '__builtins__': self._create_restricted_builtins(permissions),
            '__name__': f'feather_plugin_{plugin_name}',
            '__file__': str(self.plugins_dir / plugin_name),
        }
        
        for module_name in permissions.allowed_modules:
            try:
                restricted[module_name] = __import__(module_name)
            except ImportError:
                pass
        
        return restricted
    
    def _create_restricted_builtins(self, permissions: PluginPermissions) -> dict:
        """Create restricted builtins based on permissions"""
        safe_builtins = {
            'print': print,
            'len': len,
            'range': range,
            'str': str,
            'int': int,
            'float': float,
            'bool': bool,
            'list': list,
            'dict': dict,
            'tuple': tuple,
            'set': set,
            'enumerate': enumerate,
            'zip': zip,
            'map': map,
            'filter': filter,
            'sorted': sorted,
            'sum': sum,
            'min': min,
            'max': max,
            'abs': abs,
            'isinstance': isinstance,
            'hasattr': hasattr,
            'getattr': getattr,
            'setattr': setattr,
            'dir': dir,
            'type': type,
            'Exception': Exception,
            'ValueError': ValueError,
            'TypeError': TypeError,
            'KeyError': KeyError,
            'IndexError': IndexError,
            '__import__': self._create_restricted_import(permissions),
            'open': self._create_restricted_open(permissions),
        }
        
        return safe_builtins
    
    def _create_restricted_import(self, permissions: PluginPermissions):
        """Create restricted import function"""
        def restricted_import(name, *args, **kwargs):
            if name not in permissions.allowed_modules:
                raise ImportError(f"Module '{name}' is not allowed by plugin permissions")
            
            if name == 'os' and not permissions.can_execute_commands:
                raise ImportError("Module 'os' requires execute_commands permission")
            
            if name in ['socket', 'urllib', 'requests'] and not permissions.can_network:
                raise ImportError(f"Module '{name}' requires network permission")
            
            return __import__(name, *args, **kwargs)
        
        return restricted_import
    
    def _create_restricted_open(self, permissions: PluginPermissions):
        """Create restricted file open function"""
        def restricted_open(file, mode='r', *args, **kwargs):
            if not permissions.can_write_files:
                if 'w' in mode or 'a' in mode or '+' in mode or 'x' in mode:
                    raise PermissionError(
                        f"Plugin does not have write permission (mode: {mode})"
                    )
            
            plugin_dir = str(self.plugins_dir)
            file_path = os.path.abspath(file)
            
            if not file_path.startswith(plugin_dir):
                raise PermissionError(
                    f"Plugin can only access files within {plugin_dir}"
                )
            
            return open(file, mode, *args, **kwargs)
        
        return restricted_open
    
    def execute_plugin_function(self, plugin_name: str, function_name: str, *args, **kwargs):
        """Execute a function from a loaded plugin"""
        if plugin_name not in self.loaded_plugins:
            raise ValueError(f"Plugin {plugin_name} not loaded")
        
        plugin_module = self.loaded_plugins[plugin_name]
        
        if not hasattr(plugin_module, function_name):
            raise ValueError(f"Function {function_name} not found in plugin {plugin_name}")
        
        func = getattr(plugin_module, function_name)
        
        return func(*args, **kwargs)
    
    def unload_plugin(self, plugin_name: str):
        """Unload a plugin"""
        if plugin_name in self.loaded_plugins:
            del self.loaded_plugins[plugin_name]
        if plugin_name in self.permissions:
            del self.permissions[plugin_name]
    
    def check_permission(self, plugin_name: str, permission: str) -> bool:
        """Check if plugin has a specific permission"""
        if plugin_name not in self.permissions:
            return False
        
        perms = self.permissions[plugin_name]
        
        permission_map = {
            'write_files': perms.can_write_files,
            'network': perms.can_network,
            'database': perms.can_database,
            'execute_commands': perms.can_execute_commands,
        }
        
        return permission_map.get(permission, False)


plugin_executor = PluginExecutor()
