"""
E2B Sandbox implementation.

This module provides a concrete implementation of BaseSandbox using E2B.
"""

import os
from typing import Any, TYPE_CHECKING

from openspace.utils.logging import Logger
from .sandbox import BaseSandbox
from ..types import SandboxOptions

logger = Logger.get_logger(__name__)

# Import E2B SDK components (optional dependency)
if TYPE_CHECKING:
    # For type checking purposes only
    try:
        from e2b_code_interpreter import CommandHandle, Sandbox
    except ImportError:
        CommandHandle = None  # type: ignore
        Sandbox = None  # type: ignore

try:
    logger.debug("Attempting to import e2b_code_interpreter...")
    from e2b_code_interpreter import (  # type: ignore
        CommandHandle,
        Sandbox,
    )
    logger.debug("Successfully imported e2b_code_interpreter")
    E2B_AVAILABLE = True
except ImportError as e:
    logger.debug(f"Failed to import e2b_code_interpreter: {e}")
    CommandHandle = None  # type: ignore
    Sandbox = None  # type: ignore
    E2B_AVAILABLE = False


class E2BSandbox(BaseSandbox):
    """E2B sandbox implementation for secure code execution."""
    
    def __init__(self, options: SandboxOptions):
        """Initialize E2B sandbox.
        
        Args:
            options: Sandbox configuration options including:
                - api_key: E2B API key (or use E2B_API_KEY env var)
                - sandbox_template_id: Template ID for the sandbox (default: "base")
                - timeout: Command execution timeout in seconds
        """
        super().__init__(options)
        
        if not E2B_AVAILABLE:
            raise ImportError(
                "E2B SDK (e2b-code-interpreter) not found. Please install it with "
                "'pip install e2b-code-interpreter'."
            )
        
        # Get API key from options or environment
        self.api_key = options.get("api_key") or os.environ.get("E2B_API_KEY")
        if not self.api_key:
            raise ValueError(
                "E2B API key is required. Provide it via 'options.api_key'"
                " or the E2B_API_KEY environment variable."
            )
        
        # Get sandbox configuration
        self.sandbox_template_id = options.get("sandbox_template_id", "base")
        self.timeout = options.get("timeout", 600)  # Default 10 minutes
        
        # Sandbox instance (using Any to avoid import issues with optional dependency)
        self._sandbox: Any = None
        self._process: Any = None
        
    async def start(self) -> bool:
        """Start the E2B sandbox instance.
        
        Returns:
            True if sandbox started successfully, False otherwise.
        """
        if self._active:
            logger.debug("E2B sandbox already active")
            return True
        
        try:
            logger.debug(f"Creating E2B sandbox with template: {self.sandbox_template_id}")
            self._sandbox = Sandbox(
                template=self.sandbox_template_id,
                api_key=self.api_key,
            )
            self._active = True
            logger.info(f"E2B sandbox started successfully (template: {self.sandbox_template_id})")
            return True
            
        except Exception as e:
            logger.error(f"Failed to start E2B sandbox: {e}")
            self._active = False
            return False
    
    async def stop(self) -> None:
        """Stop the E2B sandbox instance."""
        if not self._active:
            logger.debug("E2B sandbox not active")
            return
        
        try:
            # Terminate any running process
            if self._process:
                try:
                    logger.debug("Terminating sandbox process")
                    self._process.kill()
                except Exception as e:
                    logger.warning(f"Error terminating sandbox process: {e}")
                finally:
                    self._process = None
            
            # Close the sandbox
            if self._sandbox:
                try:
                    logger.debug("Closing E2B sandbox instance")
                    self._sandbox.kill()
                    logger.info("E2B sandbox stopped successfully")
                except Exception as e:
                    logger.warning(f"Error closing E2B sandbox: {e}")
                finally:
                    self._sandbox = None
            
            self._active = False
            
        except Exception as e:
            logger.error(f"Error stopping E2B sandbox: {e}")
            raise
    
    async def execute_safe(self, command: str, **kwargs) -> Any:
        """Execute a command safely in the E2B sandbox.
        
        Args:
            command: The command to execute
            **kwargs: Additional options:
                - envs: Environment variables (dict)
                - timeout: Command timeout in milliseconds
                - background: Run in background (bool)
                - on_stdout: Stdout callback function
                - on_stderr: Stderr callback function
        
        Returns:
            CommandHandle object representing the running process
        """
        if not self._active or not self._sandbox:
            raise RuntimeError("E2B sandbox is not active. Call start() first.")
        
        try:
            # Extract execution options
            envs = kwargs.get("envs", {})
            timeout = kwargs.get("timeout", self.timeout * 1000)  # Convert to ms
            background = kwargs.get("background", False)
            on_stdout = kwargs.get("on_stdout")
            on_stderr = kwargs.get("on_stderr")
            
            logger.debug(f"Executing command in E2B sandbox: {command}")
            
            # Execute the command
            self._process = self._sandbox.commands.run(
                command,
                envs=envs,
                timeout=timeout,
                background=background,
                on_stdout=on_stdout,
                on_stderr=on_stderr,
            )
            
            return self._process
            
        except Exception as e:
            logger.error(f"Failed to execute command in E2B sandbox: {e}")
            raise
    
    def get_connector(self) -> Any:
        """Get the underlying E2B sandbox connector.
        
        Returns:
            The E2B Sandbox instance, or None if not active.
        """
        return self._sandbox
    
    def get_host(self, port: int) -> str:
        """Get the host URL for a specific port.
        
        Args:
            port: The port number to get the host for
            
        Returns:
            The host URL string
            
        Raises:
            RuntimeError: If sandbox is not active
        """
        if not self._active or not self._sandbox:
            raise RuntimeError("E2B sandbox is not active. Call start() first.")
        
        return self._sandbox.get_host(port)
    
    @property
    def sandbox(self) -> Any:
        """Get the underlying E2B Sandbox instance."""
        return self._sandbox
    
    @property
    def process(self) -> Any:
        """Get the current running process handle."""
        return self._process

