"""RMI SDK — Python client for Rug Munch Intelligence.

v4.0 Sovereign-first. Friendly wrapper around the auto-generated
OpenAPI client. See rugmunch/ for the full generated SDK.

Quick start:
    >>> from rugmunch_sdk import RMI
    >>> client = RMI(base_url="https://api.rugmunch.io", api_key="...")
    >>> risk = client.get_token_risk(chain="solana", address="DezXAZ...")
    >>> print(risk.tier, risk.score)
    >>>
    >>> report = client.generate_report("token", "solana:DezXAZ...")
    >>> print(report.markdown[:500])
"""
from __future__ import annotations

import asyncio

import httpx

from .models import (
    McpTool,
    NewsItem,
    RagHit,
    Report,
    TokenRisk,
)


class RMI:
    """Async Python client for the Rug Munch Intelligence API."""

    def __init__(
        self,
        base_url: str = "https://api.rugmunch.io",
        api_key: str | None = None,
        timeout: float = 30.0,
    ):
        self.base_url = base_url.rstrip("/")
        self.api_key = api_key
        self._client = httpx.AsyncClient(
            base_url=self.base_url,
            timeout=timeout,
            headers={"X-Agent-Id": api_key} if api_key else {},
        )

    async def __aenter__(self):
        return self

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

    async def close(self):
        await self._client.aclose()

    async def _get(self, path: str, **params) -> dict:
        r = await self._client.get(path, params=params)
        r.raise_for_status()
        return r.json()

    async def _post(self, path: str, json: dict | None = None) -> dict:
        r = await self._client.post(path, json=json or {})
        r.raise_for_status()
        return r.json()

    # ── Health / introspection ──────────────────────────────────
    async def get_health(self) -> dict:
        return await self._get("/health")

    async def get_catalog_stats(self) -> dict:
        return await self._get("/api/v1/catalog/stats")

    async def get_x402_catalog(self) -> list[dict]:
        d = await self._get("/api/v1/x402/catalog")
        return d.get("tools", [])

    # ── MCP tools ───────────────────────────────────────────────
    async def list_mcp_tools(self) -> list[McpTool]:
        d = await self._get("/mcp/tools")
        return [McpTool(**t) for t in d.get("tools", [])]

    async def call_mcp_tool(self, name: str, arguments: dict | None = None) -> dict:
        return await self._post(
            f"/mcp/call/{name}", json={"arguments": arguments or {}}
        )

    # ── Catalog (the v4.0 moat) ─────────────────────────────────
    async def get_token_risk(self, chain: str, address: str) -> TokenRisk:
        d = await self._get(f"/api/v1/catalog/tokens/{chain}/{address}/risk")
        return TokenRisk(**d)

    async def get_wallet_analysis(self, chain: str, address: str) -> dict:
        return await self._get(f"/api/v1/catalog/wallets/{chain}/{address}")

    async def resolve_entity(self, wallet_id: str, max_chains: int = 5) -> dict:
        return await self._post(
            "/api/v1/catalog/entities/resolve",
            json={"wallet_id": wallet_id, "max_chains": max_chains},
        )

    # ── RAG ──────────────────────────────────────────────────────
    async def rag_search(
        self, query: str, collection: str = "scam_intel", top_k: int = 5
    ) -> list[RagHit]:
        d = await self._post(
            "/api/v1/rag/v2/search",
            json={"query": query, "collection": collection, "top_k": top_k},
        )
        return [RagHit(**h) for h in d.get("hits", [])]

    async def rag_ingest(
        self, content: str, collection: str = "scam_intel", doc_id: str | None = None
    ) -> dict:
        return await self._post(
            "/api/v1/rag/v2/ingest",
            json={"content": content, "collection": collection, "doc_id": doc_id},
        )

    # ── News (T28) ──────────────────────────────────────────────
    async def get_news_trending(
        self, window_hours: int = 24, limit: int = 20
    ) -> list[NewsItem]:
        d = await self._get(
            "/api/v1/news/trending", window_hours=window_hours, limit=limit
        )
        return [NewsItem(**i) for i in d.get("items", [])]

    async def list_news(self, since_hours: int = 24, limit: int = 20) -> list[NewsItem]:
        d = await self._post(
            "/api/v1/news",
            json={"since_hours": since_hours, "limit": limit},
        )
        return [NewsItem(**i) for i in d.get("items", [])]

    async def get_news(self, news_id: str) -> NewsItem:
        d = await self._get(f"/api/v1/news/{news_id}")
        return NewsItem(**d)

    async def analyze_news(self, news_id: str) -> str | None:
        d = await self._post(f"/api/v1/news/{news_id}/analyze")
        return d.get("analysis")

    # ── Reports (T29) ───────────────────────────────────────────
    async def generate_report(
        self, subject_type: str, subject_id: str, save: bool = True
    ) -> Report:
        d = await self._post(
            "/api/v1/reports/generate",
            json={"subject_type": subject_type, "subject_id": subject_id, "save": save},
        )
        return Report(**d)


# ── Sync convenience (for scripts / notebooks) ────────────────────
class SyncRMI:
    """Sync wrapper. Uses a private event loop per call."""

    def __init__(self, *args, **kwargs):
        self._client = RMI(*args, **kwargs)

    def __enter__(self):
        return self

    def __exit__(self, *exc):
        asyncio.run(self._client.close())

    def __getattr__(self, name):
        attr = getattr(self._client, name)
        if not callable(attr) or name.startswith("_"):
            return attr

        def sync_call(*a, **kw):
            loop = asyncio.new_event_loop()
            try:
                return loop.run_until_complete(attr(*a, **kw))
            finally:
                loop.close()

        return sync_call


def RMI_sync(*args, **kwargs) -> SyncRMI:
    """Shortcut to construct a SyncRMI."""
    return SyncRMI(*args, **kwargs)
