"""Sandbox core implementation"""

import os
import sys
import tempfile
import subprocess
from typing import Optional

from diona.project.sandbox.config import SandboxConfig
from diona.project.sandbox.result import SandboxResult
from diona.project.sandbox.wrappers import generate_wrapper_code, register_default_wrappers

register_default_wrappers()


class Sandbox:
    """Python code execution sandbox using subprocess for isolation and timeout control."""
    
    def __init__(self, config: Optional[SandboxConfig] = None):
        self.config = config or SandboxConfig()
    
    def execute(self, content: str) -> SandboxResult:
        """Execute Python code and return result.
        
        Args:
            content: Python code to execute
            
        Returns:
            SandboxResult: Contains return value, stdout, stderr
        """
        return self.execute_with_timeout(content)
    
    def execute_with_timeout(self, content: str) -> SandboxResult:
        """Execute Python code with timeout control using subprocess.
        
        Args:
            content: Python code to execute
            
        Returns:
            SandboxResult: Contains return value, stdout, stderr
        """
        result = SandboxResult()
        
        wrapper_code = self._create_wrapper_code(content)
        
        with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False, encoding='utf-8') as f:
            f.write(wrapper_code)
            temp_file = f.name
        
        try:
            process = self._create_process(temp_file)
            
            try:
                stdout, stderr = process.communicate(timeout=self.config.timeout)
                result.stdout = stdout.decode('utf-8', errors='replace')
                result.stderr = stderr.decode('utf-8', errors='replace')
                result.stdout, result.stderr = self._parse_output(result.stdout, result.stderr)
            except subprocess.TimeoutExpired:
                process.kill()
                process.communicate()
                result.timed_out = True
                result.error = f"Execution timed out after {self.config.timeout} seconds"
        except Exception as e:
            result.error = str(e)
        finally:
            try:
                os.unlink(temp_file)
            except OSError:
                pass
        
        return result
    
    def _parse_output(self, stdout: str, stderr: str) -> tuple:
        """Parse output and remove markers."""
        start_marker = "__DIONA_RESULT_START__"
        end_marker = "__DIONA_RESULT_END__"
        
        stderr_start = stderr.find(start_marker)
        
        if stderr_start != -1:
            stderr_before = stderr[:stderr_start]
            stderr_after = stderr[stderr_start:]
            stderr_end = stderr_after.find(end_marker)
            
            if stderr_end != -1:
                content = stderr_after[len(start_marker):stderr_end]
                content = content.replace('\r\n', '\n').replace('\r', '').strip('\n')
                return "", stderr_before + content
        
        stdout_start = stdout.find(start_marker)
        stdout_end = stdout.find(end_marker)
        
        if stdout_start != -1 and stdout_end != -1:
            content = stdout[stdout_start + len(start_marker):stdout_end]
            content = content.replace('\r\n', '\n').replace('\r', '').strip('\n')
            return content, stderr
        
        return stdout, stderr
    
    def _create_wrapper_code(self, content: str) -> str:
        wrapper_code = generate_wrapper_code(self.config.env_vars)
        
        indented_content = '\n'.join('    ' + line for line in content.split('\n'))
        
        return f'''{wrapper_code}

_safe_globals = {{'__builtins__': builtins, '__name__': '__sandbox__', '__doc__': None}}

stdout_capture = io.StringIO()
stderr_capture = io.StringIO()
old_stdout = sys.stdout
old_stderr = sys.stderr
sys.stdout = stdout_capture
sys.stderr = stderr_capture

try:
{indented_content}
except SystemExit as e:
    pass
except Exception as e:
    print(f"Error: {{type(e).__name__}}: {{e}}", file=sys.stderr)

sys.stdout = old_stdout
sys.stderr = old_stderr

sys.stdout.write("__DIONA_RESULT_START__")
sys.stdout.write(stdout_capture.getvalue())
sys.stderr.write(stderr_capture.getvalue())
sys.stdout.write("__DIONA_RESULT_END__")
'''
    
    def _create_process(self, script_path: str) -> subprocess.Popen:
        env = os.environ.copy()
        
        if not self.config.allow_network:
            env['PYTHONHTTPSVERIFY'] = '1'
        
        creationflags = 0
        if sys.platform == 'win32':
            creationflags = subprocess.CREATE_NEW_PROCESS_GROUP
        
        return subprocess.Popen(
            [sys.executable, '-u', script_path],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            env=env,
            creationflags=creationflags,
            stdin=subprocess.DEVNULL
        )
