"""
Thin synchronous HTTP helper built on :mod:`urllib.request`.

Translates HTTP errors into typed :mod:`markitect.llm.exceptions`.
"""

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

from markitect.llm.exceptions import (
    LLMAPIError,
    LLMRateLimitError,
    LLMTimeoutError,
)


def post_json(
    url: str,
    payload: Dict[str, Any],
    headers: Optional[Dict[str, str]] = None,
    timeout: int = 300,
) -> Dict[str, Any]:
    """POST *payload* as JSON and return the parsed response body.

    Raises:
        LLMRateLimitError: on HTTP 429
        LLMAPIError: on other non-2xx responses
        LLMTimeoutError: on socket / read timeout
    """
    data = json.dumps(payload).encode()
    req = urllib.request.Request(
        url,
        data=data,
        headers={"Content-Type": "application/json", **(headers or {})},
        method="POST",
    )

    try:
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            body = resp.read().decode()
            try:
                return json.loads(body)
            except json.JSONDecodeError as exc:
                preview = body[:300].replace("\n", "\\n")
                raise LLMAPIError(
                    f"Invalid JSON response from {url}: {exc} — body preview: {preview!r}",
                    cause=exc,
                ) from exc
    except urllib.error.HTTPError as exc:
        body = ""
        try:
            body = exc.read().decode()
        except Exception:
            pass

        if exc.code == 429:
            raise LLMRateLimitError(
                f"Rate limited (429) from {url}",
                status_code=429,
                response_body=body,
                cause=exc,
            ) from exc

        raise LLMAPIError(
            f"HTTP {exc.code} from {url}",
            status_code=exc.code,
            response_body=body,
            cause=exc,
        ) from exc
    except urllib.error.URLError as exc:
        if "timed out" in str(exc.reason):
            raise LLMTimeoutError(
                f"Request to {url} timed out after {timeout}s",
                cause=exc,
            ) from exc
        raise LLMAPIError(
            f"URL error for {url}: {exc.reason}",
            cause=exc,
        ) from exc
    except TimeoutError as exc:
        raise LLMTimeoutError(
            f"Request to {url} timed out after {timeout}s",
            cause=exc,
        ) from exc
