from unittest.mock import patch

from yslow.net import get_default_gateway, find_gateway_probe_method


class TestGetDefaultGatewayLinux:
    def test_parses_standard_output(self):
        fake_output = "default via 192.168.1.1 dev eth0 proto dhcp metric 100\n"
        with (
            patch("yslow.net.platform.system", return_value="Linux"),
            patch("yslow.net.subprocess.check_output", return_value=fake_output),
        ):
            assert get_default_gateway() == "192.168.1.1"

    def test_multiple_defaults_returns_first(self):
        fake_output = (
            "default via 10.0.0.1 dev wlan0 proto dhcp metric 600\n"
            "default via 192.168.1.1 dev eth0 proto dhcp metric 100\n"
        )
        with (
            patch("yslow.net.platform.system", return_value="Linux"),
            patch("yslow.net.subprocess.check_output", return_value=fake_output),
        ):
            assert get_default_gateway() == "10.0.0.1"

    def test_no_default_route(self):
        fake_output = (
            "192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.50\n"
        )
        with (
            patch("yslow.net.platform.system", return_value="Linux"),
            patch("yslow.net.subprocess.check_output", return_value=fake_output),
        ):
            assert get_default_gateway() is None

    def test_command_fails(self):
        with (
            patch("yslow.net.platform.system", return_value="Linux"),
            patch("yslow.net.subprocess.check_output", side_effect=FileNotFoundError),
        ):
            assert get_default_gateway() is None

    def test_empty_output(self):
        with (
            patch("yslow.net.platform.system", return_value="Linux"),
            patch("yslow.net.subprocess.check_output", return_value=""),
        ):
            assert get_default_gateway() is None


class TestGetDefaultGatewayDarwin:
    NETSTAT_OUTPUT = (
        "Routing tables\n"
        "\n"
        "Internet:\n"
        "Destination        Gateway            Flags        Netif Expire\n"
        "default            192.168.1.1        UGScg          en0\n"
        "127.0.0.1          127.0.0.1          UH             lo0\n"
        "192.168.1/24       link#4             UCS            en0\n"
    )

    def test_parses_netstat_output(self):
        with (
            patch("yslow.net.platform.system", return_value="Darwin"),
            patch(
                "yslow.net.subprocess.check_output", return_value=self.NETSTAT_OUTPUT
            ),
        ):
            assert get_default_gateway() == "192.168.1.1"

    def test_no_default_route(self):
        fake_output = (
            "Routing tables\n"
            "\n"
            "Internet:\n"
            "Destination        Gateway            Flags        Netif Expire\n"
            "127.0.0.1          127.0.0.1          UH             lo0\n"
        )
        with (
            patch("yslow.net.platform.system", return_value="Darwin"),
            patch("yslow.net.subprocess.check_output", return_value=fake_output),
        ):
            assert get_default_gateway() is None

    def test_command_fails(self):
        with (
            patch("yslow.net.platform.system", return_value="Darwin"),
            patch("yslow.net.subprocess.check_output", side_effect=FileNotFoundError),
        ):
            assert get_default_gateway() is None

    def test_empty_output(self):
        with (
            patch("yslow.net.platform.system", return_value="Darwin"),
            patch("yslow.net.subprocess.check_output", return_value=""),
        ):
            assert get_default_gateway() is None


class TestFindGatewayProbeMethod:
    def test_finds_first_open_tcp_port(self):
        def fake_tcp_ping(host, port):
            return 1.0 if port == 443 else None

        with patch("yslow.net.tcp_ping", side_effect=fake_tcp_ping):
            method, port = find_gateway_probe_method("192.168.1.1")
        assert method == "tcp"
        assert port == 443

    def test_falls_back_to_icmp(self):
        with (
            patch("yslow.net.tcp_ping", return_value=None),
            patch("yslow.net.icmp_ping", return_value=1.0),
        ):
            method, port = find_gateway_probe_method("192.168.1.1")
        assert method == "icmp"
        assert port is None

    def test_nothing_works(self):
        with (
            patch("yslow.net.tcp_ping", return_value=None),
            patch("yslow.net.icmp_ping", return_value=None),
        ):
            method, port = find_gateway_probe_method("192.168.1.1")
        assert method == "none"
        assert port is None
