"""HTTP session factory and etag helpers for extraction pipelines."""

import niquests


def create_session(timeout_seconds: int = 60, max_retries: int = 3) -> niquests.Session:
    """Return a new niquests Session.

    timeout_seconds is stored as an attribute so callers that need a consistent
    timeout can reference it via session.timeout_seconds.
    """
    assert timeout_seconds > 0, f"timeout_seconds must be positive, got {timeout_seconds}"
    assert max_retries >= 0, f"max_retries must be non-negative, got {max_retries}"
    session = niquests.Session()
    session.timeout_seconds = timeout_seconds  # type: ignore[attr-defined]
    return session


def normalize_etag(raw: str) -> str:
    """Normalize an HTTP etag for use as a filename component.

    Strips surrounding quotes and replaces colons with underscores so the
    result is safe as a filename on all platforms.

    Example: '"abc:def"' → 'abc_def'
    """
    assert raw, "raw etag must not be empty"
    return raw.strip().strip('"').replace(":", "_")


def head_etag(session: niquests.Session, url: str, timeout_seconds: int = 60) -> str | None:
    """Send a HEAD request and return the normalized etag, or None if absent.

    Returns None (not raises) when the server omits the etag header, so callers
    can fall back to a synthetic key (e.g. content-length + last-modified).
    """
    assert url, "url must not be empty"
    assert timeout_seconds > 0, f"timeout_seconds must be positive, got {timeout_seconds}"
    response = session.head(url, timeout=timeout_seconds)
    raw = response.headers.get("etag", "")
    if not raw:
        return None
    return normalize_etag(raw)
