import subprocess
import json
import sys
import os
from pathlib import Path
from typing import Dict, Any, Optional
import tempfile


class SubprocessSandbox:
    """Execute plugins in isolated subprocess with restricted environment"""
    
    def __init__(self, plugin_path: Path, permissions: Dict[str, Any]):
        self.plugin_path = plugin_path
        self.permissions = permissions
        self.process = None
    
    def execute_function(self, function_name: str, *args, **kwargs) -> Any:
        """Execute a plugin function in isolated subprocess"""
        sandbox_script = self._create_sandbox_script(function_name, args, kwargs)
        
        env = self._create_restricted_environment()
        
        result = subprocess.run(
            [sys.executable, '-c', sandbox_script],
            cwd=str(self.plugin_path),
            env=env,
            capture_output=True,
            text=True,
            timeout=30
        )
        
        if result.returncode != 0:
            raise RuntimeError(f"Plugin execution failed: {result.stderr}")
        
        try:
            return json.loads(result.stdout)
        except json.JSONDecodeError:
            return result.stdout
    
    def _create_sandbox_script(self, function_name: str, args: tuple, kwargs: dict) -> str:
        """Create Python script that runs in subprocess"""
        return f'''
import sys
import json
import os

sys.path.insert(0, {repr(str(self.plugin_path))})

try:
    import __init__ as plugin_module
    
    if not hasattr(plugin_module, {repr(function_name)}):
        raise ValueError(f"Function {repr(function_name)} not found in plugin")
    
    func = getattr(plugin_module, {repr(function_name)})
    
    args = {repr(args)}
    kwargs = {repr(kwargs)}
    
    result = func(*args, **kwargs)
    
    if result is not None:
        print(json.dumps(result))
    
except Exception as e:
    print(f"ERROR: {{e}}", file=sys.stderr)
    sys.exit(1)
'''
    
    def _create_restricted_environment(self) -> Dict[str, str]:
        """Create restricted environment variables"""
        env = {}
        
        safe_vars = [
            'PATH',
            'PYTHONPATH',
            'HOME',
            'USER',
            'LANG',
            'LC_ALL'
        ]
        
        for var in safe_vars:
            if var in os.environ:
                env[var] = os.environ[var]
        
        if not self.permissions.get('can_network', False):
            env['http_proxy'] = 'http://127.0.0.1:1'
            env['https_proxy'] = 'http://127.0.0.1:1'
        
        if not self.permissions.get('can_write_files', False):
            pass
        
        return env


class PluginValidator:
    """Validate plugins before installation"""
    
    def __init__(self):
        self.temp_dir = None
    
    def validate_plugin(self, plugin_archive: Path) -> tuple[bool, list[str]]:
        """Validate a plugin archive before installation"""
        errors = []
        
        if not self._check_archive_format(plugin_archive):
            errors.append("Invalid archive format")
            return False, errors
        
        with tempfile.TemporaryDirectory() as temp_dir:
            self.temp_dir = Path(temp_dir)
            
            if not self._extract_archive(plugin_archive):
                errors.append("Failed to extract archive")
                return False, errors
            
            if not self._validate_manifest():
                errors.append("Invalid or missing plugin.json manifest")
            
            if not self._validate_permissions():
                errors.append("Invalid permissions.json")
            
            if not self._run_safety_checks():
                errors.append("Plugin failed safety checks")
            
            if not self._verify_signature():
                errors.append("Plugin signature verification failed")
        
        return len(errors) == 0, errors
    
    def _check_archive_format(self, plugin_archive: Path) -> bool:
        """Check if archive is valid ZIP"""
        import zipfile
        return zipfile.is_zipfile(plugin_archive)
    
    def _extract_archive(self, plugin_archive: Path) -> bool:
        """Extract plugin archive"""
        import zipfile
        try:
            with zipfile.ZipFile(plugin_archive, 'r') as zf:
                zf.extractall(self.temp_dir)
            return True
        except Exception:
            return False
    
    def _validate_manifest(self) -> bool:
        """Validate plugin.json exists and is valid"""
        manifest_file = self.temp_dir / 'plugin.json'
        if not manifest_file.exists():
            return False
        
        try:
            with open(manifest_file, 'r') as f:
                manifest = json.load(f)
            
            required_fields = ['name', 'version', 'entry_point']
            return all(field in manifest for field in required_fields)
        except Exception:
            return False
    
    def _validate_permissions(self) -> bool:
        """Validate permissions.json if it exists"""
        perms_file = self.temp_dir / 'permissions.json'
        if not perms_file.exists():
            return True
        
        try:
            with open(perms_file, 'r') as f:
                perms = json.load(f)
            
            if 'allowed_modules' not in perms:
                return False
            
            return isinstance(perms['allowed_modules'], list)
        except Exception:
            return False
    
    def _run_safety_checks(self) -> bool:
        """Run basic safety checks on plugin code"""
        dangerous_patterns = [
            'eval(',
            'exec(',
            '__import__("os").system',
            'subprocess.call',
            'open(',
        ]
        
        for py_file in self.temp_dir.rglob('*.py'):
            try:
                with open(py_file, 'r') as f:
                    content = f.read()
                    
                    for pattern in dangerous_patterns:
                        if pattern in content:
                            return False
            except Exception:
                continue
        
        return True
    
    def _verify_signature(self) -> bool:
        """Verify plugin signature"""
        return True


plugin_validator = PluginValidator()
