"""Unit tests for utility functions."""

from pathlib import Path
from unittest.mock import MagicMock, patch

# ---------------------------------------------------------------------------
# get_version()
# ---------------------------------------------------------------------------


class TestGetVersion:
    def setup_method(self):
        """Reset the version cache before each test."""
        import linux_voice_assistant.util as util

        util._version_cache = None

    def test_returns_unknown_when_file_missing(self):
        import linux_voice_assistant.util as util

        util._version_cache = None

        with patch.object(Path, "read_text", side_effect=FileNotFoundError):
            result = util.get_version()
        assert result == "unknown"

    def test_returns_version_string_from_file(self):
        import linux_voice_assistant.util as util

        util._version_cache = None

        with patch.object(Path, "read_text", return_value="1.2.3\n"):
            result = util.get_version()
        assert result == "1.2.3"

    def test_strips_whitespace_from_version(self):
        import linux_voice_assistant.util as util

        util._version_cache = None

        with patch.object(Path, "read_text", return_value="  2.0.0  \n"):
            result = util.get_version()
        assert result == "2.0.0"

    def test_returns_unknown_for_empty_file(self):
        import linux_voice_assistant.util as util

        util._version_cache = None

        with patch.object(Path, "read_text", return_value="   "):
            result = util.get_version()
        assert result == "unknown"

    def test_caches_result_after_first_call(self):
        import linux_voice_assistant.util as util

        util._version_cache = None

        with patch.object(Path, "read_text", return_value="3.0.0") as mock_read:
            util.get_version()
            util.get_version()
            mock_read.assert_called_once()

    def test_returns_cached_value_on_second_call(self):
        import linux_voice_assistant.util as util

        util._version_cache = None

        with patch.object(Path, "read_text", return_value="4.0.0"):
            first = util.get_version()

        second = util.get_version()
        assert first == second == "4.0.0"

    def test_returns_unknown_on_permission_error(self):
        import linux_voice_assistant.util as util

        util._version_cache = None

        with patch.object(Path, "read_text", side_effect=PermissionError):
            result = util.get_version()
        assert result == "unknown"


# ---------------------------------------------------------------------------
# get_esphome_version()
# ---------------------------------------------------------------------------


class TestGetEsphomeVersion:
    def setup_method(self):
        import linux_voice_assistant.util as util

        util._esphome_version_cache = None

    def test_returns_version_when_package_installed(self):
        import linux_voice_assistant.util as util

        with patch("linux_voice_assistant.util.version", return_value="42.7.0"):
            result = util.get_esphome_version()
        assert result == "42.7.0"

    def test_returns_unknown_when_package_not_installed(self):
        from importlib.metadata import PackageNotFoundError

        import linux_voice_assistant.util as util

        with patch("linux_voice_assistant.util.version", side_effect=PackageNotFoundError):
            result = util.get_esphome_version()
        assert result == "unknown"

    def test_caches_result_after_first_call(self):
        import linux_voice_assistant.util as util

        with patch("linux_voice_assistant.util.version", return_value="1.0.0") as mock_ver:
            util.get_esphome_version()
            util.get_esphome_version()
            mock_ver.assert_called_once()

    def test_returns_cached_value_on_second_call(self):
        import linux_voice_assistant.util as util

        with patch("linux_voice_assistant.util.version", return_value="5.0.0"):
            first = util.get_esphome_version()

        second = util.get_esphome_version()
        assert first == second == "5.0.0"


# ---------------------------------------------------------------------------
# call_all()
# ---------------------------------------------------------------------------


class TestCallAll:
    def test_calls_single_callable(self):
        from linux_voice_assistant.util import call_all

        mock_fn = MagicMock()
        call_all(mock_fn)
        mock_fn.assert_called_once()

    def test_calls_multiple_callables_in_order(self):
        from linux_voice_assistant.util import call_all

        calls = []
        call_all(
            lambda: calls.append(1),
            lambda: calls.append(2),
            lambda: calls.append(3),
        )
        assert calls == [1, 2, 3]

    def test_skips_none_entries(self):
        from linux_voice_assistant.util import call_all

        mock_fn = MagicMock()
        call_all(None, mock_fn, None)
        mock_fn.assert_called_once()

    def test_all_none_does_nothing(self):
        from linux_voice_assistant.util import call_all

        call_all(None, None, None)

    def test_empty_args_does_nothing(self):
        from linux_voice_assistant.util import call_all

        call_all()

    def test_none_mixed_with_callables_calls_only_non_none(self):
        from linux_voice_assistant.util import call_all

        results = []
        call_all(None, lambda: results.append("a"), None, lambda: results.append("b"))
        assert results == ["a", "b"]


# ---------------------------------------------------------------------------
# get_default_interface() and get_default_ipv4()
# ---------------------------------------------------------------------------


class TestGetDefaultInterface:
    def test_returns_interface_name_from_gateway(self):
        import linux_voice_assistant.util as util

        with patch("linux_voice_assistant.util.netifaces") as mock_netifaces:
            # Set AF_INET before building the dict so the key matches
            mock_netifaces.AF_INET = 2
            mock_netifaces.default_gateway.return_value = {2: ("192.168.1.1", "eth0")}
            result = util.get_default_interface()
        assert result == "eth0"

    def test_returns_none_when_no_gateway(self):
        import linux_voice_assistant.util as util

        with patch("linux_voice_assistant.util.netifaces") as mock_netifaces:
            mock_netifaces.default_gateway.return_value = {}
            result = util.get_default_interface()
        assert result is None

    def test_returns_none_when_no_ipv4_gateway(self):
        import linux_voice_assistant.util as util

        with patch("linux_voice_assistant.util.netifaces") as mock_netifaces:
            mock_netifaces.AF_INET = 2
            # Only a non-IPv4 gateway present
            mock_netifaces.default_gateway.return_value = {99: ("10.0.0.1", "eth1")}
            result = util.get_default_interface()
        assert result is None


class TestGetDefaultIpv4:
    def test_returns_ip_for_interface(self):
        import linux_voice_assistant.util as util

        with patch("linux_voice_assistant.util.netifaces") as mock_netifaces:
            mock_netifaces.AF_INET = 2
            mock_netifaces.ifaddresses.return_value = {2: [{"addr": "192.168.1.50"}]}
            result = util.get_default_ipv4("eth0")
        assert result == "192.168.1.50"

    def test_returns_none_for_empty_interface(self):
        import linux_voice_assistant.util as util

        result = util.get_default_ipv4("")
        assert result is None

    def test_returns_none_for_none_interface(self):
        import linux_voice_assistant.util as util

        result = util.get_default_ipv4(None)
        assert result is None

    def test_returns_none_when_no_ipv4_address(self):
        import linux_voice_assistant.util as util

        with patch("linux_voice_assistant.util.netifaces") as mock_netifaces:
            mock_netifaces.AF_INET = 2
            mock_netifaces.ifaddresses.return_value = {}
            result = util.get_default_ipv4("eth0")
        assert result is None
