"""SendInbox — официальный Python SDK (REST API v1).

Требуется Python 3.7+. Зависимостей нет (только стандартная библиотека).
Ключ: кабинет → Настройки → API-ключи.

    from sendinbox import SendInbox

    si = SendInbox("sk_live_ВАШ_КЛЮЧ")
    si.emails.send(
        to="user@mail.ru",
        subject="Привет",
        html="<b>Тест из SDK</b>",
        fromEmail="news@ваш-домен.ru",
    )

Все методы возвращают dict (распарсенный JSON) и бросают SendInboxError
при HTTP-ошибке (в исключении доступны .status и .body).
"""

import json
import urllib.request
import urllib.parse
import urllib.error

__all__ = ["SendInbox", "SendInboxError"]

DEFAULT_BASE_URL = "https://app.working-esender.ru"


class SendInboxError(Exception):
    def __init__(self, message, status=0, body=None):
        super().__init__(message)
        self.status = status
        self.body = body


def _qs(params):
    if not params:
        return ""
    clean = {k: v for k, v in params.items() if v is not None and v != ""}
    return ("?" + urllib.parse.urlencode(clean)) if clean else ""


class _Crud:
    """Ресурс с полным CRUD (списки, сегменты, подписчики)."""

    def __init__(self, client, base):
        self._c = client
        self._base = base

    def list(self, **params):
        return self._c.request("GET", self._base + _qs(params))

    def create(self, **data):
        return self._c.request("POST", self._base, data)

    def get(self, id):
        return self._c.request("GET", self._base + "/" + urllib.parse.quote(str(id), safe=""))

    def update(self, id, **data):
        return self._c.request("PATCH", self._base + "/" + urllib.parse.quote(str(id), safe=""), data)

    def remove(self, id):
        return self._c.request("DELETE", self._base + "/" + urllib.parse.quote(str(id), safe=""))


class _Emails:
    def __init__(self, client):
        self._c = client

    def send(self, **payload):
        return self._c.request("POST", "/api/v1/emails", payload)

    def get(self, id):
        return self._c.request("GET", "/api/v1/emails/" + urllib.parse.quote(str(id), safe=""))

    def verify(self, email):
        return self._c.request("POST", "/api/v1/emails/info", {"email": email})


class _Campaigns:
    def __init__(self, client):
        self._c = client

    def list(self):
        return self._c.request("GET", "/api/v1/campaigns")

    def create(self, **data):
        return self._c.request("POST", "/api/v1/campaigns", data)

    def get(self, id):
        return self._c.request("GET", "/api/v1/campaigns/" + urllib.parse.quote(str(id), safe=""))

    def send(self, id):
        return self._c.request("POST", "/api/v1/campaigns/" + urllib.parse.quote(str(id), safe="") + "/send")


class _Suppression:
    def __init__(self, client):
        self._c = client

    def list(self, **params):
        return self._c.request("GET", "/api/v1/suppression" + _qs(params))

    def add(self, email, reason=None):
        return self._c.request("POST", "/api/v1/suppression", {"email": email, "reason": reason})

    def remove(self, email):
        return self._c.request("DELETE", "/api/v1/suppression" + _qs({"email": email}))


class _Templates:
    def __init__(self, client):
        self._c = client

    def list(self):
        return self._c.request("GET", "/api/v1/templates")

    def create(self, **data):
        return self._c.request("POST", "/api/v1/templates", data)


class _Automations:
    def __init__(self, client):
        self._c = client

    def trigger(self, event, email, data=None):
        return self._c.request("POST", "/api/v1/automations/trigger", {"event": event, "email": email, "data": data or {}})


class SendInbox:
    def __init__(self, api_key, base_url=DEFAULT_BASE_URL, timeout=30):
        if not api_key:
            raise SendInboxError("Не указан API-ключ")
        self.api_key = api_key
        self.base_url = base_url.rstrip("/")
        self.timeout = timeout
        self.emails = _Emails(self)
        self.lists = _Crud(self, "/api/v1/lists")
        self.subscribers = _Crud(self, "/api/v1/subscribers")
        self.segments = _Crud(self, "/api/v1/segments")
        self.campaigns = _Campaigns(self)
        self.suppression = _Suppression(self)
        self.templates = _Templates(self)
        self.automations = _Automations(self)

    def stats(self):
        """Агрегаты аккаунта за всё время."""
        return self.request("GET", "/api/v1/stats")

    def request(self, method, path, body=None):
        """Низкоуровневый запрос."""
        data = None
        headers = {"Authorization": "Bearer " + self.api_key, "Accept": "application/json"}
        if body is not None:
            data = json.dumps(body, ensure_ascii=False).encode("utf-8")
            headers["Content-Type"] = "application/json"
        req = urllib.request.Request(self.base_url + path, data=data, headers=headers, method=method)
        try:
            with urllib.request.urlopen(req, timeout=self.timeout) as resp:
                raw = resp.read().decode("utf-8")
                return json.loads(raw) if raw else None
        except urllib.error.HTTPError as e:
            raw = e.read().decode("utf-8", "replace")
            parsed = None
            try:
                parsed = json.loads(raw) if raw else None
            except ValueError:
                parsed = raw
            msg = parsed.get("error") if isinstance(parsed, dict) else ("HTTP %d" % e.code)
            raise SendInboxError(msg, e.code, parsed)
        except urllib.error.URLError as e:
            raise SendInboxError("Сетевая ошибка: %s" % e.reason)
