"""Tests for IP allowlist — CIDR validation and access control."""

from __future__ import annotations

import pytest
from backend.dashboard.ip_allowlist import (
    IPAllowlistConfig,
    check_ip_allowed,
    validate_cidrs,
)


class TestValidateCIDRs:
    def test_validate_cidrs_valid(self) -> None:
        """IPv4 and IPv6 CIDRs pass validation."""
        result = validate_cidrs(["10.0.0.0/8", "192.168.1.0/24", "2001:db8::/32"])
        assert len(result) == 3
        assert "10.0.0.0/8" in result
        assert "192.168.1.0/24" in result
        assert "2001:db8::/32" in result

    def test_validate_cidrs_invalid(self) -> None:
        """Invalid CIDR raises ValueError."""
        with pytest.raises(ValueError):
            validate_cidrs(["not-a-cidr"])

    def test_validate_cidrs_single_ip(self) -> None:
        """Single IP with /32 is valid."""
        result = validate_cidrs(["203.0.113.5/32"])
        assert result == ["203.0.113.5/32"]

    def test_validate_cidrs_non_strict(self) -> None:
        """Host bits set → normalized via strict=False."""
        result = validate_cidrs(["10.1.2.3/8"])
        assert result == ["10.0.0.0/8"]


class TestCheckIPAllowed:
    def test_check_ip_allowed_in_range(self) -> None:
        """IP within CIDR → True."""
        config = IPAllowlistConfig(enabled=True, cidrs=["10.0.0.0/8", "192.168.1.0/24"])
        assert check_ip_allowed("10.0.0.5", config) is True
        assert check_ip_allowed("192.168.1.100", config) is True

    def test_check_ip_allowed_outside_range(self) -> None:
        """IP outside CIDR → False."""
        config = IPAllowlistConfig(enabled=True, cidrs=["10.0.0.0/8"])
        assert check_ip_allowed("172.16.0.1", config) is False

    def test_check_ip_allowed_disabled(self) -> None:
        """Disabled allowlist → always True."""
        config = IPAllowlistConfig(enabled=False, cidrs=["10.0.0.0/8"])
        assert check_ip_allowed("172.16.0.1", config) is True
        assert check_ip_allowed("1.2.3.4", config) is True

    def test_check_ip_allowed_ipv6(self) -> None:
        """IPv6 address within CIDR."""
        config = IPAllowlistConfig(enabled=True, cidrs=["2001:db8::/32"])
        assert check_ip_allowed("2001:db8::1", config) is True
        assert check_ip_allowed("2001:db9::1", config) is False

    def test_check_ip_allowed_invalid_ip(self) -> None:
        """Invalid client IP → False."""
        config = IPAllowlistConfig(enabled=True, cidrs=["10.0.0.0/8"])
        assert check_ip_allowed("not-an-ip", config) is False

    def test_check_ip_allowed_empty_cidrs(self) -> None:
        """Enabled but no CIDRs → everything blocked."""
        config = IPAllowlistConfig(enabled=True, cidrs=[])
        assert check_ip_allowed("10.0.0.1", config) is False


class TestMiddlewareIntegration:
    async def test_middleware_blocks_disallowed_ip(self) -> None:
        """Non-allowed IP → check fails."""
        config = IPAllowlistConfig(enabled=True, cidrs=["10.0.0.0/8"])
        assert check_ip_allowed("172.16.0.1", config) is False

    async def test_middleware_passes_allowed_ip(self) -> None:
        """Allowed IP → check passes."""
        config = IPAllowlistConfig(enabled=True, cidrs=["10.0.0.0/8"])
        assert check_ip_allowed("10.0.0.5", config) is True

    async def test_middleware_skips_non_enterprise(self) -> None:
        """Non-Enterprise tiers → ip_allowlist not in features."""
        from backend.shared.tier_config import TIER_CONFIG

        # Community/Pro/Team should not have ip_allowlist
        for tier_name in ("community", "pro"):
            features = TIER_CONFIG[tier_name].get("features", [])
            assert "ip_allowlist" not in features

        # Enterprise should have it
        enterprise = TIER_CONFIG.get("enterprise", {})
        features = enterprise.get("features", [])
        assert "ip_allowlist" in features
