"""Sandboxes resource -- lifecycle, exec, files, skills, and images."""

from __future__ import annotations

import builtins
from typing import Any
from uuid import UUID

from mcpgateway_sdk._http import HTTPClient
from mcpgateway_sdk.models.sandbox import ExecResult, Sandbox, SandboxFile

_ENDPOINTS = {
    "list": ("GET", "/api/v1/sandboxes"),
    "create": ("POST", "/api/v1/sandboxes"),
    "get": ("GET", "/api/v1/sandboxes/{sandbox_id}"),
    "update": ("PATCH", "/api/v1/sandboxes/{sandbox_id}"),
    "destroy": ("DELETE", "/api/v1/sandboxes/{sandbox_id}"),
    "exec": ("POST", "/api/v1/sandboxes/{sandbox_id}/exec"),
    "stop": ("POST", "/api/v1/sandboxes/{sandbox_id}/stop"),
    "resume": ("POST", "/api/v1/sandboxes/{sandbox_id}/resume"),
    "activity": ("GET", "/api/v1/sandboxes/{sandbox_id}/activity"),
    "list_files": ("GET", "/api/v1/sandboxes/{sandbox_id}/files"),
    "download": ("GET", "/api/v1/sandboxes/{sandbox_id}/files/{file_path}"),
    "upload": ("PUT", "/api/v1/sandboxes/{sandbox_id}/files/{file_path}"),
    "install_skills": ("POST", "/api/v1/sandboxes/{sandbox_id}/skills"),
    "list_images": ("GET", "/api/v1/sandbox-images"),
    "image_templates": ("GET", "/api/v1/sandbox-images/templates"),
    "package_catalog": ("GET", "/api/v1/sandbox-images/packages"),
}

# Type aliases to avoid shadowing builtin ``list`` inside the class.
_DictResult = dict[str, Any]
_ListDictResult = builtins.list[dict[str, Any]]


