"""
URL validation utilities with SSRF protection.

This module provides utilities for validating URLs to prevent Server-Side
Request Forgery (SSRF) attacks. All URL inputs that will be fetched server-side
should be validated using these functions.

Usage:
    from lightwave.schema.validation.url import is_ssrf_vulnerable_url, is_private_ip

    # Validate a user-provided URL before fetching
    is_vulnerable, reason = is_ssrf_vulnerable_url(user_url)
    if is_vulnerable:
        raise ValidationError(f"URL blocked: {reason}")

    # Check if an IP is private
    if is_private_ip("10.0.0.1"):
        raise ValidationError("Private IPs not allowed")
"""

from __future__ import annotations

import ipaddress
from urllib.parse import urlparse

# Private IP ranges that should be blocked
PRIVATE_IP_NETWORKS = [
    ipaddress.ip_network("10.0.0.0/8"),
    ipaddress.ip_network("172.16.0.0/12"),
    ipaddress.ip_network("192.168.0.0/16"),
    ipaddress.ip_network("127.0.0.0/8"),
    ipaddress.ip_network("169.254.0.0/16"),  # Link-local / AWS metadata
    ipaddress.ip_network("::1/128"),  # IPv6 localhost
    ipaddress.ip_network("fc00::/7"),  # IPv6 private
    ipaddress.ip_network("fe80::/10"),  # IPv6 link-local
]

# Cloud metadata endpoints that should always be blocked
METADATA_HOSTS = frozenset(
    {
        "169.254.169.254",
        "metadata.google.internal",
        "metadata.aws.internal",
    }
)

# Localhost variations (including compact notations)
LOCALHOST_HOSTS = frozenset(
    {
        "localhost",
        "127.0.0.1",
        "127.1",  # Compact notation for 127.0.0.1
        "127.0.1",  # Alternative compact notation
        "0.0.0.0",
        "::1",
        "[::1]",
    }
)

# Allowed protocols for URLs
ALLOWED_PROTOCOLS = frozenset({"http", "https"})


def is_private_ip(ip_str: str) -> bool:
    """
    Check if an IP address is in a private range.

    Args:
        ip_str: IP address string to check

    Returns:
        True if the IP is in a private range, False otherwise
    """
    try:
        ip = ipaddress.ip_address(ip_str)
        return any(ip in network for network in PRIVATE_IP_NETWORKS)
    except ValueError:
        return False


def is_ssrf_vulnerable_url(url: str) -> tuple[bool, str]:
    """
    Check if a URL could be an SSRF attack.

    This function checks for common SSRF attack patterns:
    - Non-http/https protocols (file://, gopher://, etc.)
    - Localhost and loopback addresses
    - Private IP ranges (10.x, 172.16-31.x, 192.168.x)
    - Cloud metadata endpoints (169.254.169.254, etc.)

    Args:
        url: The URL to validate

    Returns:
        Tuple of (is_vulnerable: bool, reason: str)
        If vulnerable, reason explains why.
        If safe, reason is "ok".

    Example:
        >>> is_ssrf_vulnerable_url("http://localhost/admin")
        (True, 'localhost: localhost')
        >>> is_ssrf_vulnerable_url("https://example.com/api")
        (False, 'ok')
    """
    try:
        parsed = urlparse(url)

        # Check protocol
        if parsed.scheme not in ALLOWED_PROTOCOLS:
            return True, f"dangerous protocol: {parsed.scheme}"

        # Check hostname
        hostname = parsed.hostname or ""

        # Localhost variations
        if hostname in LOCALHOST_HOSTS:
            return True, f"localhost: {hostname}"

        # Strip brackets from IPv6
        clean_hostname = hostname.strip("[]")

        # Check for IP address in private range
        try:
            ipaddress.ip_address(clean_hostname)
            if is_private_ip(clean_hostname):
                return True, f"private IP: {hostname}"
        except ValueError:
            pass  # Not an IP, continue checking

        # Cloud metadata endpoints
        if hostname in METADATA_HOSTS:
            return True, f"cloud metadata: {hostname}"

        return False, "ok"

    except Exception as e:
        return True, f"parse error: {e}"


def validate_url_for_fetch(url: str) -> None:
    """
    Validate a URL is safe for server-side fetching.

    Raises:
        ValueError: If the URL is potentially dangerous (SSRF risk)

    Example:
        >>> validate_url_for_fetch("http://localhost/admin")
        Traceback (most recent call last):
            ...
        ValueError: URL blocked: localhost: localhost
    """
    is_vulnerable, reason = is_ssrf_vulnerable_url(url)
    if is_vulnerable:
        raise ValueError(f"URL blocked: {reason}")
