"""Prompt TM client for Python 3.9+. One file, standard library only.

Download: https://www.prompt-tm.com/sdk/prompt_tm.py

    from prompt_tm import PromptTM
    ptm = PromptTM(api_key=os.environ["PROMPT_TM_KEY"])
    link = ptm.post("/connected_accounts/link", {"auth_config_id": "ac_...", "user_id": "alice"})
    result = ptm.execute("GITHUB_LIST_REPOSITORIES", user_id="alice", arguments={"per_page": 5})
"""
import base64
import hashlib
import hmac
import json
import time
import urllib.error
import urllib.parse
import urllib.request


class PromptTMError(Exception):
    def __init__(self, status, body):
        super().__init__(f"Prompt TM API {status}: {body}")
        self.status, self.body = status, body


class PromptTM:
    def __init__(self, api_key, base_url="https://www.prompt-tm.com"):
        self.api_key, self.base_url = api_key, base_url.rstrip("/")

    def request(self, method, path, body=None, query=None):
        url = f"{self.base_url}/api/v3{path}"
        if query:
            url += "?" + urllib.parse.urlencode({k: v for k, v in query.items() if v is not None})
        data = None if body is None else json.dumps(body).encode()
        req = urllib.request.Request(url, data=data, method=method, headers={"x-api-key": self.api_key, "content-type": "application/json"})
        try:
            with urllib.request.urlopen(req, timeout=70) as res:
                return json.loads(res.read() or b"null")
        except urllib.error.HTTPError as err:
            raw = err.read()
            try:
                payload = json.loads(raw or b"null")
            except ValueError:
                raise PromptTMError(err.code, raw[:300].decode(errors="replace")) from None
            # A failed tool call answers 400 with {"successful": false}: a result, not an exception.
            if isinstance(payload, dict) and "successful" in payload:
                return payload
            raise PromptTMError(err.code, payload) from None

    def get(self, path, **query):
        return self.request("GET", path, query=query)

    def post(self, path, body=None):
        return self.request("POST", path, body or {})

    def put(self, path, body=None):
        return self.request("PUT", path, body or {})

    def patch(self, path, body=None):
        return self.request("PATCH", path, body or {})

    def delete(self, path):
        return self.request("DELETE", path)

    def execute(self, tool_slug, user_id=None, connected_account_id=None, arguments=None):
        """Runs a tool. Returns {"data", "error", "successful", "log_id"}."""
        return self.post(f"/tools/execute/{tool_slug}", {"user_id": user_id, "connected_account_id": connected_account_id, "arguments": arguments or {}})

    def tools(self, toolkit_slug=None, fmt=None, query=None):
        """Tool definitions; fmt="openai", "openai_responses", "anthropic" or "mcp" gives SDK-ready shapes."""
        return self.get("/tools", toolkit_slug=toolkit_slug, format=fmt, query=query)["items"]

    def session(self, user_id, toolkits=None):
        """A router session for one user: returns session_id and mcp.url."""
        return self.post("/tool_router/session", {"user_id": user_id, "toolkits": toolkits})


def verify_webhook(secret, headers, raw_body, tolerance=300):
    """Checks a delivery's Standard Webhooks signature. headers: a dict-like, raw_body: str."""
    wid, ts, sigs = headers.get("webhook-id"), headers.get("webhook-timestamp"), headers.get("webhook-signature")
    try:
        if not (wid and ts and sigs) or abs(time.time() - int(ts)) > tolerance:
            return False
    except ValueError:
        return False
    mac = hmac.new(secret.encode(), f"{wid}.{ts}.{raw_body}".encode(), hashlib.sha256).digest()
    expected = "v1," + base64.b64encode(mac).decode()
    return any(hmac.compare_digest(s, expected) for s in sigs.split(" "))