class SandboxesResource:
    """Interact with sandboxes managed by the MCP Gateway."""

    _endpoints = _ENDPOINTS

    def __init__(self, http: HTTPClient) -> None:
        self._http = http

    # ------------------------------------------------------------------
    # CRUD
    # ------------------------------------------------------------------

    async def list(self) -> builtins.list[Sandbox]:
        """List all sandboxes.

        Returns:
            A list of Sandbox models.
        """
        data = await self._http.request("GET", "/api/v1/sandboxes")
        return [Sandbox.model_validate(item) for item in data.get("items", [])]

    async def create(
        self,
        image: str = "mcpgateway/sandbox-runtime:latest",
        **kwargs: Any,
    ) -> Sandbox:
        """Create a new sandbox.

        Args:
            image: Container image to use.
            **kwargs: Additional fields (idle_timeout_seconds, memory_mb, etc.).

        Returns:
            The created Sandbox model.
        """
        body: dict[str, Any] = {"image": image, **kwargs}
        data = await self._http.request("POST", "/api/v1/sandboxes", json=body)
        return Sandbox.model_validate(data)

    async def get(self, sandbox_id: str | UUID) -> Sandbox:
        """Get a single sandbox by ID.

        Args:
            sandbox_id: The sandbox UUID.

        Returns:
            The Sandbox model.
        """
        data = await self._http.request("GET", f"/api/v1/sandboxes/{sandbox_id}")
        return Sandbox.model_validate(data)

    async def update(self, sandbox_id: str | UUID, **fields: Any) -> Sandbox:
        """Partially update a sandbox. Only provided fields are sent.

        Args:
            sandbox_id: The sandbox UUID.
            **fields: Fields to update (idle_timeout_seconds, memory_mb, etc.).

        Returns:
            The updated Sandbox model.
        """
        data = await self._http.request("PATCH", f"/api/v1/sandboxes/{sandbox_id}", json=fields)
        return Sandbox.model_validate(data)

    async def destroy(self, sandbox_id: str | UUID) -> None:
        """Delete a sandbox.

        Args:
            sandbox_id: The sandbox UUID.
        """
        await self._http.request("DELETE", f"/api/v1/sandboxes/{sandbox_id}")

    # ------------------------------------------------------------------
    # Execution
    # ------------------------------------------------------------------

    async def exec(
        self,
        sandbox_id: str | UUID,
        command: str,
        timeout: int = 30,
    ) -> ExecResult:
        """Execute a command in a sandbox.

        Args:
            sandbox_id: The sandbox UUID.
            command: Shell command to execute.
            timeout: Timeout in seconds (default 30).

        Returns:
            An ExecResult with exit_code, stdout, stderr, duration_ms.
        """
        data = await self._http.request(
            "POST",
            f"/api/v1/sandboxes/{sandbox_id}/exec",
            json={"command": command, "timeout_seconds": timeout},
        )
        return ExecResult.model_validate(data)

    # ------------------------------------------------------------------
    # Lifecycle
    # ------------------------------------------------------------------

    async def stop(self, sandbox_id: str | UUID) -> None:
        """Stop a running sandbox.

        Args:
            sandbox_id: The sandbox UUID.
        """
        await self._http.request("POST", f"/api/v1/sandboxes/{sandbox_id}/stop")

    async def resume(self, sandbox_id: str | UUID) -> Sandbox:
        """Resume a stopped sandbox.

        Args:
            sandbox_id: The sandbox UUID.

        Returns:
            The resumed Sandbox model.
        """
        data = await self._http.request("POST", f"/api/v1/sandboxes/{sandbox_id}/resume")
        return Sandbox.model_validate(data)

    async def activity(self, sandbox_id: str | UUID) -> _ListDictResult:
        """Get activity log for a sandbox.

        Args:
            sandbox_id: The sandbox UUID.

        Returns:
            A raw list of activity dicts.
        """
        data: Any = await self._http.request("GET", f"/api/v1/sandboxes/{sandbox_id}/activity")
        if isinstance(data, dict):
            raw = data.get("items") or data.get("activity") or []
            items: _ListDictResult = builtins.list(raw)
            return items
        return builtins.list(data) if data else []

    # ------------------------------------------------------------------
    # Files
    # ------------------------------------------------------------------

    async def list_files(
        self,
        sandbox_id: str | UUID,
        path: str = "/workspace",
    ) -> builtins.list[SandboxFile]:
        """List files in a sandbox directory.

        Args:
            sandbox_id: The sandbox UUID.
            path: Directory path to list (default ``/workspace``).

        Returns:
            A list of SandboxFile models.
        """
        data: Any = await self._http.request(
            "GET",
            f"/api/v1/sandboxes/{sandbox_id}/files",
            params={"path": path},
        )
        items = data.get("items", data) if isinstance(data, dict) else data
        return [SandboxFile.model_validate(item) for item in (items or [])]

    async def download(
        self,
        sandbox_id: str | UUID,
        file_path: str,
    ) -> bytes:
        """Download a file from a sandbox.

        Args:
            sandbox_id: The sandbox UUID.
            file_path: Path to the file within the sandbox.

        Returns:
            Raw bytes of the file content.
        """
        return await self._http.request_raw(
            "GET", f"/api/v1/sandboxes/{sandbox_id}/files/{file_path}"
        )

    async def upload(
        self,
        sandbox_id: str | UUID,
        file_path: str,
        content: bytes,
    ) -> None:
        """Upload a file to a sandbox.

        Args:
            sandbox_id: The sandbox UUID.
            file_path: Destination path within the sandbox.
            content: Raw bytes of the file content.
        """
        await self._http.request(
            "PUT",
            f"/api/v1/sandboxes/{sandbox_id}/files/{file_path}",
            content=content,
        )

    # ------------------------------------------------------------------
    # Skills
    # ------------------------------------------------------------------

    async def install_skills(
        self,
        sandbox_id: str | UUID,
        skill_ids: builtins.list[str | UUID],
    ) -> _DictResult:
        """Install skills into a sandbox.

        Args:
            sandbox_id: The sandbox UUID.
            skill_ids: List of skill UUIDs to install.

        Returns:
            A dict with installation results.
        """
        result: _DictResult = await self._http.request(
            "POST",
            f"/api/v1/sandboxes/{sandbox_id}/skills",
            json={"skill_ids": [str(s) for s in skill_ids]},
        )
        return result

    # ------------------------------------------------------------------
    # Images
    # ------------------------------------------------------------------

    async def list_images(self) -> _ListDictResult:
        """List available sandbox images.

        Returns:
            A list of image dicts.
        """
        data = await self._http.request("GET", "/api/v1/sandbox-images")
        if isinstance(data, builtins.list):
            return builtins.list(data)
        return builtins.list(data.get("items", []))

    async def image_templates(self) -> _ListDictResult:
        """List sandbox image templates.

        Returns:
            A list of template dicts.
        """
        data: Any = await self._http.request("GET", "/api/v1/sandbox-images/templates")
        if isinstance(data, dict):
            raw = data.get("templates") or data.get("items") or []
            items: _ListDictResult = builtins.list(raw)
            return items
        return builtins.list(data) if data else []

    async def package_catalog(self) -> _ListDictResult:
        """List available packages for sandbox images.

        Returns:
            A list of package/category dicts.
        """
        data: Any = await self._http.request("GET", "/api/v1/sandbox-images/packages")
        if isinstance(data, dict):
            raw = data.get("categories") or data.get("items") or []
            items: _ListDictResult = builtins.list(raw)
            return items
        return builtins.list(data) if data else []
