"""
Zyphon Python Client SDK
Zero-dependency client using standard library urllib and json.
"""

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


class ZyphonError(Exception):
    def __init__(self, message: str, code: str, status: int, details: Optional[Dict[str, Any]] = None):
        super().__init__(message)
        self.code = code
        self.status = status
        self.details = details or {}


class ZyphonClient:
    def __init__(self, base_url: str, api_key: str, timeout_seconds: float = 10.0):
        self.base_url = base_url.rstrip("/")
        self.api_key = api_key
        self.timeout = timeout_seconds

    def send(
        self,
        channel: str,
        recipient: str,
        content: Dict[str, Any],
        sync: bool = True,
        idempotency_key: Optional[str] = None,
        metadata: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        """
        Sends a normalized message through Zyphon.
        """
        url = f"{self.base_url}/api/v1/messages"
        if not sync:
            url += "?sync=false"

        payload = {
            "channel": channel,
            "recipient": recipient,
            "content": content,
        }
        if metadata:
            payload["metadata"] = metadata

        data = json.dumps(payload).encode("utf-8")
        headers = {
            "Content-Type": "application/json",
            "X-API-Key": self.api_key,
        }
        if idempotency_key:
            headers["X-Idempotency-Key"] = idempotency_key

        req = urllib.request.Request(url, data=data, headers=headers, method="POST")

        try:
            with urllib.request.urlopen(req, timeout=self.timeout) as response:
                return json.loads(response.read().decode("utf-8"))
        except urllib.error.HTTPError as e:
            raw_body = e.read().decode("utf-8")
            try:
                err_data = json.loads(raw_body)
                code = err_data.get("error", {}).get("code", "DISPATCH_FAILED")
                msg = err_data.get("error", {}).get("message", f"HTTP {e.code}")
            except Exception:
                err_data = {"raw": raw_body}
                code = "HTTP_ERROR"
                msg = f"HTTP {e.code}"
            raise ZyphonError(msg, code, e.code, err_data)
        except urllib.error.URLError as e:
            raise ZyphonError(str(e.reason), "NETWORK_ERROR", 500)

    def send_email(
        self,
        recipient: str,
        subject: str,
        body: str,
        html: Optional[str] = None,
        sync: bool = True,
        idempotency_key: Optional[str] = None,
        metadata: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        content = {"type": "text", "subject": subject, "body": body}
        if html:
            content["html"] = html
        return self.send("email", recipient, content, sync, idempotency_key, metadata)

    def send_sms(
        self,
        recipient: str,
        body: str,
        sync: bool = True,
        idempotency_key: Optional[str] = None,
        metadata: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        return self.send("sms", recipient, {"type": "text", "body": body}, sync, idempotency_key, metadata)

    def send_whatsapp(
        self,
        recipient: str,
        body: str,
        sync: bool = True,
        idempotency_key: Optional[str] = None,
        metadata: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        return self.send("whatsapp", recipient, {"type": "text", "body": body}, sync, idempotency_key, metadata)

    def health(self) -> Dict[str, Any]:
        req = urllib.request.Request(f"{self.base_url}/api/v1/health")
        with urllib.request.urlopen(req, timeout=self.timeout) as response:
            return json.loads(response.read().decode("utf-8"))

