"""Bundles resource -- CRUD, tool management, server/connection assignment."""

from __future__ import annotations

import builtins
from typing import Any
from uuid import UUID

from mcpgateway_sdk._http import HTTPClient
from mcpgateway_sdk.models.bundle import (
    Bundle,
    BundleDetail,
    BundleTool,
    BundleWithKey,
    ServerAssignment,
)
from mcpgateway_sdk.models.connection import BundleConnection

_ENDPOINTS = {
    # CRUD (5)
    "list": ("GET", "/api/v1/bundles"),
    "get": ("GET", "/api/v1/bundles/{bundle_id}"),
    "get_by_agent": ("GET", "/api/v1/bundles/by-agent/{agent_id}"),
    "create": ("POST", "/api/v1/bundles"),
    "update": ("PUT", "/api/v1/bundles/{bundle_id}"),
    "delete": ("DELETE", "/api/v1/bundles/{bundle_id}"),
    # Server assignment (2)
    "assign_server": ("POST", "/api/v1/bundles/{bundle_id}/servers/{server_id}"),
    "remove_server": ("DELETE", "/api/v1/bundles/{bundle_id}/servers/{server_id}"),
    # Tool management (4)
    "add_tools": ("POST", "/api/v1/bundles/{bundle_id}/tools"),
    "list_tools": ("GET", "/api/v1/bundles/{bundle_id}/tools"),
    "update_tool": ("PUT", "/api/v1/bundles/{bundle_id}/tools/{tool_id}"),
    "remove_tool": ("DELETE", "/api/v1/bundles/{bundle_id}/tools/{tool_id}"),
    # Connection assignment (3)
    "assign_connection": ("POST", "/api/v1/bundles/{bundle_id}/connections/{connection_id}"),
    "remove_connection": ("DELETE", "/api/v1/bundles/{bundle_id}/connections/{connection_id}"),
    "list_connections": ("GET", "/api/v1/bundles/{bundle_id}/connections"),
}


