"""E2B API-based sandbox backend."""

import time
from typing import Any

from app.services.sandbox.base import BaseSandboxBackend, ExecutionResult, SandboxCapabilities
from app.services.sandbox.config import SandboxConfig
from loguru import logger

# Lazy import e2b to make it optional
_e2b = None


def _get_e2b():
    """Lazy load e2b SDK."""
    global _e2b
    if _e2b is None:
        try:
            import e2b
            _e2b = e2b
        except ImportError:
            raise ImportError(
                "e2b package is required for E2B backend. "
                "Install it with: pip install e2b"
            )
    return _e2b


# Language mapping for E2B
_LANGUAGE_MAP = {
    "python": "python",
    "bash": "bash",
    "node": "node",
    "javascript": "javascript",
}


class E2bBackend(BaseSandboxBackend):
    """E2B cloud-based sandbox backend.

    E2B (https://e2b.dev/) provides secure, cloud-based code execution
    with built-in isolation and networking.
    """

    name = "e2b"

    def __init__(self, config: SandboxConfig):
        self.config = config
        self._client = None

        if not config.api_key:
            raise ValueError("E2B API key is required. Set SANDBOX_API_KEY environment variable.")

    @property
    def client(self):
        """Get or create E2B client."""
        e2b_lib = _get_e2b()
        if self._client is None:
            self._client = e2b_lib.AsyncSandbox
        return self._client

    def get_capabilities(self) -> SandboxCapabilities:
        return SandboxCapabilities(
            supported_languages=["python", "bash", "node", "javascript"],
            max_timeout=self.config.max_timeout,
            max_memory_mb=512,
            network_available=True,
            filesystem_available=True,
        )

    async def health_check(self) -> bool:
        """Check if E2B service is available."""
        try:
            e2b_lib = _get_e2b()
            # Try to list sandboxes to verify API is accessible
            await e2b_lib.AsyncSandbox.list(api_key=self.config.api_key)
            return True
        except Exception:
            return False

    async def execute(
        self,
        code: str,
        language: str,
        timeout: int = 30,
        work_dir: str | None = None,
        **kwargs
    ) -> ExecutionResult:
        """Execute code using E2B cloud sandbox."""
        start_time = time.time()

        # Map language to E2B format
        e2b_language = _LANGUAGE_MAP.get(language, language)
        if language not in _LANGUAGE_MAP:
            return ExecutionResult(
                success=False,
                stdout="",
                stderr="",
                exit_code=1,
                duration_ms=int((time.time() - start_time) * 1000),
                error=f"Unsupported language: {language}"
            )

        e2b_lib = _get_e2b()

        try:
            # Create sandbox and run code
            async with await e2b_lib.AsyncSandbox.create(
                api_key=self.config.api_key,
                timeout=timeout,
            ) as sandbox:
                # Build the command based on language
                if e2b_language == "python":
                    cmd = "python3"
                    args = ["-c", code]
                elif e2b_language == "bash":
                    cmd = "bash"
                    args = ["-c", code]
                elif e2b_language in ("node", "javascript"):
                    cmd = "node"
                    args = ["-e", code]
                else:
                    return ExecutionResult(
                        success=False,
                        stdout="",
                        stderr="",
                        exit_code=1,
                        duration_ms=int((time.time() - start_time) * 1000),
                        error=f"Unsupported language: {language}"
                    )

                # Run the command - use string format for e2b
                cmd_str = f"{cmd} {args[0]} '{args[1]}'"
                result = await sandbox.commands.run(cmd_str)

            duration_ms = int((time.time() - start_time) * 1000)

            return ExecutionResult(
                success=result.exit_code == 0,
                stdout=result.stdout or "",
                stderr=result.stderr or "",
                exit_code=result.exit_code or 0,
                duration_ms=duration_ms,
                error=None
            )

        except Exception as e:
            duration_ms = int((time.time() - start_time) * 1000)
            error_msg = str(e)
            logger.exception(f"[E2B] Execution error")

            # Handle timeout
            if "timeout" in error_msg.lower():
                return ExecutionResult(
                    success=False,
                    stdout="",
                    stderr="",
                    exit_code=124,
                    duration_ms=duration_ms,
                    error=f"Code execution timed out after {timeout}s"
                )

            return ExecutionResult(
                success=False,
                stdout="",
                stderr="",
                exit_code=1,
                duration_ms=duration_ms,
                error=f"E2B execution error: {error_msg[:200]}"
            )