"""
Shared webhook validation logic to prevent SSRF attacks.
"""

import logging
import socket
import ipaddress
from urllib.parse import urlparse
from typing import Optional

log = logging.getLogger(__name__)

ALLOWED_PORTS = {80, 443}

# Additional IP ranges to blacklist
BLACKLIST = [
    ipaddress.ip_network("100.64.0.0/10"),  # CGNAT (Carrier-grade NAT)
    ipaddress.ip_network("198.18.0.0/15"),  # Benchmark testing
]


def _host_is_private(hostname: str) -> bool:
    """Check if hostname resolves to a private IP."""
    try:
        ip = ipaddress.ip_address(hostname)
        # Check standard private ranges
        if ip.is_private or ip.is_loopback or ip.is_link_local:
            return True
        # Check additional blacklisted ranges
        if any(ip in net for net in BLACKLIST):
            return True
        return False
    except ValueError:
        # Not an IP, need to resolve
        try:
            infos = socket.getaddrinfo(hostname, None)
        except (socket.gaierror, OSError):
            return True  # treat unresolvable as unsafe
        for _, _, _, _, sockaddr in infos:
            try:
                ip = ipaddress.ip_address(sockaddr[0])
            except ValueError:
                continue
            if ip.is_private or ip.is_loopback or ip.is_link_local:
                return True
            # Check additional blacklisted ranges
            if any(ip in net for net in BLACKLIST):
                return True
        return False


def validate_webhook_url(url: Optional[str], require_https: bool = False) -> bool:
    """
    Validate webhook URL to prevent SSRF attacks.
    Returns True if URL is safe, False otherwise.
    """
    if not url:
        return True  # None is okay (no webhook)

    try:
        parsed = urlparse(url)

        # Check scheme
        if parsed.scheme not in ("http", "https"):
            log.warning(f"Invalid webhook URL scheme: {parsed.scheme}")
            return False

        # Check HTTPS requirement
        if require_https and parsed.scheme != "https":
            log.warning("Webhook URL must use HTTPS")
            return False

        # Check port
        if parsed.port and parsed.port not in ALLOWED_PORTS:
            log.warning(f"Invalid webhook port: {parsed.port}")
            return False

        # Get hostname
        host = parsed.hostname
        if not host:
            log.warning("Webhook URL has no hostname")
            return False

        # Normalize hostname to IDNA to avoid Unicode attacks
        try:
            host = host.encode("idna").decode("ascii")
        except Exception:
            log.warning(f"Invalid hostname encoding: {host}")
            return False

        # Check if it resolves to private IP
        if _host_is_private(host):
            log.warning(f"Webhook URL points to private/local address: {host}")
            return False

        # Additional checks for cloud metadata endpoints
        forbidden_patterns = [
            "169.254.169.254",  # AWS/GCP metadata
            "metadata.google.internal",  # GCP metadata
            "metadata.azure.com",  # Azure metadata
        ]
        if any(pattern in url.lower() for pattern in forbidden_patterns):
            log.warning(f"Webhook URL may be targeting metadata endpoint: {url}")
            return False

        return True

    except Exception as e:
        log.error(f"Webhook validation error: {e}")
        return False
