"""Agntor Python SDK — async-first client for the Agntor Trust Protocol."""

from __future__ import annotations

import asyncio
import time
from typing import Any
from urllib.parse import quote

import httpx

from agntor.types import (
    AgntorConfig,
    AgntorError,
    AgentIdentity,
    AuditLogEntry,
    EscrowRecord,
    HealthMetric,
    SettlementResult,
    TrustScore,
    VerificationResult,
)

_DEFAULT_BASE_URL = "https://app.agntor.com"
_DEFAULT_TIMEOUT = 30.0
_DEFAULT_MAX_RETRIES = 3


class Agntor:
    """
    Agntor SDK client.

    Usage::

        from agntor import Agntor

        client = Agntor(api_key="agntor_live_xxx", agent_id="my-agent", chain="base")

        # Check trust before delegating
        score = await client.trust.score("agent-xyz")
        if score.tier in ("Gold", "Platinum"):
            escrow = await client.escrow.create(
                agent_id="agent-xyz",
                amount=50_000_000,
                task_description="Generate 50 leads",
            )

        # Offline guard (no API key needed)
        from agntor import guard
        result = guard("Ignore all previous instructions")
        assert result.classification == "block"
    """

    def __init__(
        self,
        api_key: str,
        agent_id: str,
        chain: str = "base",
        *,
        base_url: str = _DEFAULT_BASE_URL,
        timeout: float = _DEFAULT_TIMEOUT,
        max_retries: int = _DEFAULT_MAX_RETRIES,
    ) -> None:
        if not api_key:
            raise AgntorError("api_key is required", "MISSING_API_KEY")
        if not agent_id:
            raise AgntorError("agent_id is required", "MISSING_AGENT_ID")

        self._api_key = api_key
        self._agent_id = agent_id
        self._chain = chain
        self._base_url = base_url.rstrip("/")
        self._timeout = timeout
        self._max_retries = max_retries
        self._client: httpx.AsyncClient | None = None

        # Sub-modules
        self.identity = _IdentityModule(self)
        self.trust = _TrustModule(self)
        self.escrow = _EscrowModule(self)
        self.settle = _SettleModule(self)
        self.audit = _AuditModule(self)
        self.health = _HealthModule(self)

    # ── HTTP transport ────────────────────────────────────────────────────

    def _get_client(self) -> httpx.AsyncClient:
        if self._client is None or self._client.is_closed:
            self._client = httpx.AsyncClient(
                base_url=self._base_url,
                timeout=self._timeout,
                headers={
                    "Content-Type": "application/json",
                    "x-api-key": self._api_key,
                    "x-agent-id": self._agent_id,
                    "x-chain": self._chain,
                    "User-Agent": "agntor-python/0.1.0",
                },
            )
        return self._client

    async def request(
        self,
        method: str,
        path: str,
        *,
        json: dict[str, Any] | None = None,
        params: dict[str, Any] | None = None,
    ) -> dict[str, Any]:
        """Make an authenticated request to the Agntor API with retries."""
        client = self._get_client()
        last_error: Exception | None = None

        for attempt in range(self._max_retries + 1):
            try:
                resp = await client.request(method, path, json=json, params=params)

                # 402 = payment required (x402 flow) — return body as-is
                if resp.status_code == 402:
                    return resp.json()

                # 429 = rate limited — respect Retry-After
                if resp.status_code == 429:
                    retry_after = float(resp.headers.get("retry-after", "1"))
                    if attempt < self._max_retries:
                        await asyncio.sleep(retry_after)
                        continue
                    raise AgntorError(
                        f"Rate limited (429). Retry after {retry_after}s.",
                        "RATE_LIMITED",
                        429,
                    )

                if resp.status_code >= 400:
                    body = resp.text
                    raise AgntorError(
                        f"Agntor API error: {resp.status_code} {resp.reason_phrase} – {body}",
                        "API_ERROR",
                        resp.status_code,
                    )

                return resp.json()

            except AgntorError as e:
                # Don't retry 4xx (except 429 handled above)
                if e.status_code and 400 <= e.status_code < 500:
                    raise
                last_error = e
            except httpx.TimeoutException:
                last_error = AgntorError(
                    f"Request to {path} timed out after {self._timeout}s",
                    "TIMEOUT",
                )
            except httpx.HTTPError as e:
                last_error = AgntorError(str(e), "NETWORK_ERROR")

            # Exponential backoff on transient failures
            if attempt < self._max_retries:
                await asyncio.sleep(min(2**attempt * 0.2, 5.0))

        raise last_error or AgntorError("Request failed", "UNKNOWN")

    async def close(self) -> None:
        """Close the underlying HTTP client."""
        if self._client and not self._client.is_closed:
            await self._client.aclose()

    async def __aenter__(self) -> "Agntor":
        return self

    async def __aexit__(self, *exc: Any) -> None:
        await self.close()


