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


DEFAULT_BASE_URL = "https://www.productresolver.com"


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


class ProductResolverClient:
    def __init__(self, api_key=None, base_url=DEFAULT_BASE_URL):
        self.api_key = api_key or ""
        self.base_url = base_url.rstrip("/")

    def billing_url(self):
        return self.base_url + "/billing"

    def can_resolve(self, intent, mode=None):
        params = {"intent": intent}
        if mode:
            params["mode"] = mode
        return self._request("GET", "/can-resolve", query=params)

    def resolve(self, intent, **options):
        self._require_api_key()
        payload = {"intent": intent}
        payload.update(options)
        return self._request("POST", "/resolve", payload=payload, auth=True)

    def batch_resolve(self, intents, **options):
        self._require_api_key()
        payload = {"intents": intents}
        payload.update(options)
        return self._request("POST", "/resolve/batch", payload=payload, auth=True)

    def balance(self):
        self._require_api_key()
        return self._request("GET", "/billing/balance", auth=True)

    def _require_api_key(self):
        if not self.api_key:
            raise ProductResolverError(
                "Product Resolver API key required. Buy credits at " + self.billing_url()
            )

    def _request(self, method, path, query=None, payload=None, auth=False):
        url = self.base_url + path
        if query:
            url += "?" + urllib.parse.urlencode(query)

        body = None
        headers = {"accept": "application/json"}
        if payload is not None:
            body = json.dumps(payload).encode("utf-8")
            headers["content-type"] = "application/json"
        if auth:
            headers["authorization"] = "Bearer " + self.api_key

        request = urllib.request.Request(url, data=body, headers=headers, method=method)
        try:
            with urllib.request.urlopen(request, timeout=30) as response:
                return json.loads(response.read().decode("utf-8"))
        except urllib.error.HTTPError as exc:
            raw = exc.read().decode("utf-8")
            try:
                parsed = json.loads(raw)
            except json.JSONDecodeError:
                parsed = {"raw": raw}
            message = parsed.get("message") or parsed.get("error") or str(exc)
            raise ProductResolverError(message, status=exc.code, body=parsed) from exc
