# retoor <retoor@molodetz.nl>

import ipaddress
import httpx
import pytest
from devplacepy import net_guard
from devplacepy.curl_transport import CurlTransport
from devplacepy.net_guard import BlockedAddressError
from tests.conftest import run_async


class _RecordingInner(httpx.AsyncBaseTransport):
    def __init__(self):
        self.called = False
        self.closed = False

    async def handle_async_request(self, request):
        self.called = True
        return httpx.Response(200, content=b"ok", request=request)

    async def aclose(self):
        self.closed = True


def test_loopback_is_blocked():
    assert net_guard.is_blocked_address(ipaddress.ip_address("127.0.0.1"))


def test_private_ranges_are_blocked():
    assert net_guard.is_blocked_address(ipaddress.ip_address("10.0.0.1"))
    assert net_guard.is_blocked_address(ipaddress.ip_address("192.168.1.1"))
    assert net_guard.is_blocked_address(ipaddress.ip_address("172.16.0.1"))


def test_link_local_and_unspecified_blocked():
    assert net_guard.is_blocked_address(ipaddress.ip_address("169.254.1.1"))
    assert net_guard.is_blocked_address(ipaddress.ip_address("0.0.0.0"))


def test_public_address_allowed():
    assert not net_guard.is_blocked_address(ipaddress.ip_address("1.1.1.1"))
    assert not net_guard.is_blocked_address(ipaddress.ip_address("8.8.8.8"))


def test_ipv4_mapped_ipv6_unwrapped_and_blocked():
    mapped = ipaddress.ip_address("::ffff:127.0.0.1")
    assert isinstance(net_guard.effective_address(mapped), ipaddress.IPv4Address)
    assert net_guard.is_blocked_address(mapped)


def test_nat64_prefix_unwrapped_to_ipv4():
    nat64 = ipaddress.ip_address("64:ff9b::7f00:1")
    resolved = net_guard.effective_address(nat64)
    assert isinstance(resolved, ipaddress.IPv4Address)
    assert net_guard.is_blocked_address(nat64)


def test_guard_rejects_non_http_scheme():
    with pytest.raises(BlockedAddressError):
        run_async(net_guard.guard_public_url("ftp://example.com/x"))
    with pytest.raises(BlockedAddressError):
        run_async(net_guard.guard_public_url("file:///etc/passwd"))


def test_guard_rejects_missing_host():
    with pytest.raises(BlockedAddressError):
        run_async(net_guard.guard_public_url("http://"))


def test_guard_rejects_loopback_literal():
    with pytest.raises(BlockedAddressError):
        run_async(net_guard.guard_public_url("http://127.0.0.1:8080/admin"))


def test_guard_rejects_private_literal():
    with pytest.raises(BlockedAddressError):
        run_async(net_guard.guard_public_url("http://10.0.0.5/internal"))


def test_guard_allow_private_skips_resolution():
    host = run_async(
        net_guard.guard_public_url("http://127.0.0.1:9000/x", allow_private=True)
    )
    assert host == "127.0.0.1"


def test_guarded_client_wraps_stealth_transport():
    client = net_guard.guarded_async_client(timeout=5.0)
    transport = client._transport
    assert isinstance(transport, net_guard._GuardedTransport)
    assert isinstance(transport._inner, CurlTransport)
    run_async(client.aclose())


def test_guarded_transport_blocks_before_delegating():
    inner = _RecordingInner()
    transport = net_guard._GuardedTransport(inner)
    request = httpx.Request("GET", "http://127.0.0.1/admin")
    with pytest.raises(BlockedAddressError):
        run_async(transport.handle_async_request(request))
    assert inner.called is False


def test_guarded_transport_delegates_when_allowed(monkeypatch):
    async def _allow(url, **kwargs):
        return "example.com"

    monkeypatch.setattr(net_guard, "guard_public_url", _allow)
    inner = _RecordingInner()
    transport = net_guard._GuardedTransport(inner)
    response = run_async(
        transport.handle_async_request(httpx.Request("GET", "https://example.com/x"))
    )
    assert inner.called is True
    assert response.status_code == 200


def test_guarded_transport_aclose_closes_inner():
    inner = _RecordingInner()
    transport = net_guard._GuardedTransport(inner)
    run_async(transport.aclose())
    assert inner.closed is True