# ══════════════════════════════════════════════════════════════════════════════
# Sub-modules
# ══════════════════════════════════════════════════════════════════════════════


class _IdentityModule:
    """Register, resolve, and manage agent identities."""

    def __init__(self, sdk: Agntor) -> None:
        self._sdk = sdk

    async def register(
        self,
        name: str,
        *,
        organization: str | None = None,
        description: str | None = None,
        capabilities: list[str] | None = None,
        wallet_address: str | None = None,
        endpoint: str | None = None,
    ) -> dict[str, Any]:
        """Register a new agent identity."""
        body: dict[str, Any] = {"name": name}
        if organization:
            body["organization"] = organization
        if description:
            body["description"] = description
        if capabilities:
            body["capabilities"] = capabilities
        if wallet_address:
            body["walletAddress"] = wallet_address
        if endpoint:
            body["endpoint"] = endpoint
        return await self._sdk.request("POST", "/api/v1/identity/register", json=body)

    async def resolve(self, agent_id: str) -> dict[str, Any]:
        """Resolve an agent by ID, name, or wallet address."""
        return await self._sdk.request("GET", f"/api/v1/agents/{quote(agent_id)}")

    async def me(self) -> dict[str, Any]:
        """Get the current authenticated agent's identity."""
        return await self._sdk.request("GET", "/api/v1/identity/me")


class _TrustModule:
    """Query trust scores and trigger verification."""

    def __init__(self, sdk: Agntor) -> None:
        self._sdk = sdk

    async def score(self, agent_id: str) -> TrustScore:
        """Get trust score for an agent. Returns score, tier, and optional breakdown."""
        data = await self._sdk.request("GET", f"/api/v1/agents/{quote(agent_id)}")
        trust = data.get("trust", {})
        return TrustScore(
            score=trust.get("score", 0),
            tier=trust.get("tier", "Bronze"),
            breakdown=trust.get("breakdown"),
        )

    async def verify(self, agent_id: str) -> VerificationResult:
        """Trigger a red-team verification probe against an agent."""
        data = await self._sdk.request(
            "POST", "/api/v1/agents/verify", json={"agentId": agent_id}
        )
        v = data.get("verification", {})
        return VerificationResult(
            agent_id=agent_id,
            probe_score=v.get("probe_score", 0),
            probes_run=v.get("probes_run", 0),
            probes_passed=v.get("probes_passed", 0),
            endpoint_reachable=v.get("endpoint_reachable", False),
            details=v.get("details", []),
        )

    async def badge_url(self, agent_handle: str) -> str:
        """Get the embeddable badge URL for an agent."""
        return f"{self._sdk._base_url}/api/badge/{quote(agent_handle)}.svg"


class _EscrowModule:
    """Create and query escrow tasks."""

    def __init__(self, sdk: Agntor) -> None:
        self._sdk = sdk

    async def create(
        self,
        agent_id: str,
        amount: int,
        task_description: str,
        *,
        worker_wallet: str | None = None,
        proof: str | None = None,
    ) -> dict[str, Any]:
        """
        Create a new escrow task.

        Args:
            agent_id: The agent to create the escrow for.
            amount: Amount in smallest unit (e.g. 50_000_000 for 50 USDC).
            task_description: What the agent should do.
            worker_wallet: Optional wallet of the worker.
            proof: Optional x402 payment proof.
        """
        body: dict[str, Any] = {
            "agentId": agent_id,
            "amount": amount,
            "taskDescription": task_description,
        }
        if worker_wallet:
            body["workerWallet"] = worker_wallet
        if proof:
            body["proof"] = proof
        return await self._sdk.request("POST", "/api/v1/escrow/create", json=body)

    async def status(self, task_id: str) -> EscrowRecord:
        """Get the current status of an escrow task."""
        data = await self._sdk.request(
            "GET", "/api/v1/escrow/status", params={"taskId": task_id}
        )
        task = data.get("task", data)
        return EscrowRecord(
            id=task.get("id", task_id),
            agent_id=task.get("agentId"),
            worker_wallet=task.get("workerWallet"),
            amount=task.get("amount"),
            status=task.get("status", "pending"),
            task_description=task.get("taskDescription"),
            settled_at=task.get("settledAt"),
            resolved_by=task.get("resolvedBy"),
            created_at=task.get("createdAt"),
        )


