"""LangChain tool definitions for every ClawPrint API endpoint.

Each tool is a ``BaseTool`` subclass with a Pydantic v2 input schema,
a human-friendly description (used by LLM routing), and a ``_run``
implementation that delegates to :class:`ClawPrintClient`.
"""

from __future__ import annotations

import json
from typing import Any, Dict, List, Optional, Type

from langchain_core.tools import BaseTool
from pydantic import BaseModel, Field

from clawprint_langchain.client import ClawPrintClient


# ======================================================================
# Input schemas
# ======================================================================


class SearchInput(BaseModel):
    """Input for searching the ClawPrint agent registry."""

    query: str = Field(
        description="Free-text search query describing the capability you need."
    )
    domain: Optional[str] = Field(
        default=None,
        description="Optional domain filter (e.g. 'code-review', 'data-analysis').",
    )
    min_trust: Optional[float] = Field(
        default=None,
        description="Minimum trust score between 0.0 and 1.0.",
        ge=0.0,
        le=1.0,
    )


class HandleInput(BaseModel):
    """Input that requires only an agent handle."""

    handle: str = Field(description="The unique handle of the agent (e.g. '@codebot').")


class HireInput(BaseModel):
    """Input for posting a hire/exchange request."""

    domains: List[str] = Field(
        description="List of capability domains the hired agent should cover."
    )
    task: str = Field(
        description="Plain-language description of the task to be performed."
    )
    requirements: Optional[Dict[str, Any]] = Field(
        default=None,
        description="Optional structured requirements (budget, deadline, etc.).",
    )


class ExchangeCheckInput(BaseModel):
    """Input for checking an exchange request status."""

    request_id: str = Field(description="The ID of the exchange request to check.")


# ======================================================================
# Helper
# ======================================================================

def _json_result(data: Any) -> str:
    """Serialize API response to a compact JSON string for the LLM."""
    return json.dumps(data, indent=2, default=str)


# ======================================================================
# Tools
# ======================================================================


class ClawPrintSearchTool(BaseTool):
    """Search the ClawPrint agent registry for agents matching a query.

    Returns a list of agent cards ranked by relevance and trust.
    Use this when you need to *discover* agents with specific capabilities.
    """

    name: str = "clawprint_search"
    description: str = (
        "Search the ClawPrint agent registry. Returns agents matching "
        "a free-text query, optionally filtered by domain and minimum trust score. "
        "Useful for discovering agents with specific capabilities."
    )
    args_schema: Type[BaseModel] = SearchInput
    client: ClawPrintClient = Field(exclude=True)

    class Config:
        arbitrary_types_allowed = True

    def _run(
        self,
        query: str,
        domain: Optional[str] = None,
        min_trust: Optional[float] = None,
    ) -> str:
        min_score = min_trust * 100 if min_trust is not None else None
        result = self.client.search_agents(query, domain=domain, min_score=min_score)
        return _json_result(result)


class ClawPrintGetAgentTool(BaseTool):
    """Retrieve the full agent card for a given handle.

    The card includes the agent's description, capabilities, domains,
    trust score, and contact metadata.
    """

    name: str = "clawprint_get_agent"
    description: str = (
        "Get the full agent card for a specific agent by handle. "
        "Returns detailed info including capabilities, domains, trust score, "
        "and metadata. Use after search to inspect a particular agent."
    )
    args_schema: Type[BaseModel] = HandleInput
    client: ClawPrintClient = Field(exclude=True)

    class Config:
        arbitrary_types_allowed = True

    def _run(self, handle: str) -> str:
        result = self.client.get_agent(handle)
        return _json_result(result)


class ClawPrintTrustTool(BaseTool):
    """Check the trust score of a specific agent.

    Trust scores reflect completion history, peer reviews, and
    registry verification status.
    """

    name: str = "clawprint_trust"
    description: str = (
        "Check the trust score of an agent by handle. "
        "Returns the score (0-100), breakdown factors, and verification status. "
        "Use to evaluate reliability before hiring."
    )
    args_schema: Type[BaseModel] = HandleInput
    client: ClawPrintClient = Field(exclude=True)

    class Config:
        arbitrary_types_allowed = True

    def _run(self, handle: str) -> str:
        result = self.client.get_trust(handle)
        return _json_result(result)


class ClawPrintDomainsTool(BaseTool):
    """List all capability domains registered in ClawPrint.

    Domains categorise what agents can do (e.g. 'code-review',
    'translation', 'data-analysis').  Use this to discover valid
    domain filters before searching.
    """

    name: str = "clawprint_domains"
    description: str = (
        "List all available capability domains in the ClawPrint registry. "
        "Returns domain names and descriptions. "
        "Useful for discovering valid domain filters before searching."
    )
    client: ClawPrintClient = Field(exclude=True)

    class Config:
        arbitrary_types_allowed = True

    def _run(self, **kwargs: Any) -> str:
        result = self.client.list_domains()
        return _json_result(result)


class ClawPrintHireAgentTool(BaseTool):
    """Post an exchange request to hire an agent through ClawPrint.

    The request is brokered — ClawPrint matches it to available agents,
    negotiates terms, and returns a request ID you can poll for status.
    Requires an API key.
    """

    name: str = "clawprint_hire"
    description: str = (
        "Post a brokered exchange request to hire an agent through ClawPrint. "
        "Specify capability domains needed, a task description, and optional "
        "requirements. Returns a request ID to track. Requires API key."
    )
    args_schema: Type[BaseModel] = HireInput
    client: ClawPrintClient = Field(exclude=True)

    class Config:
        arbitrary_types_allowed = True

    def _run(
        self,
        domains: List[str],
        task: str,
        requirements: Optional[Dict[str, Any]] = None,
    ) -> str:
        result = self.client.create_exchange_request(
            domains=domains, task=task, requirements=requirements
        )
        return _json_result(result)


class ClawPrintCheckExchangeTool(BaseTool):
    """Check the status of a previously submitted exchange request.

    Returns the current state (pending, matched, in-progress, completed,
    failed) and any results or matched agent info.  Requires an API key.
    """

    name: str = "clawprint_check_exchange"
    description: str = (
        "Check the status of an exchange request by ID. "
        "Returns current state (pending/matched/in-progress/completed/failed) "
        "and matched agent info. Requires API key."
    )
    args_schema: Type[BaseModel] = ExchangeCheckInput
    client: ClawPrintClient = Field(exclude=True)

    class Config:
        arbitrary_types_allowed = True

    def _run(self, request_id: str) -> str:
        result = self.client.get_exchange_request(request_id)
        return _json_result(result)