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

DEFAULT_BASE = "https://zoralinks.com/api/v1"


class ZoraLinksError(Exception):
    def __init__(self, status, error, message, payload=None):
        super().__init__(message or error or f"ZoraLinks request failed ({status})")
        self.status = status
        self.error = error or "request_error"
        self.payload = payload or {}


class ZoraLinks:
    def __init__(self, api_key=None, base_url=DEFAULT_BASE, timeout=20):
        self.api_key = api_key or os.environ.get("ZORALINKS_API_KEY")
        if not self.api_key:
            raise ValueError("ZoraLinks: falta api_key o ZORALINKS_API_KEY")
        self.base_url = str(base_url).rstrip("/")
        self.timeout = timeout

    def _request(self, method, path, params=None, body=None):
        url = f"{self.base_url}{path}"
        if params:
            clean = {key: value for key, value in params.items() if value is not None and value != ""}
            if clean:
                url += "?" + urllib.parse.urlencode(clean)
        data = json.dumps(body).encode("utf-8") if body is not None else None
        request = urllib.request.Request(
            url,
            data=data,
            method=method,
            headers={
                "Accept": "application/json",
                "Authorization": f"Bearer {self.api_key}",
                "User-Agent": "ZoraLinks-Python-SDK/1.0",
                **({"Content-Type": "application/json"} if body is not None else {}),
            },
        )
        try:
            with urllib.request.urlopen(request, timeout=self.timeout) as response:
                return json.loads(response.read().decode("utf-8"))
        except urllib.error.HTTPError as error:
            raw = error.read().decode("utf-8") or "{}"
            try:
                payload = json.loads(raw)
            except json.JSONDecodeError:
                payload = {"error": "request_error", "message": raw[:300]}
            raise ZoraLinksError(error.code, payload.get("error"), payload.get("message"), payload) from None

    def me(self):
        return self._request("GET", "/me")

    def list_links(self, limit=50, offset=0, status=None):
        return self._request("GET", "/links", params={"limit": limit, "offset": offset, "status": status})

    def get_link(self, id_or_alias):
        return self._request("GET", f"/links/{urllib.parse.quote(str(id_or_alias), safe='')}")

    def create_link(self, alias, destination, title=None, steps=3):
        return self._request(
            "POST",
            "/links",
            body={"alias": alias, "destination": destination, "title": title, "steps": steps},
        )

    def update_link(self, id_or_alias, data, method="PATCH"):
        return self._request(method, f"/links/{urllib.parse.quote(str(id_or_alias), safe='')}", body=data)

    def delete_link(self, id_or_alias):
        return self._request("DELETE", f"/links/{urllib.parse.quote(str(id_or_alias), safe='')}")

    def stats(self, period="all"):
        return self._request("GET", "/stats", params={"period": period})

    def analytics(self, period="30d"):
        return self._request("GET", "/analytics", params={"period": period})

    def link_analytics(self, id_or_alias, period="30d"):
        return self._request(
            "GET",
            f"/links/{urllib.parse.quote(str(id_or_alias), safe='')}/analytics",
            params={"period": period},
        )
