"""
Behavioral tests for ssrf_validator module.

Tests the SSRF (Server-Side Request Forgery) validation functions.
"""

import pytest


class TestIsIPBlockedLoopback:
    """Tests for loopback address blocking."""

    def test_127_0_0_1_blocked_by_default(self):
        """127.0.0.1 is blocked by default."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("127.0.0.1") is True

    def test_127_0_0_1_allowed_with_localhost_flag(self):
        """127.0.0.1 is allowed with allow_localhost=True."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("127.0.0.1", allow_localhost=True) is False

    def test_loopback_range_blocked(self):
        """Entire 127.x.x.x range is blocked."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("127.255.255.255") is True

    def test_ipv6_loopback_blocked(self):
        """IPv6 loopback ::1 is blocked."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("::1") is True

    def test_ipv6_loopback_allowed_with_flag(self):
        """IPv6 loopback allowed with allow_localhost=True."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("::1", allow_localhost=True) is False


class TestIsIPBlockedPrivate:
    """Tests for private IP address blocking."""

    def test_10_x_blocked(self):
        """10.x.x.x is blocked by default."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("10.0.0.1") is True

    def test_172_16_x_blocked(self):
        """172.16.x.x is blocked by default."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("172.16.0.1") is True

    def test_192_168_x_blocked(self):
        """192.168.x.x is blocked by default."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("192.168.1.1") is True

    def test_private_allowed_with_flag(self):
        """Private IPs allowed with allow_private_ips=True."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("10.0.0.1", allow_private_ips=True) is False
        assert is_ip_blocked("172.16.0.1", allow_private_ips=True) is False
        assert is_ip_blocked("192.168.1.1", allow_private_ips=True) is False


class TestIsIPBlockedCGNAT:
    """Tests for CGNAT (100.64.0.0/10) address handling."""

    def test_cgnat_blocked_by_default(self):
        """CGNAT range is blocked by default."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("100.64.0.1") is True

    def test_cgnat_allowed_with_private_flag(self):
        """CGNAT allowed with allow_private_ips=True."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("100.64.0.1", allow_private_ips=True) is False

    def test_cgnat_end_of_range(self):
        """End of CGNAT range is blocked."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("100.127.255.255") is True


class TestIsIPBlockedLinkLocal:
    """Tests for link-local address handling."""

    def test_link_local_blocked(self):
        """Link-local 169.254.x.x is blocked."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("169.254.0.1") is True

    def test_link_local_allowed_with_private_flag(self):
        """Link-local allowed with allow_private_ips=True (except AWS)."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        # Regular link-local should be allowed
        assert is_ip_blocked("169.254.1.1", allow_private_ips=True) is False


class TestIsIPBlockedAWSMetadata:
    """Tests for AWS metadata endpoint blocking."""

    def test_aws_metadata_always_blocked(self):
        """AWS metadata endpoint 169.254.169.254 is always blocked."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        # Should be blocked even with all flags
        assert is_ip_blocked("169.254.169.254") is True
        assert is_ip_blocked("169.254.169.254", allow_localhost=True) is True
        assert is_ip_blocked("169.254.169.254", allow_private_ips=True) is True


class TestIsIPBlockedPublic:
    """Tests for public IP address handling."""

    def test_public_ip_not_blocked(self):
        """Public IPs are not blocked."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("8.8.8.8") is False
        assert is_ip_blocked("1.1.1.1") is False
        assert is_ip_blocked("208.67.222.222") is False


class TestIsIPBlockedIPv6Private:
    """Tests for IPv6 private address handling."""

    def test_ipv6_fc00_blocked(self):
        """IPv6 fc00::/7 unique local is blocked."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("fc00::1") is True

    def test_ipv6_fd00_blocked(self):
        """IPv6 fd00:: unique local is blocked."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("fd00::1") is True

    def test_ipv6_fe80_blocked(self):
        """IPv6 fe80:: link-local is blocked."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("fe80::1") is True

    def test_ipv6_private_allowed_with_flag(self):
        """IPv6 private allowed with allow_private_ips=True."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("fc00::1", allow_private_ips=True) is False
        assert is_ip_blocked("fe80::1", allow_private_ips=True) is False


