"""HTTPS URL fetch with redirect cap and private-IP / internal hostname blocks (screenshot, webhooks)."""
from __future__ import annotations

import ipaddress
import socket
from urllib.parse import urlparse

import httpx

_MAX_REDIRECTS_DEFAULT = 8


def _is_blocked_host(host: str) -> bool:
    h = (host or "").strip().lower().rstrip(".")
    if not h:
        return True
    if h in ("localhost",) or h.endswith(".localhost"):
        return True
    if h.endswith(".local") or h.endswith(".internal") or h.endswith(".corp") or h.endswith(".lan"):
        return True
    try:
        infos = socket.getaddrinfo(h, None, type=socket.SOCK_STREAM)
    except OSError:
        return True
    for info in infos:
        ip_str = info[4][0]
        try:
            ip = ipaddress.ip_address(ip_str)
        except ValueError:
            return True
        if ip.is_loopback or ip.is_private or ip.is_link_local or ip.is_reserved or ip.is_multicast:
            return True
    return False


def assert_safe_https_url(url: str) -> str:
    p = urlparse(url.strip())
    if p.scheme.lower() != "https":
        raise ValueError("Only https URLs are allowed")
    if not p.hostname:
        raise ValueError("Invalid URL")
    if _is_blocked_host(p.hostname):
        raise ValueError("URL host is not allowed")
    return url.strip()


async def fetch_url_bytes(
    url: str,
    *,
    max_bytes: int,
    max_redirects: int = _MAX_REDIRECTS_DEFAULT,
    timeout: float = 30.0,
) -> tuple[bytes, str]:
    """Follow redirects (each hop re-validated). Returns (body, content_type)."""
    current = assert_safe_https_url(url)
    redirects = 0
    async with httpx.AsyncClient(follow_redirects=False, timeout=timeout) as client:
        while True:
            r = await client.get(current)
            if r.status_code in (301, 302, 303, 307, 308) and r.headers.get("location"):
                redirects += 1
                if redirects > max_redirects:
                    raise ValueError("Too many redirects")
                loc = r.headers["location"].strip()
                if loc.startswith("/"):
                    p = urlparse(current)
                    current = f"{p.scheme}://{p.netloc}{loc}"
                else:
                    current = loc
                current = assert_safe_https_url(current)
                continue
            r.raise_for_status()
            data = r.content
            if len(data) > max_bytes:
                raise ValueError("Response too large")
            ct = (r.headers.get("content-type") or "application/octet-stream").split(";")[0].strip()
            return data, ct
