"""HTTP fetch — stdlib wrapper around urllib.request.urlopen."""

from __future__ import annotations

from dataclasses import dataclass
from typing import Mapping
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen

__all__ = ["fetch"]


@dataclass(frozen=True, slots=True)
class FetchResult:
    """Response from :func:`fetch`."""

    status: int
    headers: dict[str, str]
    body: bytes

    def text(self, encoding: str = "utf-8", errors: str = "strict") -> str:
        return self.body.decode(encoding, errors)


def fetch(
    url: str,
    *,
    method: str = "GET",
    headers: Mapping[str, str] | None = None,
    data: bytes | None = None,
    timeout: float | None = None,
) -> FetchResult:
    """
    Perform an HTTP request and return status, response headers, and body.

    Raises :exc:`urllib.error.HTTPError` for 4xx/5xx (same as urlopen).
    Raises :exc:`urllib.error.URLError` on network failure.
    """
    hdrs = dict(headers) if headers else {}
    req = Request(url, data=data, headers=hdrs, method=method.upper())

    try:
        with urlopen(req, timeout=timeout) as resp:
            raw_headers = resp.headers
            header_map = {k.lower(): v for k, v in raw_headers.items()}
            body = resp.read()
            status = getattr(resp, "status", 200)
            return FetchResult(status=status, headers=header_map, body=body)
    except HTTPError as e:
        body = e.read()
        hdrs_err = {k.lower(): v for k, v in e.headers.items()} if e.headers else {}
        return FetchResult(status=e.code, headers=hdrs_err, body=body)
