"""Servers resource -- CRUD, lifecycle, tools, credentials, OAuth, and catalog."""

from __future__ import annotations

import builtins
from typing import Any
from uuid import UUID

from mcpgateway_sdk._http import HTTPClient
from mcpgateway_sdk.models.server import Server

_ENDPOINTS = {
    # CRUD (5)
    "list": ("GET", "/api/v1/servers"),
    "get": ("GET", "/api/v1/servers/{server_id}"),
    "create": ("POST", "/api/v1/servers"),
    "update": ("PUT", "/api/v1/servers/{server_id}"),
    "delete": ("DELETE", "/api/v1/servers/{server_id}"),
    # Lifecycle (5)
    "start": ("POST", "/api/v1/servers/{server_id}/start"),
    "stop": ("POST", "/api/v1/servers/{server_id}/stop"),
    "health": ("POST", "/api/v1/servers/{server_id}/health"),
    "health_all": ("POST", "/api/v1/servers/health/all"),
    "bulk_action": ("POST", "/api/v1/servers/bulk-action"),
    # Tools on server (7)
    "list_tools": ("GET", "/api/v1/servers/{server_id}/tools"),
    "update_tool_overrides": (
        "PATCH",
        "/api/v1/servers/{server_id}/tools/{tool_id}/overrides",
    ),
    "reset_tool_overrides": (
        "DELETE",
        "/api/v1/servers/{server_id}/tools/{tool_id}/overrides",
    ),
    "correct_tool_schema": (
        "POST",
        "/api/v1/servers/{server_id}/tools/{tool_id}/correct-schema",
    ),
    "get_tool_selection": ("GET", "/api/v1/servers/{server_id}/tool-selection"),
    "update_tool_selection": ("PATCH", "/api/v1/servers/{server_id}/tool-selection"),
    "sync_tools": ("POST", "/api/v1/servers/{server_id}/sync"),
    # Local tools (3)
    "create_tool": ("POST", "/api/v1/servers/{server_id}/tools"),
    "update_tool": ("PATCH", "/api/v1/servers/{server_id}/tools/{tool_id}"),
    "delete_tool": ("DELETE", "/api/v1/servers/{server_id}/tools/{tool_id}"),
    # Credentials (3)
    "list_credentials": ("GET", "/api/v1/servers/{server_id}/credentials"),
    "create_credential": ("POST", "/api/v1/servers/{server_id}/credentials"),
    "delete_credential": (
        "DELETE",
        "/api/v1/servers/{server_id}/credentials/{credential_id}",
    ),
    # OAuth config (3)
    "get_oauth_config": ("GET", "/api/v1/servers/{server_id}/oauth-config"),
    "configure_oauth": ("POST", "/api/v1/servers/{server_id}/oauth-config"),
    "delete_oauth_config": ("DELETE", "/api/v1/servers/{server_id}/oauth-config"),
    # Connection (1)
    "get_connection_options": ("GET", "/api/v1/servers/{server_id}/connection-options"),
    # Catalog (3)
    "catalog_search": ("GET", "/api/v1/catalog/search"),
    "catalog_get": ("GET", "/api/v1/catalog/{registry}/{registry_id}"),
    "import_from_catalog": ("POST", "/api/v1/servers/catalog-imports"),
}

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


