"""
Agent resource clients (sync and async) for registration, lookup, update,
search, and deregistration.
"""

from __future__ import annotations

from typing import Any
from urllib.parse import quote

from ..auth import sign_register, sign_update, sign_typed_data, get_address, generate_nonce, generate_deadline
from ..http import HttpClient, AsyncHttpClient
from ..models import Agent, AgentWithDetails, PaginatedResponse, PaginationInfo


class AgentsClient:
    """Synchronous client for agent endpoints."""

    def __init__(self, http: HttpClient, private_key: str | None = None) -> None:
        self._http = http
        self._private_key = private_key

    def register(
        self,
        name: str,
        description: str,
        metadata: dict[str, Any] | None = None,
        domains: list[str] | None = None,
    ) -> Agent:
        """Register a new agent in the ARP network. Requires a private key."""
        if not self._private_key:
            raise ValueError("A private_key is required to register an agent.")

        sig_data = sign_register(
            self._private_key,
            name=name,
            description=description,
            domains=domains or [],
        )
        address = get_address(self._private_key)

        body: dict[str, Any] = {
            "name": name,
            "description": description,
            "address": address,
            "signature": sig_data["signature"],
            "nonce": sig_data["nonce"],
            "deadline": sig_data["deadline"],
        }
        if metadata is not None:
            body["metadata"] = metadata
        if domains is not None:
            body["domains"] = domains

        data = self._http.post("/v1/agents", body)
        return Agent.model_validate(data)

    def get(self, agent_id: str) -> AgentWithDetails:
        """Get detailed information about a specific agent."""
        data = self._http.get(f"/v1/agents/{quote(agent_id, safe='')}")
        return AgentWithDetails.model_validate(data)

    def update(
        self,
        agent_id: str,
        name: str | None = None,
        description: str | None = None,
        metadata: dict[str, Any] | None = None,
        domains: list[str] | None = None,
    ) -> Agent:
        """Update an existing agent's information. Requires a private key."""
        if not self._private_key:
            raise ValueError("A private_key is required to update an agent.")

        current = self.get(agent_id)
        sig_data = sign_update(
            self._private_key,
            agent_id=agent_id,
            name=name or current.name,
            description=description or current.description,
            domains=domains or current.domains,
        )

        body: dict[str, Any] = {
            "signature": sig_data["signature"],
            "nonce": sig_data["nonce"],
            "deadline": sig_data["deadline"],
        }
        if name is not None:
            body["name"] = name
        if description is not None:
            body["description"] = description
        if metadata is not None:
            body["metadata"] = metadata
        if domains is not None:
            body["domains"] = domains

        data = self._http.patch(f"/v1/agents/{quote(agent_id, safe='')}", body)
        return Agent.model_validate(data)

    def search(
        self,
        query: str | None = None,
        domain: str | None = None,
        status: str | None = None,
        min_reputation: float | None = None,
        max_reputation: float | None = None,
        sort_by: str | None = None,
        sort_order: str | None = None,
        page: int | None = None,
        limit: int | None = None,
    ) -> PaginatedResponse[Agent]:
        """Search for agents with filtering, sorting, and pagination."""
        params: dict[str, Any] = {
            "query": query,
            "domain": domain,
            "status": status,
            "minReputation": min_reputation,
            "maxReputation": max_reputation,
            "sortBy": sort_by,
            "sortOrder": sort_order,
            "page": page,
            "limit": limit,
        }

        data = self._http.get("/v1/agents", params)
        agents = [Agent.model_validate(item) for item in data["data"]]
        pagination = PaginationInfo.model_validate(data["pagination"])
        return PaginatedResponse[Agent](data=agents, pagination=pagination)

    def deregister(self, agent_id: str) -> None:
        """Deregister an agent from the ARP network. Requires a private key."""
        if not self._private_key:
            raise ValueError("A private_key is required to deregister an agent.")

        nonce = generate_nonce()
        deadline = generate_deadline()
        signature = sign_typed_data(self._private_key, "UpdateAgent", {
            "agentId": agent_id,
            "name": "",
            "description": "",
            "domains": "",
            "nonce": nonce,
            "deadline": deadline,
        })

        self._http.delete(
            f"/v1/agents/{quote(agent_id, safe='')}?signature={quote(signature, safe='')}&nonce={nonce}&deadline={deadline}"
        )