class BundlesResource:
    """Manage bundles that group tools for agents."""

    _endpoints = _ENDPOINTS

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

    # ── CRUD ──────────────────────────────────────────────────────────

    async def list(
        self,
        *,
        page: int = 1,
        page_size: int = 20,
        search: str | None = None,
    ) -> builtins.list[Bundle]:
        """List bundles with optional search filtering.

        Args:
            page: Page number (1-indexed).
            page_size: Items per page.
            search: Optional text search filter.

        Returns:
            A list of Bundle objects.
        """
        params: dict[str, Any] = {"page": page, "page_size": page_size}
        if search is not None:
            params["search"] = search
        data = await self._http.request("GET", "/api/v1/bundles", params=params)
        return [Bundle.model_validate(item) for item in data.get("items", [])]

    async def get(self, bundle_id: str | UUID) -> BundleDetail:
        """Get a bundle with its full tool list.

        Args:
            bundle_id: UUID of the bundle.

        Returns:
            A BundleDetail with tools included.
        """
        data = await self._http.request("GET", f"/api/v1/bundles/{bundle_id}")
        return BundleDetail.model_validate(data)

    async def get_by_agent(self, agent_id: str | UUID) -> BundleDetail:
        """Get the bundle associated with an agent.

        Args:
            agent_id: External agent identifier.

        Returns:
            A BundleDetail for the agent's bundle.
        """
        data = await self._http.request("GET", f"/api/v1/bundles/by-agent/{agent_id}")
        return BundleDetail.model_validate(data)

    async def create(
        self,
        name: str,
        *,
        description: str | None = None,
        agent_id: str | UUID | None = None,
        tools: builtins.list[dict[str, Any]] | None = None,
    ) -> BundleWithKey:
        """Create a new bundle.

        Args:
            name: Bundle name.
            description: Optional description.
            agent_id: Optional external agent identifier (unique per bundle).
            tools: Optional list of tool dicts with keys: tool_id, override_name,
                   override_description, enabled, display_order.

        Returns:
            A BundleWithKey including the opaque sharing key.
        """
        body: dict[str, Any] = {"name": name}
        if description is not None:
            body["description"] = description
        if agent_id is not None:
            body["agent_id"] = str(agent_id)
        if tools is not None:
            body["tools"] = tools
        data = await self._http.request("POST", "/api/v1/bundles", json=body)
        return BundleWithKey.model_validate(data)

    async def update(
        self,
        bundle_id: str | UUID,
        *,
        name: str | None = None,
        description: str | None = None,
        agent_id: str | UUID | None = None,
    ) -> BundleWithKey:
        """Update a bundle's metadata.

        Args:
            bundle_id: UUID of the bundle to update.
            name: New name (optional).
            description: New description (optional).
            agent_id: New agent_id (optional).

        Returns:
            The updated BundleWithKey (includes bundle_key).
        """
        body: dict[str, Any] = {}
        if name is not None:
            body["name"] = name
        if description is not None:
            body["description"] = description
        if agent_id is not None:
            body["agent_id"] = str(agent_id)
        data = await self._http.request("PUT", f"/api/v1/bundles/{bundle_id}", json=body)
        return BundleWithKey.model_validate(data)

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

        Args:
            bundle_id: UUID of the bundle to delete.
        """
        await self._http.request("DELETE", f"/api/v1/bundles/{bundle_id}")

    # ── Server assignment ─────────────────────────────────────────────

    async def assign_server(self, bundle_id: str | UUID, server_id: str | UUID) -> ServerAssignment:
        """Assign all tools from a server to a bundle.

        Args:
            bundle_id: UUID of the bundle.
            server_id: UUID of the server whose tools to add.

        Returns:
            A ServerAssignment with counts of added/existing tools.
        """
        data = await self._http.request("POST", f"/api/v1/bundles/{bundle_id}/servers/{server_id}")
        return ServerAssignment.model_validate(data)

    async def remove_server(self, bundle_id: str | UUID, server_id: str | UUID) -> None:
        """Remove all tools from a server in a bundle.

        Args:
            bundle_id: UUID of the bundle.
            server_id: UUID of the server whose tools to remove.
        """
        await self._http.request("DELETE", f"/api/v1/bundles/{bundle_id}/servers/{server_id}")

    # ── Tool management ───────────────────────────────────────────────

    async def add_tools(
        self, bundle_id: str | UUID, tools: builtins.list[dict[str, Any]]
    ) -> builtins.list[BundleTool]:
        """Add tools to a bundle.

        Args:
            bundle_id: UUID of the bundle.
            tools: List of tool dicts with keys: tool_id, override_name,
                   override_description, enabled, display_order.

        Returns:
            List of BundleTool objects that were added.
        """
        data = await self._http.request(
            "POST",
            f"/api/v1/bundles/{bundle_id}/tools",
            json={"tools": tools},
        )
        return [BundleTool.model_validate(item) for item in data]

    async def list_tools(
        self,
        bundle_id: str | UUID,
        *,
        page: int = 1,
        page_size: int = 100,
    ) -> builtins.list[BundleTool]:
        """List tools in a bundle.

        Args:
            bundle_id: UUID of the bundle.
            page: Page number (1-indexed).
            page_size: Items per page.

        Returns:
            List of BundleTool objects.
        """
        params: dict[str, Any] = {"page": page, "page_size": page_size}
        data = await self._http.request("GET", f"/api/v1/bundles/{bundle_id}/tools", params=params)
        return [BundleTool.model_validate(item) for item in data.get("items", [])]

    async def update_tool(
        self,
        bundle_id: str | UUID,
        tool_id: str | UUID,
        *,
        override_name: str | None = None,
        override_description: str | None = None,
        enabled: bool | None = None,
        display_order: int | None = None,
    ) -> BundleTool:
        """Update a tool's overrides within a bundle.

        Args:
            bundle_id: UUID of the bundle.
            tool_id: UUID of the underlying tool (MCPTool.id).
            override_name: New name override (optional).
            override_description: New description override (optional).
            enabled: Whether the tool is active (optional).
            display_order: Display position (optional).

        Returns:
            The updated BundleTool.
        """
        body: dict[str, Any] = {}
        if override_name is not None:
            body["override_name"] = override_name
        if override_description is not None:
            body["override_description"] = override_description
        if enabled is not None:
            body["enabled"] = enabled
        if display_order is not None:
            body["display_order"] = display_order
        data = await self._http.request(
            "PUT", f"/api/v1/bundles/{bundle_id}/tools/{tool_id}", json=body
        )
        return BundleTool.model_validate(data)

    async def remove_tool(self, bundle_id: str | UUID, tool_id: str | UUID) -> None:
        """Remove a tool from a bundle.

        Args:
            bundle_id: UUID of the bundle.
            tool_id: UUID of the underlying tool (MCPTool.id) to remove.
        """
        await self._http.request("DELETE", f"/api/v1/bundles/{bundle_id}/tools/{tool_id}")

    # ── Connection assignment ─────────────────────────────────────────

    async def assign_connection(
        self, bundle_id: str | UUID, connection_id: str | UUID
    ) -> BundleConnection:
        """Assign a user connection to a bundle.

        Args:
            bundle_id: UUID of the bundle.
            connection_id: UUID of the user connection.

        Returns:
            A BundleConnection representing the assignment.
        """
        data = await self._http.request(
            "POST",
            f"/api/v1/bundles/{bundle_id}/connections/{connection_id}",
        )
        return BundleConnection.model_validate(data)

    async def remove_connection(self, bundle_id: str | UUID, connection_id: str | UUID) -> None:
        """Remove a connection from a bundle.

        Args:
            bundle_id: UUID of the bundle.
            connection_id: UUID of the user connection (UserConnection.id) to remove.
        """
        await self._http.request(
            "DELETE",
            f"/api/v1/bundles/{bundle_id}/connections/{connection_id}",
        )

    async def list_connections(self, bundle_id: str | UUID) -> builtins.list[BundleConnection]:
        """List connections assigned to a bundle.

        Args:
            bundle_id: UUID of the bundle.

        Returns:
            List of BundleConnection objects.
        """
        data = await self._http.request("GET", f"/api/v1/bundles/{bundle_id}/connections")
        return [BundleConnection.model_validate(item) for item in data.get("items", [])]
