"""SSRF-safe URL validation for remote browser navigation."""

from __future__ import annotations

import ipaddress
import socket
from urllib.parse import urlparse

BLOCKED_HOSTNAMES = frozenset({
    "localhost",
    "localhost.localdomain",
    "metadata",
    "metadata.google.internal",
    "169.254.169.254",
})


def _blocked_ip(ip: ipaddress.IPv4Address | ipaddress.IPv6Address) -> bool:
    if ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_reserved or ip.is_multicast:
        return True
    if isinstance(ip, ipaddress.IPv4Address):
        if ip in ipaddress.ip_network("0.0.0.0/8"):
            return True
        if ip in ipaddress.ip_network("100.64.0.0/10"):
            return True
    if isinstance(ip, ipaddress.IPv6Address):
        if ip == ipaddress.IPv6Address("::1"):
            return True
        if ip in ipaddress.ip_network("fc00::/7"):
            return True
        if ip in ipaddress.ip_network("fe80::/10"):
            return True
    return False


def resolve_host_blocked(hostname: str) -> str | None:
    host = hostname.lower().strip(".")
    if not host:
        return "Empty hostname"
    if host in BLOCKED_HOSTNAMES or "metadata" in host:
        return f"Blocked hostname: {hostname}"
    try:
        infos = socket.getaddrinfo(host, None, type=socket.SOCK_STREAM)
    except socket.gaierror as exc:
        return f"DNS resolution failed: {exc}"
    if not infos:
        return f"No addresses for hostname: {hostname}"
    for info in infos:
        ip_str = info[4][0]
        try:
            ip = ipaddress.ip_address(ip_str)
        except ValueError:
            return f"Invalid IP: {ip_str}"
        if _blocked_ip(ip):
            return f"Blocked IP: {ip_str}"
    return None


def validate_navigation_url(url: str) -> tuple[str | None, str | None]:
    """Return (normalized_url, error_message)."""
    trimmed = url.strip()
    if not trimmed:
        return None, "Empty URL"
    try:
        with_proto = trimmed if "://" in trimmed else f"https://{trimmed}"
        parsed = urlparse(with_proto)
    except Exception:
        return None, "Invalid URL"
    if parsed.scheme not in ("http", "https"):
        return None, f"Unsupported scheme: {parsed.scheme}"
    if parsed.username or parsed.password:
        return None, "Credentials in URL are not allowed"
    if not parsed.hostname:
        return None, "Missing hostname"
    host_err = resolve_host_blocked(parsed.hostname)
    if host_err:
        return None, host_err
    normalized = parsed.geturl()
    return normalized, None
