"""
Tests for security/ip_ranges.py

Tests cover:
- PRIVATE_IP_RANGES constant contains expected networks
- Private IP detection works correctly
- IPv4 and IPv6 address validation
"""

import ipaddress


class TestPrivateIPRanges:
    """Tests for PRIVATE_IP_RANGES constant."""

    def test_private_ip_ranges_is_list(self):
        """PRIVATE_IP_RANGES should be a list."""
        from local_deep_research.security.ip_ranges import PRIVATE_IP_RANGES

        assert isinstance(PRIVATE_IP_RANGES, list)

    def test_private_ip_ranges_not_empty(self):
        """PRIVATE_IP_RANGES should not be empty."""
        from local_deep_research.security.ip_ranges import PRIVATE_IP_RANGES

        assert len(PRIVATE_IP_RANGES) > 0

    def test_private_ip_ranges_contains_ip_networks(self):
        """All entries should be ip_network objects."""
        from local_deep_research.security.ip_ranges import PRIVATE_IP_RANGES

        for network in PRIVATE_IP_RANGES:
            assert isinstance(
                network, (ipaddress.IPv4Network, ipaddress.IPv6Network)
            )

    def test_contains_loopback_ipv4(self):
        """Should contain IPv4 loopback (127.0.0.0/8)."""
        from local_deep_research.security.ip_ranges import PRIVATE_IP_RANGES

        loopback = ipaddress.ip_network("127.0.0.0/8")
        assert loopback in PRIVATE_IP_RANGES

    def test_contains_loopback_ipv6(self):
        """Should contain IPv6 loopback (::1/128)."""
        from local_deep_research.security.ip_ranges import PRIVATE_IP_RANGES

        loopback = ipaddress.ip_network("::1/128")
        assert loopback in PRIVATE_IP_RANGES

    def test_contains_rfc1918_class_a(self):
        """Should contain RFC1918 Class A private (10.0.0.0/8)."""
        from local_deep_research.security.ip_ranges import PRIVATE_IP_RANGES

        private_a = ipaddress.ip_network("10.0.0.0/8")
        assert private_a in PRIVATE_IP_RANGES

    def test_contains_rfc1918_class_b(self):
        """Should contain RFC1918 Class B private (172.16.0.0/12)."""
        from local_deep_research.security.ip_ranges import PRIVATE_IP_RANGES

        private_b = ipaddress.ip_network("172.16.0.0/12")
        assert private_b in PRIVATE_IP_RANGES

    def test_contains_rfc1918_class_c(self):
        """Should contain RFC1918 Class C private (192.168.0.0/16)."""
        from local_deep_research.security.ip_ranges import PRIVATE_IP_RANGES

        private_c = ipaddress.ip_network("192.168.0.0/16")
        assert private_c in PRIVATE_IP_RANGES

    def test_contains_cgnat(self):
        """Should contain CGNAT range (100.64.0.0/10)."""
        from local_deep_research.security.ip_ranges import PRIVATE_IP_RANGES

        cgnat = ipaddress.ip_network("100.64.0.0/10")
        assert cgnat in PRIVATE_IP_RANGES

    def test_contains_link_local_ipv4(self):
        """Should contain IPv4 link-local (169.254.0.0/16)."""
        from local_deep_research.security.ip_ranges import PRIVATE_IP_RANGES

        link_local = ipaddress.ip_network("169.254.0.0/16")
        assert link_local in PRIVATE_IP_RANGES

    def test_contains_link_local_ipv6(self):
        """Should contain IPv6 link-local (fe80::/10)."""
        from local_deep_research.security.ip_ranges import PRIVATE_IP_RANGES

        link_local = ipaddress.ip_network("fe80::/10")
        assert link_local in PRIVATE_IP_RANGES

    def test_contains_ipv6_unique_local(self):
        """Should contain IPv6 unique local (fc00::/7)."""
        from local_deep_research.security.ip_ranges import PRIVATE_IP_RANGES

        unique_local = ipaddress.ip_network("fc00::/7")
        assert unique_local in PRIVATE_IP_RANGES


class TestPrivateIPDetection:
    """Tests for using PRIVATE_IP_RANGES to detect private IPs."""

    def _is_private(self, ip_str: str) -> bool:
        """Helper to check if IP is in private ranges."""
        from local_deep_research.security.ip_ranges import PRIVATE_IP_RANGES

        try:
            ip = ipaddress.ip_address(ip_str)
            return any(ip in network for network in PRIVATE_IP_RANGES)
        except ValueError:
            return False

    def test_localhost_is_private(self):
        """127.0.0.1 should be detected as private."""
        assert self._is_private("127.0.0.1") is True

    def test_localhost_ipv6_is_private(self):
        """::1 should be detected as private."""
        assert self._is_private("::1") is True

    def test_10_network_is_private(self):
        """10.x.x.x addresses should be detected as private."""
        assert self._is_private("10.0.0.1") is True
        assert self._is_private("10.255.255.255") is True

    def test_172_16_network_is_private(self):
        """172.16-31.x.x addresses should be detected as private."""
        assert self._is_private("172.16.0.1") is True
        assert self._is_private("172.31.255.255") is True

    def test_172_32_is_not_private(self):
        """172.32.x.x should NOT be detected as private."""
        assert self._is_private("172.32.0.1") is False

    def test_192_168_network_is_private(self):
        """192.168.x.x addresses should be detected as private."""
        assert self._is_private("192.168.0.1") is True
        assert self._is_private("192.168.255.255") is True

    def test_cgnat_is_private(self):
        """100.64.x.x CGNAT addresses should be detected as private."""
        assert self._is_private("100.64.0.1") is True
        assert self._is_private("100.127.255.255") is True

    def test_link_local_is_private(self):
        """169.254.x.x link-local addresses should be detected as private."""
        assert self._is_private("169.254.1.1") is True

    def test_public_ip_is_not_private(self):
        """Public IPs should NOT be detected as private."""
        assert self._is_private("8.8.8.8") is False
        assert self._is_private("1.1.1.1") is False
        assert self._is_private("93.184.216.34") is False  # example.com

    def test_ipv6_public_is_not_private(self):
        """Public IPv6 addresses should NOT be detected as private."""
        assert self._is_private("2001:4860:4860::8888") is False  # Google DNS


class TestIPRangesUsedByValidators:
    """Tests to verify PRIVATE_IP_RANGES is correctly imported by validators."""

    def test_ssrf_validator_uses_shared_ranges(self):
        """SSRF validator should import BLOCKED_IP_RANGES from ip_ranges."""
        from local_deep_research.security.ssrf_validator import (
            BLOCKED_IP_RANGES,
        )
        from local_deep_research.security.ip_ranges import PRIVATE_IP_RANGES

        # Should be the same object (imported, not copied)
        assert BLOCKED_IP_RANGES is PRIVATE_IP_RANGES

    def test_notification_validator_uses_shared_ranges(self):
        """Notification validator should use shared ranges."""
        from local_deep_research.security.notification_validator import (
            NotificationURLValidator,
        )
        from local_deep_research.security.ip_ranges import PRIVATE_IP_RANGES

        # Should be the same object
        assert NotificationURLValidator.PRIVATE_IP_RANGES is PRIVATE_IP_RANGES