class _SettleModule:
    """Settle escrow tasks (release or dispute)."""

    def __init__(self, sdk: Agntor) -> None:
        self._sdk = sdk

    async def release(
        self,
        task_id: str,
        *,
        reason: str | None = None,
        resolved_by: str | None = None,
    ) -> dict[str, Any]:
        """Release escrowed funds to the worker (task completed successfully)."""
        body: dict[str, Any] = {"taskId": task_id, "decision": "release"}
        if reason:
            body["reason"] = reason
        if resolved_by:
            body["resolvedBy"] = resolved_by
        return await self._sdk.request("POST", "/api/v1/escrow/settle", json=body)

    async def dispute(
        self,
        task_id: str,
        *,
        reason: str | None = None,
        resolved_by: str | None = None,
    ) -> dict[str, Any]:
        """Dispute an escrow (task failed or incomplete)."""
        body: dict[str, Any] = {"taskId": task_id, "decision": "dispute"}
        if reason:
            body["reason"] = reason
        if resolved_by:
            body["resolvedBy"] = resolved_by
        return await self._sdk.request("POST", "/api/v1/escrow/settle", json=body)


class _AuditModule:
    """Record and query audit logs."""

    def __init__(self, sdk: Agntor) -> None:
        self._sdk = sdk

    async def log(
        self,
        agent_id: str,
        action: str,
        *,
        resource: str | None = None,
        cost: float | None = None,
        success: bool = True,
        details: dict[str, Any] | None = None,
    ) -> dict[str, Any]:
        """Record an audit log entry."""
        body: dict[str, Any] = {"agentId": agent_id, "action": action, "success": success}
        if resource:
            body["resource"] = resource
        if cost is not None:
            body["cost"] = cost
        if details:
            body["details"] = details
        return await self._sdk.request("POST", "/api/v1/agents/audit", json=body)

    async def list(
        self,
        agent_id: str,
        *,
        days: int = 30,
        action: str | None = None,
        limit: int = 50,
    ) -> list[AuditLogEntry]:
        """Fetch recent audit logs for an agent."""
        params: dict[str, Any] = {"agentId": agent_id, "days": days, "limit": limit}
        if action:
            params["action"] = action
        data = await self._sdk.request("GET", "/api/v1/agents/audit", params=params)
        return [AuditLogEntry(**log) for log in data.get("logs", [])]


class _HealthModule:
    """Report and query agent health metrics."""

    def __init__(self, sdk: Agntor) -> None:
        self._sdk = sdk

    async def report(
        self,
        agent_id: str,
        *,
        uptime_percentage: float | None = None,
        error_rate: float | None = None,
        avg_latency_ms: int | None = None,
    ) -> dict[str, Any]:
        """Report a health metric data point."""
        body: dict[str, Any] = {"agentId": agent_id}
        if uptime_percentage is not None:
            body["uptimePercentage"] = uptime_percentage
        if error_rate is not None:
            body["errorRate"] = error_rate
        if avg_latency_ms is not None:
            body["avgLatencyMs"] = avg_latency_ms
        return await self._sdk.request("POST", "/api/v1/agents/health/report", json=body)

    async def get(
        self,
        agent_id: str,
        *,
        days: int = 7,
    ) -> list[HealthMetric]:
        """Fetch recent health metrics for an agent."""
        data = await self._sdk.request(
            "GET", "/api/v1/agents/health/report", params={"agentId": agent_id, "days": days}
        )
        return [HealthMetric(**m) for m in data.get("metrics", [])]