class TestIsIPBlockedInvalid:
    """Tests for invalid IP address handling."""

    def test_invalid_ip_not_blocked(self):
        """Invalid IP returns False (not blocked)."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("not-an-ip") is False

    def test_empty_string_not_blocked(self):
        """Empty string returns False."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("") is False

    def test_hostname_not_blocked(self):
        """Hostname is not blocked (not an IP)."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("example.com") is False


class TestIsIPBlockedZeroNetwork:
    """Tests for 0.0.0.0/8 network handling."""

    def test_0_0_0_0_blocked(self):
        """0.0.0.0 is blocked."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("0.0.0.0") is True

    def test_0_x_x_x_blocked(self):
        """0.x.x.x network is blocked."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("0.1.2.3") is True


class TestPrivateIpRangesBehavior:
    """Verify is_ip_blocked rejects an interior address from every entry of
    PRIVATE_IP_RANGES.

    Replaces the prior TestBlockedIPRanges class, which asserted
    ``ip_network("127.0.0.0/8") in BLOCKED_IP_RANGES`` — a tautology that
    re-stated the constant rather than exercising blocking behavior. The
    parametrized assertions here would fail if any range entry were
    accidentally removed from PRIVATE_IP_RANGES in ip_ranges.py.
    """

    @pytest.mark.parametrize(
        "ip,range_label",
        [
            ("127.0.0.5", "127.0.0.0/8 IPv4 loopback"),
            ("::1", "::1/128 IPv6 loopback"),
            ("10.0.0.5", "10.0.0.0/8 RFC1918 class A"),
            ("172.16.0.5", "172.16.0.0/12 RFC1918 class B"),
            ("172.31.255.254", "172.16.0.0/12 RFC1918 class B upper bound"),
            ("192.168.0.5", "192.168.0.0/16 RFC1918 class C"),
            ("100.64.0.5", "100.64.0.0/10 CGNAT"),
            ("169.254.0.5", "169.254.0.0/16 link-local"),
            ("fe80::1", "fe80::/10 IPv6 link-local"),
            ("fc00::1", "fc00::/7 IPv6 ULA"),
            ("0.0.0.5", "0.0.0.0/8 unspecified IPv4"),
            ("::", "::/128 unspecified IPv6"),
            ("2002:7f00:1::", "2002::/16 6to4 wrapping loopback"),
            ("64:ff9b::a9fe:a9fe", "64:ff9b::/96 NAT64 well-known"),
            ("64:ff9b:1::a9fe:a9fe", "64:ff9b:1::/48 NAT64 local-use"),
            ("2001::1", "2001::/32 Teredo"),
            ("100::1", "100::/64 discard"),
            ("::7f00:1", "::/96 IPv4-compatible IPv6 wrapping loopback"),
        ],
    )
    def test_interior_address_blocked(self, ip, range_label):
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked(ip) is True, (
            f"Expected {ip} to be blocked because it falls in {range_label}"
        )


class TestAllowedSchemes:
    """Tests for ALLOWED_SCHEMES constant."""

    def test_allowed_schemes_is_set(self):
        """ALLOWED_SCHEMES is a set."""
        from local_deep_research.security.ssrf_validator import ALLOWED_SCHEMES

        assert isinstance(ALLOWED_SCHEMES, set)

    def test_http_in_allowed(self):
        """http is in ALLOWED_SCHEMES."""
        from local_deep_research.security.ssrf_validator import ALLOWED_SCHEMES

        assert "http" in ALLOWED_SCHEMES

    def test_https_in_allowed(self):
        """https is in ALLOWED_SCHEMES."""
        from local_deep_research.security.ssrf_validator import ALLOWED_SCHEMES

        assert "https" in ALLOWED_SCHEMES


class TestAlwaysBlockedMetadataIPs:
    """Tests for ALWAYS_BLOCKED_METADATA_IPS constant."""

    def test_aws_imds_in_always_blocked(self):
        """AWS / Azure / OCI / DigitalOcean shared IMDS IP is in the set."""
        from local_deep_research.security.ssrf_validator import (
            ALWAYS_BLOCKED_METADATA_IPS,
        )

        assert "169.254.169.254" in ALWAYS_BLOCKED_METADATA_IPS


class TestGetSafeUrl:
    """Tests for get_safe_url function."""

    def test_returns_url_if_safe(self):
        """Returns URL if it's safe."""
        from unittest.mock import patch

        from local_deep_research.security.ssrf_validator import get_safe_url

        with patch(
            "socket.getaddrinfo",
            return_value=[(2, 1, 6, "", ("93.184.216.34", 0))],
        ):
            result = get_safe_url("https://example.com")
        assert result == "https://example.com"

    def test_returns_none_for_empty(self):
        """Returns default for empty URL."""
        from local_deep_research.security.ssrf_validator import get_safe_url

        result = get_safe_url("")
        assert result is None

    def test_returns_default_for_empty(self):
        """Returns specified default for empty URL."""
        from local_deep_research.security.ssrf_validator import get_safe_url

        result = get_safe_url("", default="https://fallback.com")
        assert result == "https://fallback.com"

    def test_returns_none_for_none_input(self):
        """Returns default for None input."""
        from local_deep_research.security.ssrf_validator import get_safe_url

        result = get_safe_url(None)
        assert result is None


class TestValidateUrlNoBypass:
    """Tests that validate_url performs real validation (no test bypass)."""

    def test_localhost_blocked_by_default(self):
        """validate_url blocks localhost by default."""
        from local_deep_research.security.ssrf_validator import validate_url

        result = validate_url("http://127.0.0.1")
        assert result is False

    def test_testing_env_does_not_bypass(self, monkeypatch):
        """TESTING=true environment variable does NOT bypass validation."""
        from local_deep_research.security.ssrf_validator import validate_url

        monkeypatch.setenv("TESTING", "true")
        result = validate_url("http://127.0.0.1")
        assert result is False


class TestAllowLocalhostFlag:
    """Tests for allow_localhost flag interaction."""

    def test_localhost_not_blocked_with_flag(self):
        """Localhost is not blocked when allow_localhost=True."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("127.0.0.1", allow_localhost=True) is False
        assert is_ip_blocked("::1", allow_localhost=True) is False

    def test_private_still_blocked_with_localhost_flag(self):
        """Private IPs still blocked with allow_localhost (not allow_private)."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        # allow_localhost only allows loopback, not all private
        assert is_ip_blocked("10.0.0.1", allow_localhost=True) is True
        assert is_ip_blocked("192.168.1.1", allow_localhost=True) is True


class TestAllowPrivateIPsFlag:
    """Tests for allow_private_ips flag interaction."""

    def test_private_ips_includes_loopback(self):
        """allow_private_ips also allows loopback."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("127.0.0.1", allow_private_ips=True) is False

    def test_aws_metadata_still_blocked_with_private(self):
        """AWS metadata is still blocked even with allow_private_ips."""
        from local_deep_research.security.ssrf_validator import is_ip_blocked

        assert is_ip_blocked("169.254.169.254", allow_private_ips=True) is True
