"""Tests for autodebug_replay/network_guard.py.

Verifies that NetworkGuard blocks all socket-level network access during
replay and correctly restores originals on uninstall.
"""

from __future__ import annotations

import socket

import pytest

from autodebug_replay.network_guard import NetworkGuard
from autodebug_replay.transcripts import ReplayDivergence


@pytest.fixture()
def guard():
    """Create and install a NetworkGuard, uninstalling in cleanup."""
    g = NetworkGuard()
    g.install()
    yield g
    g.uninstall()


# ---- blocking tests --------------------------------------------------------

class TestBlocksCreateConnection:
    """socket.create_connection() raises ReplayDivergence when guard is active."""

    def test_blocks_create_connection(self, guard):
        with pytest.raises(ReplayDivergence) as exc_info:
            socket.create_connection(("example.com", 80))

        assert exc_info.value.code == "NETWORK_ACCESS"
        assert exc_info.value.detail["op"] == "create_connection"


class TestBlocksConnect:
    """socket.socket.connect() raises ReplayDivergence when guard is active."""

    def test_blocks_connect(self, guard):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            with pytest.raises(ReplayDivergence) as exc_info:
                sock.connect(("example.com", 80))

            assert exc_info.value.code == "NETWORK_ACCESS"
            assert exc_info.value.detail["op"] == "connect"
        finally:
            sock.close()


class TestBlocksGetaddrinfo:
    """socket.getaddrinfo() raises ReplayDivergence when guard is active."""

    def test_blocks_getaddrinfo(self, guard):
        with pytest.raises(ReplayDivergence) as exc_info:
            socket.getaddrinfo("example.com", 80)

        assert exc_info.value.code == "NETWORK_ACCESS"
        assert exc_info.value.detail["op"] == "getaddrinfo"


class TestBlocksSendto:
    """socket.socket.sendto() raises ReplayDivergence when guard is active."""

    def test_blocks_sendto(self, guard):
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        try:
            with pytest.raises(ReplayDivergence) as exc_info:
                sock.sendto(b"hello", ("example.com", 9999))

            assert exc_info.value.code == "NETWORK_ACCESS"
            assert exc_info.value.detail["op"] == "sendto"
        finally:
            sock.close()


# ---- uninstall tests -------------------------------------------------------

class TestUninstallRestoresSocket:
    """After uninstall(), all socket functions are restored to their originals."""

    def test_uninstall_restores_socket(self):
        # Capture original references before any guard installation
        orig_create_connection = socket.create_connection
        orig_getaddrinfo = socket.getaddrinfo
        orig_connect = socket.socket.connect
        orig_connect_ex = socket.socket.connect_ex
        orig_sendto = socket.socket.sendto

        guard = NetworkGuard()
        guard.install()

        # Verify they are patched (different from originals)
        assert socket.create_connection is not orig_create_connection
        assert socket.getaddrinfo is not orig_getaddrinfo
        assert socket.socket.connect is not orig_connect
        assert socket.socket.sendto is not orig_sendto

        guard.uninstall()

        # Verify originals are fully restored
        assert socket.create_connection is orig_create_connection
        assert socket.getaddrinfo is orig_getaddrinfo
        assert socket.socket.connect is orig_connect
        assert socket.socket.connect_ex is orig_connect_ex
        assert socket.socket.sendto is orig_sendto
