"""Sandbox operations wrapper."""

import importlib
from typing import Optional

from .const import SANDBOX_APP_LABEL, SANDBOX_PROXY, SDK_PACKAGE
from .output import SandboxConnectError, SandboxCreateError
from .utils import print_error, print_info, print_success, print_warning

# Dynamic SDK import based on brand config
_sandbox_mod = importlib.import_module(SDK_PACKAGE + ".code_interpreter")
_api_mod = importlib.import_module(SDK_PACKAGE + ".core.sandbox.sandbox_api")
Sandbox = _sandbox_mod.Sandbox
SandboxQuery = _api_mod.SandboxQuery

SANDBOX_METADATA = {"app": SANDBOX_APP_LABEL, "long_running": "true"}
SANDBOX_KEEP_ALIVE = 50 * 365 * 24 * 3600  # 50 years, effectively permanent

# Build env dict only when a proxy is configured
if SANDBOX_PROXY:
    SANDBOX_ENVS = {
        "http_proxy": SANDBOX_PROXY,
        "https_proxy": SANDBOX_PROXY,
        "no_proxy": "localhost,127.0.0.1",
    }
else:
    SANDBOX_ENVS = {}


class SandboxManager:
    """Manages sandbox instances for OpenClaw."""

    def __init__(self, template_id: str = None, api_key: str = None):
        self.template_id = template_id
        self._api_key = api_key
        self._sandbox: Optional[Sandbox] = None

    def _api_opts(self) -> dict:
        """Return api_key kwarg if set, for passing to SDK calls."""
        if self._api_key:
            return {"api_key": self._api_key}
        return {}

    def create(self, template_id: str = None, timeout: int = None) -> Sandbox:
        """Create a new sandbox instance with app metadata.

        Raises SandboxCreateError on failure.
        """
        tid = template_id or self.template_id
        if not tid:
            raise SandboxCreateError("No template ID provided. Build a template first.")

        print_info(f"Creating sandbox from template: {tid}")
        try:
            create_kwargs = dict(
                timeout=timeout, metadata=SANDBOX_METADATA, **self._api_opts(),
            )
            if SANDBOX_ENVS:
                create_kwargs["envs"] = SANDBOX_ENVS
            self._sandbox = Sandbox.create(tid, **create_kwargs)
        except Exception as e:
            raise SandboxCreateError(f"Failed to create sandbox: {e}") from e

        print_success(f"Sandbox created: {self._sandbox.sandbox_id}")
        return self._sandbox

    def connect(self, sandbox_id: str) -> Sandbox:
        """Connect to an existing sandbox.

        Passes SANDBOX_KEEP_ALIVE to the SDK to prevent it from resetting
        the sandbox lifetime to its 5-minute default on every connect call.

        Raises SandboxConnectError on failure.
        """
        try:
            self._sandbox = Sandbox.connect(sandbox_id, timeout=SANDBOX_KEEP_ALIVE, **self._api_opts())
            return self._sandbox
        except Exception as e:
            raise SandboxConnectError(f"Failed to connect to sandbox {sandbox_id}: {e}") from e

    def run_command(self, command: str, timeout: int = 30, background: bool = False, envs: dict = None) -> Optional[str]:
        """Execute a command in the sandbox."""
        if not self._sandbox:
            print_error("No active sandbox. Create or connect first.")
            return None
        try:
            result = self._sandbox.commands.run(
                command, timeout=timeout, background=background, envs=envs,
            )
            return result
        except Exception as e:
            print_error(f"Command failed: {e}")
            return None

    def write_file(self, path: str, content: str):
        """Write a file to the sandbox filesystem."""
        if not self._sandbox:
            print_error("No active sandbox.")
            return
        self._sandbox.files.write(path, content)

    def read_file(self, path: str) -> Optional[str]:
        """Read a file from the sandbox filesystem."""
        if not self._sandbox:
            print_error("No active sandbox.")
            return None
        return self._sandbox.files.read(path)

    def upload_file(self, local_path: str, sandbox_path: str):
        """Upload a local file to the sandbox."""
        if not self._sandbox:
            print_error("No active sandbox.")
            return
        with open(local_path, "rb") as f:
            self._sandbox.files.write(sandbox_path, f)
        print_success(f"Uploaded {local_path} -> {sandbox_path}")

    def get_status(self) -> Optional[str]:
        """Check if the sandbox is alive by running a simple command."""
        if not self._sandbox:
            return None
        try:
            result = self._sandbox.commands.run("echo ok", timeout=5)
            return "running" if result else "unknown"
        except Exception:
            return "unreachable"

    def kill(self):
        """Terminate the current sandbox."""
        if not self._sandbox:
            print_error("No active sandbox.")
            return
        sandbox_id = self._sandbox.sandbox_id
        try:
            self._sandbox.kill()
        except Exception as e:
            print_warning(f"Error during sandbox termination: {e}")
        print_success(f"Sandbox terminated: {sandbox_id}")
        self._sandbox = None

    @property
    def sandbox(self) -> Optional[Sandbox]:
        return self._sandbox

    @property
    def sandbox_id(self) -> Optional[str]:
        return self._sandbox.sandbox_id if self._sandbox else None

    @staticmethod
    def list_app_sandboxes(api_key: str = None) -> list:
        """List all app sandboxes via API, filtered by metadata."""
        query = SandboxQuery(metadata=SANDBOX_METADATA)
        opts = {"api_key": api_key} if api_key else {}
        paginator = Sandbox.list(query=query, **opts)
        sandboxes = []
        while paginator.has_next:
            sandboxes.extend(paginator.next_items())
        return sandboxes
