"""ClawMoneyClient — search and invoke skills on the Hub."""

from __future__ import annotations

import httpx

from .exceptions import (
    AuthenticationError,
    ClawMoneyError,
    InvokeError,
    InvokeTimeoutError,
    NotFoundError,
    RateLimitError,
)
from .types import InvokeResult, Order, Skill


class ClawMoneyClient:
    """Hub client for searching and invoking other Agents' skills.

    Usage::

        client = ClawMoneyClient(api_key="sk-xxx")
        skills = await client.search(category="transformation/translate")
        result = await client.invoke(
            agent_id=skills[0].agent_id,
            skill="translate",
            input={"text": "Hello", "target_lang": "ja"},
        )
        print(result.output)
    """

    def __init__(
        self,
        api_key: str,
        base_url: str = "https://api.clawmoney.ai",
    ):
        self.api_key = api_key
        self.base_url = base_url.rstrip("/")
        self._http = httpx.AsyncClient(
            base_url=self.base_url,
            headers={
                "Authorization": f"Bearer {api_key}",
                "Content-Type": "application/json",
            },
            timeout=httpx.Timeout(30.0, connect=10.0),
        )

    # ------------------------------------------------------------------
    # Public API
    # ------------------------------------------------------------------

    async def search(
        self,
        q: str | None = None,
        category: str | None = None,
        min_rating: float | None = None,
        max_price: float | None = None,
        sort: str = "rating",
        limit: int = 10,
    ) -> list[Skill]:
        """Search for skills on the Hub."""
        params: dict = {"sort": sort, "limit": limit}
        if q is not None:
            params["q"] = q
        if category is not None:
            params["category"] = category
        if min_rating is not None:
            params["min_rating"] = min_rating
        if max_price is not None:
            params["max_price"] = max_price

        data = await self._get("/v1/skills", params=params)
        return [Skill.from_dict(item) for item in data.get("skills", [])]

    async def invoke(
        self,
        agent_id: str,
        skill: str,
        input: dict,
        timeout: int = 60,
    ) -> InvokeResult:
        """Invoke an Agent skill. Payment is handled via the ledger."""
        data = await self._post(
            "/v1/invoke",
            json={
                "agent_id": agent_id,
                "skill": skill,
                "input": input,
            },
            timeout=timeout,
        )
        return InvokeResult.from_dict(data)

    async def get_order(self, order_id: str) -> Order:
        """Fetch order details by ID."""
        data = await self._get(f"/v1/orders/{order_id}")
        return Order.from_dict(data)

    async def rate(self, order_id: str, score: int, comment: str | None = None):
        """Rate a completed order (score 1-5)."""
        body: dict = {"score": score}
        if comment is not None:
            body["comment"] = comment
        await self._post(f"/v1/orders/{order_id}/rate", json=body)

    async def close(self):
        """Close the underlying HTTP client."""
        await self._http.aclose()

    # ------------------------------------------------------------------
    # Context manager
    # ------------------------------------------------------------------

    async def __aenter__(self):
        return self

    async def __aexit__(self, *exc):
        await self.close()

    # ------------------------------------------------------------------
    # Internal helpers
    # ------------------------------------------------------------------

    async def _get(self, path: str, params: dict | None = None) -> dict:
        resp = await self._http.get(path, params=params)
        return self._handle_response(resp)

    async def _post(
        self,
        path: str,
        json: dict | None = None,
        timeout: int | None = None,
    ) -> dict:
        kw = {}
        if timeout is not None:
            kw["timeout"] = httpx.Timeout(float(timeout), connect=10.0)
        resp = await self._http.post(path, json=json, **kw)
        return self._handle_response(resp)

    @staticmethod
    def _handle_response(resp: httpx.Response) -> dict:
        if resp.status_code == 200:
            return resp.json()
        if resp.status_code == 401:
            raise AuthenticationError()
        if resp.status_code == 404:
            msg = resp.json().get("error", "Not found")
            raise NotFoundError(msg)
        if resp.status_code == 408:
            raise InvokeTimeoutError()
        if resp.status_code == 429:
            retry_after = float(resp.headers.get("Retry-After", "0"))
            raise RateLimitError(retry_after=retry_after)
        # Generic error
        try:
            body = resp.json()
            msg = body.get("error", resp.text)
        except Exception:
            msg = resp.text
        if resp.status_code >= 500:
            raise ClawMoneyError(f"Server error ({resp.status_code}): {msg}")
        raise InvokeError(f"Request failed ({resp.status_code}): {msg}")
