"""Autonomous Intelligence — drop-in crypto tools for AI agents (Python, stdlib-only).

Non-custodial: build_swap returns an UNSIGNED tx; your agent signs it locally with
the user's OWN key; the key never leaves your environment. Discovery tools need no
key. Execution (build_swap/submit_swap) needs a self-serve ai_live_ key from
POST https://autonomousintelligence.io/v1/agent/register.

    from ai_agent_tools import execute, openai_tools, register
    tools = openai_tools()                      # -> your OpenAI/Anthropic tool loop
    quote = execute("get_quote", {"inputMint": IN, "outputMint": OUT, "amount": "10000000"})
    key = register("my-agent").get("key")       # one-time, self-serve
    built = execute("build_swap", {"quoteResponse": quote["quote"], "userPublicKey": PUBKEY}, api_key=key)
    # ...sign built["unsignedTx"] locally with the user's wallet -> signed...
    res = execute("submit_swap", {"signedTransaction": signed, "build_id": built["build_id"]}, api_key=key)
"""
import json
import os
import urllib.parse
import urllib.request

BASE = os.environ.get("AI_BASE_URL", "https://autonomousintelligence.io").rstrip("/")


def _call(method, path, query=None, body=None, api_key=None):
    url = BASE + path
    if query:
        q = {k: str(v) for k, v in query.items() if v is not None}
        if q:
            url += "?" + urllib.parse.urlencode(q)
    data = json.dumps(body).encode() if body is not None else None
    req = urllib.request.Request(url, data=data, method=method)
    req.add_header("Content-Type", "application/json")
    if api_key:
        req.add_header("Authorization", "Bearer " + api_key)
    try:
        with urllib.request.urlopen(req, timeout=20) as r:
            return json.loads(r.read().decode() or "{}")
    except Exception as e:  # noqa: BLE001 - surface any transport error uniformly
        return {"ok": False, "error": {"code": "request_failed", "message": str(e)[:180]}}


# Single source of truth: name, keyed?, transport, description, JSON-schema args.
TOOLS = [
    {"name": "get_quote", "keyed": False, "method": "GET", "path": "/v1/agent/quote", "loc": "query",
     "description": "Best-route swap quote for inputMint -> outputMint of amount (base units). The 2% platform fee is disclosed in the result.",
     "parameters": {"type": "object", "required": ["inputMint", "outputMint", "amount"], "properties": {
         "inputMint": {"type": "string"}, "outputMint": {"type": "string"},
         "amount": {"type": "string", "description": "base units, integer string"},
         "slippageBps": {"type": "string", "description": "optional, default 50"}}}},
    {"name": "check_token_safety", "keyed": False, "method": "POST", "path": "/v1/agent/safety", "loc": "body",
     "description": "Rug-gate a token: honeypot, mint/freeze authority, liquidity lock, holder concentration.",
     "parameters": {"type": "object", "required": ["mint"], "properties": {"mint": {"type": "string"}}}},
    {"name": "find_gems", "keyed": False, "method": "GET", "path": "/v1/agent/gems", "loc": "query",
     "description": "Rug-gated new/trending/graduated tokens. tab: new | trending | graduated.",
     "parameters": {"type": "object", "properties": {"tab": {"type": "string"}, "limit": {"type": "number"}}}},
    {"name": "get_token_info", "keyed": False, "method": "GET", "path": "/v1/agent/token", "loc": "query",
     "description": "Live price, liquidity and market cap for a mint.",
     "parameters": {"type": "object", "required": ["mint"], "properties": {"mint": {"type": "string"}}}},
    {"name": "list_tradeable_tokens", "keyed": False, "method": "GET", "path": "/v1/agent/tokens", "loc": "query",
     "description": "Every token swappable on the DEX.",
     "parameters": {"type": "object", "properties": {}}},
    {"name": "build_swap", "keyed": True, "method": "POST", "path": "/v1/agent/swap/build", "loc": "body",
     "description": "Build an UNSIGNED swap tx (2% fee baked in) + a build_id. NON-CUSTODIAL: sign it locally with the user's own wallet, then call submit_swap with the same build_id. Requires an API key.",
     "parameters": {"type": "object", "required": ["quoteResponse", "userPublicKey"], "properties": {
         "quoteResponse": {"type": "object", "description": "the `quote` object from get_quote"},
         "userPublicKey": {"type": "string", "description": "the user's own wallet public key"}}}},
    {"name": "submit_swap", "keyed": True, "method": "POST", "path": "/v1/agent/swap/submit", "loc": "body",
     "description": "Broadcast an already-signed swap tx with its build_id. Returns signature + status (confirmed | pending | failed; never fabricated). Requires an API key.",
     "parameters": {"type": "object", "required": ["signedTransaction", "build_id"], "properties": {
         "signedTransaction": {"type": "string"}, "build_id": {"type": "string"}}}},
]
_BY_NAME = {t["name"]: t for t in TOOLS}


def execute(name, args=None, api_key=None):
    """Run a tool by name. Pass api_key for build_swap / submit_swap."""
    t = _BY_NAME.get(name)
    if not t:
        return {"ok": False, "error": {"code": "unknown_tool", "message": name}}
    if t["keyed"] and not api_key:
        return {"ok": False, "error": {"code": "key_required",
                "message": name + " needs an API key — register at " + BASE + "/v1/agent/register"}}
    args = args or {}
    if t["loc"] == "query":
        return _call(t["method"], t["path"], query=args, api_key=api_key)
    return _call(t["method"], t["path"], body=args, api_key=api_key)


def openai_tools():
    """OpenAI / Anthropic function-calling tool list (full JSON schemas)."""
    return [{"type": "function", "function": {
        "name": t["name"], "description": t["description"], "parameters": t["parameters"]}} for t in TOOLS]


def register(agent_name, contact=None, ref=None):
    """Self-serve registration -> {"ok": True, "key": "ai_live_..."}. Call once; store the key.
    `ref` tags this key's future volume for rev-share attribution."""
    return _call("POST", "/v1/agent/register", body={"agent_name": agent_name, "contact": contact, "ref": ref})
