from __future__ import annotations

from typing import Iterable, Optional, Protocol

from ..types import CommandResult, Execution, PTYHandle, SandboxHandle, SandboxStatus


class Adapter(Protocol):
	name: str

	# lifecycle
	def create(self, cfg: "SandboxConfig") -> SandboxHandle: ...
	def connect(self, sandbox_id: str) -> SandboxHandle: ...
	def shutdown(self, h: SandboxHandle) -> None: ...
	def status(self, h: SandboxHandle) -> SandboxStatus: ...
	def refresh_timeout(self, h: SandboxHandle, seconds: int) -> None: ...

	# filesystem
	def fs_write(self, h: SandboxHandle, path: str, data: bytes, text: bool) -> None: ...
	def fs_read(self, h: SandboxHandle, path: str, text: bool) -> bytes | str: ...
	def fs_ls(self, h: SandboxHandle, path: str) -> list: ...
	def fs_mkdir(self, h: SandboxHandle, path: str, parents: bool) -> None: ...
	def fs_rm(self, h: SandboxHandle, path: str, recursive: bool) -> None: ...
	def fs_mv(self, h: SandboxHandle, src: str, dst: str) -> None: ...
	def fs_put(self, h: SandboxHandle, local: str, remote: str) -> None: ...
	def fs_get(self, h: SandboxHandle, remote: str, local: str) -> None: ...

	# processes
	def run(self, h: SandboxHandle, cmd: list[str] | str, env: Optional[dict], timeout: Optional[int]) -> CommandResult: ...
	def pty_start(self, h: SandboxHandle, cmd: str, env: Optional[dict]) -> PTYHandle: ...
	def pty_send(self, pty: PTYHandle, data: bytes) -> None: ...
	def pty_stream(self, pty: PTYHandle) -> Iterable[bytes]: ...
	def pty_close(self, pty: PTYHandle) -> None: ...

	# code conveniences
	def run_code(self, h: SandboxHandle, code: str) -> Execution: ...
	def install_pkg(self, h: SandboxHandle, spec: str) -> CommandResult: ...

	# ports
	def expose(self, h: SandboxHandle, port: int, host_port: Optional[int], proto: str): ...


class SandboxConfig:
	def __init__(self, *, image: str, timeout: int, env: dict | None, workdir: str | None, metadata: dict | None, resources: Optional["Resources"] = None, volumes: Optional[list] = None, command: Optional[list[str]] = None, entrypoint: Optional[list[str]] = None, restart_policy: Optional[str] = None):
		self.image = image
		self.timeout = timeout
		self.env = env or {}
		self.workdir = workdir or "/workspace"
		self.metadata = metadata or {}
		self.resources = resources
		self.volumes = volumes or []
		self.command = command
		self.entrypoint = entrypoint
		self.restart_policy = restart_policy  # Restart policy: no, always, on-failure, unless-stopped


class Resources:
	def __init__(self, *, cpu_limit: float | None = None, mem_limit: str | None = None, pids_limit: int | None = None, network: str | None = "bridge"):
		self.cpu_limit = cpu_limit
		self.mem_limit = mem_limit
		self.pids_limit = pids_limit
		self.network = network