class ServersResource:
    """Interact with MCP servers managed by the gateway."""

    _endpoints = _ENDPOINTS

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

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

    async def list(
        self,
        search: str | None = None,
        status: str | None = None,
        type: str | None = None,
        page: int = 1,
        page_size: int = 100,
    ) -> builtins.list[Server]:
        """List servers with optional filters.

        Args:
            search: Free-text search string.
            status: Filter by server status.
            type: Filter by server type.
            page: Page number (1-based).
            page_size: Maximum results per page.

        Returns:
            A list of Server models.
        """
        params: dict[str, Any] = {"page": page, "page_size": page_size}
        if search is not None:
            params["search"] = search
        if status is not None:
            params["status"] = status
        if type is not None:
            params["type"] = type

        data = await self._http.request("GET", "/api/v1/servers", params=params)
        items = data.get("items", []) if isinstance(data, dict) else data
        return [Server.model_validate(item) for item in items]

    async def get(self, server_id: str | UUID) -> Server:
        """Get a single server by ID.

        Args:
            server_id: The server UUID.

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

    async def create(self, **fields: Any) -> Server:
        """Create a new server.

        Args:
            **fields: Server fields (name, type, config, etc.).

        Returns:
            The created Server model.
        """
        data = await self._http.request("POST", "/api/v1/servers", json=fields)
        return Server.model_validate(data)

    async def update(self, server_id: str | UUID, **fields: Any) -> Server:
        """Update a server.

        Args:
            server_id: The server UUID.
            **fields: Fields to update.

        Returns:
            The updated Server model.
        """
        data = await self._http.request("PUT", f"/api/v1/servers/{server_id}", json=fields)
        return Server.model_validate(data)

    async def delete(self, server_id: str | UUID) -> None:
        """Delete a server.

        Args:
            server_id: The server UUID.
        """
        await self._http.request("DELETE", f"/api/v1/servers/{server_id}")

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

    async def start(self, server_id: str | UUID) -> _DictResult:
        """Start a server.

        The backend returns a ``HealthCheckResponse`` (server_id, server_name,
        current_status, tools_count, ...), **not** a full Server model.

        Args:
            server_id: The server UUID.

        Returns:
            Health-check style dict with start result.
        """
        data: _DictResult = await self._http.request("POST", f"/api/v1/servers/{server_id}/start")
        return data

    async def stop(self, server_id: str | UUID) -> _DictResult:
        """Stop a server.

        The backend returns a ``HealthCheckResponse`` (server_id, server_name,
        current_status, tools_count, ...), **not** a full Server model.

        Args:
            server_id: The server UUID.

        Returns:
            Health-check style dict with stop result.
        """
        data: _DictResult = await self._http.request("POST", f"/api/v1/servers/{server_id}/stop")
        return data

    async def health(self, server_id: str | UUID) -> _DictResult:
        """Check health of a single server.

        Args:
            server_id: The server UUID.

        Returns:
            Health status dict.
        """
        result: _DictResult = await self._http.request(
            "POST", f"/api/v1/servers/{server_id}/health"
        )
        return result

    async def health_all(self) -> _DictResult:
        """Check health of all servers.

        Returns:
            Aggregated health results dict.
        """
        result: _DictResult = await self._http.request("POST", "/api/v1/servers/health/all")
        return result

    async def bulk_action(
        self,
        action: str,
        server_ids: builtins.list[str | UUID],
    ) -> _DictResult:
        """Perform a bulk action on multiple servers.

        Args:
            action: The action to perform (start, stop, restart, etc.).
            server_ids: List of server UUIDs.

        Returns:
            Result dict with affected count.
        """
        result: _DictResult = await self._http.request(
            "POST",
            "/api/v1/servers/bulk-action",
            json={
                "action": action,
                "server_ids": [str(s) for s in server_ids],
            },
        )
        return result

    # ------------------------------------------------------------------
    # Tools on server
    # ------------------------------------------------------------------

    async def list_tools(self, server_id: str | UUID) -> _ListDictResult:
        """List tools registered on a server.

        Args:
            server_id: The server UUID.

        Returns:
            A list of tool dicts.
        """
        data = await self._http.request("GET", f"/api/v1/servers/{server_id}/tools")
        if isinstance(data, builtins.dict):
            return builtins.list(data.get("items", []))
        return builtins.list(data)

    async def update_tool_overrides(
        self,
        server_id: str | UUID,
        tool_id: str | UUID,
        overrides: dict[str, Any],
    ) -> _DictResult:
        """Update overrides for a tool on a server.

        Args:
            server_id: The server UUID.
            tool_id: The tool UUID.
            overrides: Override fields dict.

        Returns:
            Updated overrides dict.
        """
        result: _DictResult = await self._http.request(
            "PATCH",
            f"/api/v1/servers/{server_id}/tools/{tool_id}/overrides",
            json=overrides,
        )
        return result

    async def reset_tool_overrides(
        self,
        server_id: str | UUID,
        tool_id: str | UUID,
    ) -> None:
        """Reset overrides for a tool on a server.

        Args:
            server_id: The server UUID.
            tool_id: The tool UUID.
        """
        await self._http.request(
            "DELETE",
            f"/api/v1/servers/{server_id}/tools/{tool_id}/overrides",
        )

    async def correct_tool_schema(
        self,
        server_id: str | UUID,
        tool_id: str | UUID,
    ) -> _DictResult:
        """Correct the schema of a tool using AI.

        Args:
            server_id: The server UUID.
            tool_id: The tool UUID.

        Returns:
            Correction result dict.
        """
        result: _DictResult = await self._http.request(
            "POST",
            f"/api/v1/servers/{server_id}/tools/{tool_id}/correct-schema",
        )
        return result

    async def get_tool_selection(self, server_id: str | UUID) -> _DictResult:
        """Get the tool selection config for a server.

        Args:
            server_id: The server UUID.

        Returns:
            Tool selection config dict.
        """
        result: _DictResult = await self._http.request(
            "GET", f"/api/v1/servers/{server_id}/tool-selection"
        )
        return result

    async def update_tool_selection(
        self,
        server_id: str | UUID,
        tools: dict[str, Any],
    ) -> _DictResult:
        """Update the tool selection config for a server.

        Args:
            server_id: The server UUID.
            tools: Tool selection config to set.

        Returns:
            Updated tool selection config dict.
        """
        result: _DictResult = await self._http.request(
            "PATCH",
            f"/api/v1/servers/{server_id}/tool-selection",
            json=tools,
        )
        return result

    async def sync_tools(self, server_id: str | UUID) -> _DictResult:
        """Sync tools from a server (re-discover).

        Args:
            server_id: The server UUID.

        Returns:
            Sync result dict.
        """
        result: _DictResult = await self._http.request("POST", f"/api/v1/servers/{server_id}/sync")
        return result

    # ------------------------------------------------------------------
    # Local tools
    # ------------------------------------------------------------------

    async def create_tool(self, server_id: str | UUID, **fields: Any) -> _DictResult:
        """Create a local tool on a server.

        Args:
            server_id: The server UUID.
            **fields: Tool fields (name, description, input_schema, etc.).

        Returns:
            Created tool dict.
        """
        result: _DictResult = await self._http.request(
            "POST",
            f"/api/v1/servers/{server_id}/tools",
            json=fields,
        )
        return result

    async def update_tool(
        self,
        server_id: str | UUID,
        tool_id: str | UUID,
        **fields: Any,
    ) -> _DictResult:
        """Update a local tool on a server.

        Args:
            server_id: The server UUID.
            tool_id: The tool UUID.
            **fields: Fields to update.

        Returns:
            Updated tool dict.
        """
        result: _DictResult = await self._http.request(
            "PATCH",
            f"/api/v1/servers/{server_id}/tools/{tool_id}",
            json=fields,
        )
        return result

    async def delete_tool(
        self,
        server_id: str | UUID,
        tool_id: str | UUID,
    ) -> None:
        """Delete a local tool from a server.

        Args:
            server_id: The server UUID.
            tool_id: The tool UUID.
        """
        await self._http.request(
            "DELETE",
            f"/api/v1/servers/{server_id}/tools/{tool_id}",
        )

    # ------------------------------------------------------------------
    # Credentials
    # ------------------------------------------------------------------

    async def list_credentials(self, server_id: str | UUID) -> _ListDictResult:
        """List credentials for a server.

        Args:
            server_id: The server UUID.

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

    async def create_credential(self, server_id: str | UUID, **fields: Any) -> _DictResult:
        """Create a credential for a server.

        Args:
            server_id: The server UUID.
            **fields: Credential fields (name, value, etc.).

        Returns:
            Created credential dict.
        """
        result: _DictResult = await self._http.request(
            "POST",
            f"/api/v1/servers/{server_id}/credentials",
            json=fields,
        )
        return result

    async def delete_credential(
        self,
        server_id: str | UUID,
        credential_id: str | UUID,
    ) -> None:
        """Delete a credential from a server.

        Args:
            server_id: The server UUID.
            credential_id: The credential UUID.
        """
        await self._http.request(
            "DELETE",
            f"/api/v1/servers/{server_id}/credentials/{credential_id}",
        )

    # ------------------------------------------------------------------
    # OAuth config
    # ------------------------------------------------------------------

    async def get_oauth_config(self, server_id: str | UUID) -> _DictResult:
        """Get OAuth config for a server.

        Args:
            server_id: The server UUID.

        Returns:
            OAuth config dict.
        """
        result: _DictResult = await self._http.request(
            "GET", f"/api/v1/servers/{server_id}/oauth-config"
        )
        return result

    async def configure_oauth(self, server_id: str | UUID, **config: Any) -> _DictResult:
        """Configure OAuth for a server.

        Args:
            server_id: The server UUID.
            **config: OAuth config fields (client_id, client_secret, etc.).

        Returns:
            Saved OAuth config dict.
        """
        result: _DictResult = await self._http.request(
            "POST",
            f"/api/v1/servers/{server_id}/oauth-config",
            json=config,
        )
        return result

    async def delete_oauth_config(self, server_id: str | UUID) -> None:
        """Delete OAuth config for a server.

        Args:
            server_id: The server UUID.
        """
        await self._http.request("DELETE", f"/api/v1/servers/{server_id}/oauth-config")

    # ------------------------------------------------------------------
    # Connection
    # ------------------------------------------------------------------

    async def get_connection_options(self, server_id: str | UUID) -> _DictResult:
        """Get connection options for a server.

        Args:
            server_id: The server UUID.

        Returns:
            Connection options dict (SSE URL, streamable HTTP URL, etc.).
        """
        result: _DictResult = await self._http.request(
            "GET", f"/api/v1/servers/{server_id}/connection-options"
        )
        return result

    # ------------------------------------------------------------------
    # Catalog
    # ------------------------------------------------------------------

    async def catalog_search(self, query: str | None = None) -> _ListDictResult:
        """Search the server catalog.

        Args:
            query: Optional search query.

        Returns:
            A list of catalog entry dicts.
        """
        params: dict[str, str] = {}
        if query is not None:
            params["search"] = query

        data = await self._http.request("GET", "/api/v1/catalog/search", params=params)
        if isinstance(data, builtins.dict):
            return builtins.list(data.get("items", []))
        return builtins.list(data)

    async def catalog_get(self, registry: str, registry_id: str) -> _DictResult:
        """Get a single catalog entry.

        Args:
            registry: The registry name (e.g. ``"npm"``).
            registry_id: The entry ID within the registry.

        Returns:
            Catalog entry dict.
        """
        result: _DictResult = await self._http.request(
            "GET", f"/api/v1/catalog/{registry}/{registry_id}"
        )
        return result

    async def import_from_catalog(
        self,
        registry: str,
        registry_id: str,
        name: str,
        **kwargs: Any,
    ) -> Server:
        """Import a server from the catalog.

        Args:
            registry: Source registry (e.g. "npm", "pypi", "docker").
            registry_id: Package identifier in the registry (e.g. "@anthropic/time-mcp").
            name: Display name for the imported server.
            **kwargs: Additional import options (config_overrides, auto_start, etc.).

        Returns:
            The imported Server model.
        """
        body: dict[str, Any] = {
            "registry": registry,
            "registry_id": registry_id,
            "name": name,
            **kwargs,
        }
        data = await self._http.request("POST", "/api/v1/servers/catalog-imports", json=body)
        return Server.model_validate(data)
