# adapters/e2b.py

from __future__ import annotations
from typing import Any
from ..core.types import ExecResult, ExecOpts
from ..core.shell import shell_escape, fire_and_forget


def e2b(sandbox: Any) -> _E2BAdapter:
    return _E2BAdapter(sandbox)


class _E2BAdapter:
    def __init__(self, sandbox: Any) -> None:
        self._sb = sandbox

    async def exec(
        self,
        cmd: str,
        args: list[str] | None = None,
        opts: ExecOpts | None = None,
    ) -> ExecResult:
        opts = opts or ExecOpts()
        command = shell_escape(cmd, args)
        user = "root" if opts.sudo else "user"

        try:
            if opts.detached:
                # Use E2B's native background mode instead of nohup shell trick
                fire_and_forget(
                    self._sb.commands.run(
                        command,
                        background=True,
                        cwd=opts.cwd,
                        user=user,
                    )
                )
                return ExecResult(stdout="", stderr="", exit_code=0)

            result = await self._sb.commands.run(
                command,
                cwd=opts.cwd,
                user=user,
            )
            return ExecResult(
                stdout=result.stdout or "",
                stderr=result.stderr or "",
                exit_code=result.exit_code,
            )
        except Exception as e:
            # E2B throws CommandExitError for non-zero exits
            return ExecResult(
                stdout=getattr(e, "stdout", "") or "",
                stderr=getattr(e, "stderr", "") or getattr(e, "message", str(e)),
                exit_code=getattr(e, "exit_code", 1),
            )

    async def write_file(self, path: str, content: str | bytes, *, sudo: bool = False) -> None:
        # Use user="root" for privileged paths during provisioning
        user = "root" if sudo else "user"
        await self._sb.files.write(path, content, user=user)

    async def read_file(self, path: str) -> str:
        content: str = await self._sb.files.read(path)
        return content

    async def stop(self) -> None:
        await self._sb.kill()

    async def file_exists(self, path: str) -> bool:
        result = await self.exec("test", ["-f", path])
        return result.exit_code == 0
