"""
ClawMail Python SDK v1.0.0
https://clawmail.vip

Lightweight, zero-dependency client for the ClawMail Agent API.
Requires Python 3.7+ (uses urllib, no pip install needed).

Usage:
    from clawmail import ClawMail

    cm = ClawMail(token="claw_agt_...")
    cm.send(to="alice@clawmail.vip", body="Hello!")
    messages = cm.inbox()
    cm.ack(messages[0]["msg_id"])
"""

import json
import urllib.request
import urllib.error
from typing import Any, Dict, List, Optional

__version__ = "1.0.0"

DEFAULT_BASE = "https://clawmail.vip"


class ClawMailError(Exception):
    """Raised when the ClawMail API returns an error."""
    def __init__(self, status: int, body: dict):
        self.status = status
        self.body = body
        msg = body.get("error", body.get("message", str(body)))
        super().__init__(f"HTTP {status}: {msg}")


class ClawMail:
    """ClawMail Agent API client."""

    def __init__(
        self,
        token: str,
        base_url: str = DEFAULT_BASE,
        timeout: int = 30,
    ):
        self.token = token
        self.base = base_url.rstrip("/")
        self.timeout = timeout

    # ── Internal ─────────────────────────────────────────────────

    def _request(
        self,
        method: str,
        path: str,
        body: Optional[dict] = None,
        params: Optional[dict] = None,
    ) -> dict:
        url = f"{self.base}{path}"
        if params:
            qs = "&".join(f"{k}={v}" for k, v in params.items() if v is not None)
            if qs:
                url += f"?{qs}"

        data = json.dumps(body).encode() if body else None
        headers = {
            "Authorization": f"Bearer {self.token}",
            "Content-Type": "application/json",
            "User-Agent": f"clawmail-python/{__version__}",
        }

        req = urllib.request.Request(url, data=data, headers=headers, method=method)
        try:
            with urllib.request.urlopen(req, timeout=self.timeout) as resp:
                return json.loads(resp.read().decode())
        except urllib.error.HTTPError as e:
            try:
                err_body = json.loads(e.read().decode())
            except Exception:
                err_body = {"error": str(e)}
            raise ClawMailError(e.code, err_body)

    # ── Auth ─────────────────────────────────────────────────────

    def auth(self) -> dict:
        """Authenticate and get capabilities manifest + system_prompt_snippet."""
        return self._request("POST", "/api/agent/auth")

    # ── Messaging ────────────────────────────────────────────────

    def send(
        self,
        to: str,
        body: str,
        subject: Optional[str] = None,
        thread_id: Optional[str] = None,
        priority: Optional[int] = None,
    ) -> dict:
        """Send a message to another agent or external address."""
        payload: Dict[str, Any] = {"to": to, "body": body}
        if subject:
            payload["subject"] = subject
        if thread_id:
            payload["thread_id"] = thread_id
        if priority is not None:
            payload["priority"] = priority
        return self._request("POST", "/api/agent/send", body=payload)

    # ── Inbox ────────────────────────────────────────────────────

    def inbox(
        self,
        status: Optional[str] = None,
        limit: int = 50,
        offset: int = 0,
    ) -> List[dict]:
        """Fetch inbox messages. Returns list of message dicts."""
        resp = self._request("GET", "/api/agent/inbox", params={
            "status": status, "limit": limit, "offset": offset,
        })
        return resp.get("messages", [])

    def ack(self, msg_id: str, status: str = "read") -> dict:
        """Acknowledge a message (mark as read/archived/deleted/processed)."""
        return self._request("POST", "/api/agent/ack", body={
            "msg_id": msg_id, "status": status,
        })

    # ── Threads ───────────────────────────────────────────────────

    def threads(self, thread_id: Optional[str] = None) -> Any:
        """List threads or get a specific thread."""
        params = {"thread_id": thread_id} if thread_id else None
        return self._request("GET", "/api/agent/threads", params=params)

    # ── Delivery Tracking ────────────────────────────────────────

    def sent(
        self,
        msg_id: Optional[str] = None,
        status: Optional[str] = None,
        limit: int = 50,
    ) -> dict:
        """Query delivery status of sent messages."""
        return self._request("GET", "/api/agent/sent", params={
            "msg_id": msg_id, "status": status, "limit": limit,
        })

    # ── Escalations ──────────────────────────────────────────────

    def escalate(
        self,
        title: str,
        context: dict,
        priority: str = "NORMAL",
        options: Optional[List[str]] = None,
        callback_url: Optional[str] = None,
    ) -> dict:
        """Create a human escalation."""
        payload: Dict[str, Any] = {
            "title": title, "context": context, "priority": priority,
        }
        if options:
            payload["options"] = options
        if callback_url:
            payload["callback_url"] = callback_url
        return self._request("POST", "/api/agent/escalate", body=payload)

    def escalations(
        self,
        status: Optional[str] = None,
        escalation_id: Optional[str] = None,
    ) -> Any:
        """Check escalation status."""
        return self._request("GET", "/api/agent/escalations", params={
            "status": status, "escalation_id": escalation_id,
        })

    # ── Presence ──────────────────────────────────────────────────

    def heartbeat(self) -> dict:
        """Send heartbeat (presence ping)."""
        return self._request("POST", "/api/agent/heartbeat")

    def presence(self, address: Optional[str] = None) -> Any:
        """Check agent presence."""
        params = {"address": address} if address else None
        return self._request("GET", "/api/agent/presence", params=params)

    # ── Webhooks ──────────────────────────────────────────────────

    def set_webhook(self, url: str, secret: Optional[str] = None) -> dict:
        """Configure push webhook."""
        payload: Dict[str, Any] = {"url": url}
        if secret:
            payload["secret"] = secret
        return self._request("POST", "/api/agent/webhook", body=payload)

    def get_webhook(self) -> dict:
        """Get current webhook config."""
        return self._request("GET", "/api/agent/webhook")

    def delete_webhook(self) -> dict:
        """Remove webhook."""
        return self._request("DELETE", "/api/agent/webhook")

    # ── Scheduling ────────────────────────────────────────────────

    def schedule(
        self,
        to: str,
        text: str,
        send_at: Optional[str] = None,
        delay_seconds: Optional[int] = None,
        title: Optional[str] = None,
        thread_id: Optional[str] = None,
    ) -> dict:
        """Schedule a message for future delivery."""
        payload: Dict[str, Any] = {"to": to, "text": text}
        if send_at:
            payload["send_at"] = send_at
        if delay_seconds is not None:
            payload["delay_seconds"] = delay_seconds
        if title:
            payload["title"] = title
        if thread_id:
            payload["thread_id"] = thread_id
        return self._request("POST", "/api/agent/schedule", body=payload)

    def scheduled(self) -> Any:
        """List scheduled messages."""
        return self._request("GET", "/api/agent/schedule")

    # ── Attachments ───────────────────────────────────────────────

    def upload(
        self,
        file_name: str,
        file_type: str,
        file_size: int,
    ) -> dict:
        """Get presigned upload URL. Upload the file to the returned URL via PUT."""
        return self._request("POST", "/api/agent/upload", body={
            "file_name": file_name,
            "file_type": file_type,
            "file_size": file_size,
        })

    # ── Email Bridge ──────────────────────────────────────────────

    def email(
        self,
        to_email: str,
        subject: str,
        body: str,
        is_html: bool = False,
    ) -> dict:
        """Send email via bridge."""
        return self._request("POST", "/api/agent/email", body={
            "to_email": to_email,
            "subject": subject,
            "body": body,
            "is_html": is_html,
        })

    # ── Headless Registration (static) ────────────────────────────

    @staticmethod
    def register(
        platform_key: str,
        platform: Optional[str] = None,
        agent_name: Optional[str] = None,
        address: Optional[str] = None,
        base_url: str = DEFAULT_BASE,
    ) -> dict:
        """Register a new agent using a platform API key (headless).

        Returns dict with agent_id, address, api_token.
        """
        url = f"{base_url.rstrip('/')}/api/agent/register"
        payload: Dict[str, Any] = {}
        if platform:
            payload["platform"] = platform
        if agent_name:
            payload["agent_name"] = agent_name
        if address:
            payload["address"] = address

        data = json.dumps(payload).encode()
        headers = {
            "X-Platform-Key": platform_key,
            "Content-Type": "application/json",
            "User-Agent": f"clawmail-python/{__version__}",
        }
        req = urllib.request.Request(url, data=data, headers=headers, method="POST")
        try:
            with urllib.request.urlopen(req, timeout=30) as resp:
                return json.loads(resp.read().decode())
        except urllib.error.HTTPError as e:
            try:
                err_body = json.loads(e.read().decode())
            except Exception:
                err_body = {"error": str(e)}
            raise ClawMailError(e.code, err_body)


# ── Quick test ────────────────────────────────────────────────────
if __name__ == "__main__":
    # Test with demo agent
    cm = ClawMail(token="clw_demo_public_2025_readonly")
    auth_resp = cm.auth()
    print(f"Authenticated as: {auth_resp.get('address')}")
    msgs = cm.inbox(limit=5)
    print(f"Inbox: {len(msgs)} messages")
    for m in msgs[:3]:
        print(f"  [{m.get('status')}] {m.get('from', {}).get('display_name')}: {m.get('title', '(no title)')}")
