"""Session-aware HTTP client.

Thin urllib wrapper with cookie-jar-based session management.
Intended to complement byteb4rb1e.utils.http.client (which is
stateless) for use cases that require login and session cookies.

This module is a migration candidate for byteb4rb1e-utils.
"""

import http.cookiejar
import urllib.parse
import urllib.request
from dataclasses import dataclass, field
from typing import Any


@dataclass
class HttpResponse:
    """Wrapper around a raw urllib response."""

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

    @property
    def text(self) -> str:
        return self.body.decode("utf-8", errors="replace")


class HttpSession:
    """HTTP client that persists cookies across requests.

    Suitable for sites that require login followed by
    cookie-authenticated page fetches.
    """

    def __init__(
        self,
        default_headers: dict[str, str] | None = None,
        timeout: int = 30,
    ) -> None:
        self._timeout = timeout
        self._default_headers = default_headers or {}
        self._jar = http.cookiejar.CookieJar()
        self._opener = urllib.request.build_opener(
            urllib.request.HTTPCookieProcessor(self._jar),
        )

    def get(
        self,
        url: str,
        params: dict[str, str] | None = None,
        headers: dict[str, str] | None = None,
    ) -> HttpResponse:
        if params:
            query = urllib.parse.urlencode(params)
            url = f"{url}?{query}"

        req = urllib.request.Request(
            url,
            headers=self._merged_headers(headers),
            method="GET",
        )
        return self._send(req)

    def post(
        self,
        url: str,
        data: dict[str, str] | None = None,
        headers: dict[str, str] | None = None,
    ) -> HttpResponse:
        body = (
            urllib.parse.urlencode(data).encode()
            if data else None
        )

        merged = self._merged_headers(headers)
        if data and "Content-Type" not in merged:
            merged["Content-Type"] = (
                "application/x-www-form-urlencoded"
            )

        req = urllib.request.Request(
            url,
            data=body,
            headers=merged,
            method="POST",
        )
        return self._send(req)

    def _send(self, req: urllib.request.Request) -> HttpResponse:
        try:
            with self._opener.open(
                req, timeout=self._timeout
            ) as resp:
                return HttpResponse(
                    status_code=resp.getcode(),
                    headers=dict(resp.getheaders()),
                    body=resp.read(),
                )
        except urllib.error.HTTPError as e:
            return HttpResponse(
                status_code=e.code,
                headers=dict(e.headers.items()),
                body=e.read(),
            )

    def _merged_headers(
        self, extra: dict[str, str] | None
    ) -> dict[str, str]:
        merged = dict(self._default_headers)
        if extra:
            merged.update(extra)
        return merged
