"""Bounded GET retry example. Python 3.9+ and Requests. No automatic redirects.
Not a global rate limiter or a hard wall-clock deadline.
"""
import math
import random
import re
import time
from datetime import datetime, timezone
from email.utils import parsedate_to_datetime

import requests


def retry_after_seconds(value, now=None):
    if value is None:
        return None
    value = value.strip()
    if re.fullmatch(r"[0-9]+", value):
        # Huge values must cause deferral rather than overflow or early retry.
        return math.inf if len(value) > 12 else int(value)
    try:
        target = parsedate_to_datetime(value)
        if target.tzinfo is None:
            return None
        current = now or datetime.now(timezone.utc)
        return max(0, math.ceil((target - current).total_seconds()))
    except (TypeError, ValueError, OverflowError):
        return None


def get_with_retries(url, *, max_attempts=4, max_wait=60, session=None,
                     sleep=time.sleep, jitter=random.random):
    if max_attempts < 1 or max_wait < 0:
        raise ValueError("Invalid retry budget")
    owned = session is None
    client = session or requests.Session()
    try:
        for attempt in range(max_attempts):
            response = client.get(url, timeout=(5, 20), allow_redirects=False)
            # Transport exceptions propagate. This policy retries only 429/503.
            if response.status_code not in (429, 503) or attempt == max_attempts - 1:
                return response
            delay = retry_after_seconds(response.headers.get("Retry-After"))
            if delay is None:
                delay = 2 ** attempt + jitter()
            if delay > max_wait:
                return response  # Defer to a scheduler; do not retry early.
            response.close()
            sleep(delay)
    finally:
        if owned:
            client.close()
