from __future__ import annotations

import json
import urllib.error
import urllib.request
from typing import Any

from browseable.config import get_daemon_url


class ClientError(RuntimeError):
    def __init__(self, message: str, *, status_code: int | None = None):
        super().__init__(message)
        self.status_code = status_code


def request_json(
    method: str,
    path: str,
    payload: dict[str, Any] | None = None,
    *,
    base_url: str | None = None,
    timeout: float = 15.0,
) -> dict[str, Any]:
    url = f"{base_url or get_daemon_url()}{path}"
    body = None
    headers = {"Accept": "application/json"}
    if payload is not None:
        body = json.dumps(payload).encode("utf-8")
        headers["Content-Type"] = "application/json"

    req = urllib.request.Request(url, method=method.upper(), data=body, headers=headers)
    try:
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            raw = resp.read().decode("utf-8")
            if not raw:
                return {}
            return json.loads(raw)
    except urllib.error.HTTPError as exc:
        try:
            error_payload = json.loads(exc.read().decode("utf-8"))
            message = error_payload.get("error", str(exc))
        except Exception:
            message = str(exc)
        raise ClientError(message, status_code=exc.code) from exc
    except urllib.error.URLError as exc:
        raise ClientError(f"Cannot connect to browseable daemon: {exc.reason}") from exc
    except OSError as exc:
        raise ClientError(f"Cannot connect to browseable daemon: {exc}") from exc
