"""Tests for interface detection with retry fallback chain."""

import time
from pathlib import Path
from unittest.mock import patch, MagicMock
from tempfile import TemporaryDirectory

import pytest

from utils.networking import get_primary_interface, detect_interface_with_retry


class TestInterfaceDetection:
    """Test robust interface detection with retry fallback."""

    def test_get_primary_interface_from_route_table(self):
        """get_primary_interface reads default route from /proc/net/route."""
        route_content = """Iface	Destination	Gateway 	Flags	RefCnt	Use	Metric	Mask		MTU	Window	IRTT
eth0	00000000	0101010A	0003	0	0	100	00000000	0	0	0
eth0	0101010A	00000000	0001	0	0	100	FFFFFFFF	0	0	0
"""
        with patch("builtins.open", create=True) as mock_open:
            mock_open.return_value.__enter__.return_value.readlines.return_value = route_content.split("\n")
            result = get_primary_interface()
            assert result == "eth0"

    def test_get_primary_interface_fallback_eth(self):
        """get_primary_interface falls back to eth* interfaces if /proc/net/route is empty."""
        with patch("builtins.open", side_effect=IOError):
            with patch("utils.networking.get_all_interfaces", return_value=["eth0", "eth1"]):
                result = get_primary_interface()
                assert result == "eth0"

    def test_get_primary_interface_fallback_wlan(self):
        """get_primary_interface falls back to first interface if no eth/en."""
        with patch("builtins.open", side_effect=IOError):
            with patch("utils.networking.get_all_interfaces", return_value=["wlan0"]):
                result = get_primary_interface()
                assert result == "wlan0"

    def test_get_primary_interface_no_interfaces(self):
        """get_primary_interface returns None if no interfaces available."""
        with patch("builtins.open", side_effect=IOError):
            with patch("utils.networking.get_all_interfaces", return_value=[]):
                result = get_primary_interface()
                assert result is None

    def test_detect_interface_with_retry_immediate_success(self):
        """detect_interface_with_retry returns interface immediately if found."""
        with patch("utils.networking.get_primary_interface", return_value="eth0"):
            result = detect_interface_with_retry(max_retries=3, retry_delay=1)
            assert result == "eth0"

    def test_detect_interface_with_retry_eventual_success(self):
        """detect_interface_with_retry retries and succeeds after N attempts."""
        call_count = [0]

        def get_iface_side_effect():
            call_count[0] += 1
            if call_count[0] <= 2:
                return None  # Fail first 2 times
            return "wlan0"  # Succeed on 3rd call

        with patch("utils.networking.get_primary_interface", side_effect=get_iface_side_effect):
            with patch("utils.networking._get_up_interface", return_value=None):
                result = detect_interface_with_retry(max_retries=3, retry_delay=0.01)
                assert result == "wlan0"
                assert call_count[0] == 3

    def test_detect_interface_with_retry_exhausted(self):
        """detect_interface_with_retry returns None if all retries exhausted."""
        with patch("utils.networking.get_primary_interface", return_value=None):
            with patch("utils.networking._get_up_interface", return_value=None):
                result = detect_interface_with_retry(max_retries=2, retry_delay=0.01)
                assert result is None

    def test_detect_interface_with_retry_uses_config_fallback(self):
        """detect_interface_with_retry accepts config-set interface as ultimate fallback."""
        with patch("utils.networking.get_primary_interface", return_value=None):
            with patch("utils.networking._get_up_interface", return_value=None):
                result = detect_interface_with_retry(
                    max_retries=1,
                    retry_delay=0.01,
                    config_interface="configured_iface"
                )
                # Should use config fallback when detection fails
                assert result == "configured_iface"

    def test_detect_interface_exponential_backoff(self):
        """detect_interface_with_retry uses exponential backoff (5s, 10s, 20s)."""
        call_times = []

        def get_iface_with_timing():
            call_times.append(time.time())
            return None

        with patch("utils.networking.get_primary_interface", side_effect=get_iface_with_timing):
            with patch("utils.networking._get_up_interface", return_value=None):
                with patch("time.sleep") as mock_sleep:
                    result = detect_interface_with_retry(
                        max_retries=3,
                        retry_delay=5,  # Base delay
                        exponential=True
                    )
                    # Should have called sleep with 5, 10, 20
                    assert mock_sleep.call_count == 3
                    # Check exponential backoff values (5 * 2^0, 5 * 2^1, 5 * 2^2)
                    sleep_calls = [call[0][0] for call in mock_sleep.call_args_list]
                    assert sleep_calls == [5, 10, 20]

    def test_detect_interface_filters_excluded(self):
        """detect_interface_with_retry excludes lo, tailscale*, wg*, tun*, docker*, veth*, br-*."""
        excluded_ifaces = ["lo", "tailscale0", "wg0", "tun0", "docker0", "veth123", "br-abc"]

        with patch("utils.networking.get_primary_interface", return_value=None):
            # Mock get_all_interfaces to return only excluded ones
            with patch("utils.networking.get_all_interfaces", return_value=excluded_ifaces):
                result = detect_interface_with_retry(max_retries=1, retry_delay=0.01)
                assert result is None