class AsyncAgentsClient:
    """Asynchronous client for agent endpoints."""

    def __init__(self, http: AsyncHttpClient, private_key: str | None = None) -> None:
        self._http = http
        self._private_key = private_key

    async def register(
        self,
        name: str,
        description: str,
        metadata: dict[str, Any] | None = None,
        domains: list[str] | None = None,
    ) -> Agent:
        """Register a new agent in the ARP network. Requires a private key."""
        if not self._private_key:
            raise ValueError("A private_key is required to register an agent.")

        sig_data = sign_register(
            self._private_key,
            name=name,
            description=description,
            domains=domains or [],
        )
        address = get_address(self._private_key)

        body: dict[str, Any] = {
            "name": name,
            "description": description,
            "address": address,
            "signature": sig_data["signature"],
            "nonce": sig_data["nonce"],
            "deadline": sig_data["deadline"],
        }
        if metadata is not None:
            body["metadata"] = metadata
        if domains is not None:
            body["domains"] = domains

        data = await self._http.post("/v1/agents", body)
        return Agent.model_validate(data)

    async def get(self, agent_id: str) -> AgentWithDetails:
        """Get detailed information about a specific agent."""
        data = await self._http.get(f"/v1/agents/{quote(agent_id, safe='')}")
        return AgentWithDetails.model_validate(data)

    async def update(
        self,
        agent_id: str,
        name: str | None = None,
        description: str | None = None,
        metadata: dict[str, Any] | None = None,
        domains: list[str] | None = None,
    ) -> Agent:
        """Update an existing agent's information. Requires a private key."""
        if not self._private_key:
            raise ValueError("A private_key is required to update an agent.")

        current = await self.get(agent_id)
        sig_data = sign_update(
            self._private_key,
            agent_id=agent_id,
            name=name or current.name,
            description=description or current.description,
            domains=domains or current.domains,
        )

        body: dict[str, Any] = {
            "signature": sig_data["signature"],
            "nonce": sig_data["nonce"],
            "deadline": sig_data["deadline"],
        }
        if name is not None:
            body["name"] = name
        if description is not None:
            body["description"] = description
        if metadata is not None:
            body["metadata"] = metadata
        if domains is not None:
            body["domains"] = domains

        data = await self._http.patch(f"/v1/agents/{quote(agent_id, safe='')}", body)
        return Agent.model_validate(data)

    async def search(
        self,
        query: str | None = None,
        domain: str | None = None,
        status: str | None = None,
        min_reputation: float | None = None,
        max_reputation: float | None = None,
        sort_by: str | None = None,
        sort_order: str | None = None,
        page: int | None = None,
        limit: int | None = None,
    ) -> PaginatedResponse[Agent]:
        """Search for agents with filtering, sorting, and pagination."""
        params: dict[str, Any] = {
            "query": query,
            "domain": domain,
            "status": status,
            "minReputation": min_reputation,
            "maxReputation": max_reputation,
            "sortBy": sort_by,
            "sortOrder": sort_order,
            "page": page,
            "limit": limit,
        }

        data = await self._http.get("/v1/agents", params)
        agents = [Agent.model_validate(item) for item in data["data"]]
        pagination = PaginationInfo.model_validate(data["pagination"])
        return PaginatedResponse[Agent](data=agents, pagination=pagination)

    async def deregister(self, agent_id: str) -> None:
        """Deregister an agent from the ARP network. Requires a private key."""
        if not self._private_key:
            raise ValueError("A private_key is required to deregister an agent.")

        nonce = generate_nonce()
        deadline = generate_deadline()
        signature = sign_typed_data(self._private_key, "UpdateAgent", {
            "agentId": agent_id,
            "name": "",
            "description": "",
            "domains": "",
            "nonce": nonce,
            "deadline": deadline,
        })

        await self._http.delete(
            f"/v1/agents/{quote(agent_id, safe='')}?signature={quote(signature, safe='')}&nonce={nonce}&deadline={deadline}"
        )
