from unittest.mock import patch

from yslow.checks.dns import (
    _measure_resolver,
    _random_subdomain,
    run,
    summary,
)
from yslow.models import DnsResult


class TestRandomSubdomain:
    def test_format(self):
        sub = _random_subdomain()
        assert sub.endswith(".google.com")
        prefix = sub.split(".")[0]
        assert len(prefix) == 8  # token_hex(4) produces 8 hex chars

    def test_unique(self):
        subs = {_random_subdomain() for _ in range(10)}
        assert len(subs) == 10


class TestMeasureResolver:
    @patch("yslow.checks.dns._query_resolver")
    def test_returns_dns_result(self, mock_query):
        mock_query.return_value = 5.0
        result = _measure_resolver("8.8.8.8", "Google DNS", is_configured=False)
        assert isinstance(result, DnsResult)
        assert result.server == "8.8.8.8"
        assert result.label == "Google DNS"
        assert result.cached_ms == 5.0
        assert result.uncached_ms == 5.0
        assert result.is_configured is False

    @patch("yslow.checks.dns._query_resolver")
    def test_configured_flag(self, mock_query):
        mock_query.return_value = 5.0
        result = _measure_resolver("10.0.0.1", "ISP DNS", is_configured=True)
        assert result.is_configured is True

    @patch("yslow.checks.dns._query_resolver")
    def test_handles_timeout(self, mock_query):
        mock_query.return_value = None
        result = _measure_resolver("8.8.8.8", "Google DNS", is_configured=False)
        assert result.cached_ms is None
        assert result.uncached_ms is None


class TestRun:
    @patch("yslow.checks.dns._measure_resolver")
    @patch("yslow.checks.dns.get_system_nameservers")
    def test_deduplicates_configured_and_public(self, mock_ns, mock_measure):
        """If configured resolver is also in public list, don't test it twice."""
        mock_ns.return_value = ["8.8.8.8"]

        def side_effect(server, label, is_configured):
            return DnsResult(server, label, 2.0, 60.0, is_configured)

        mock_measure.side_effect = side_effect
        results = run()
        # 8.8.8.8 should appear once (configured), plus OpenDNS + Cloudflare
        servers = [r.server for r in results]
        assert servers.count("8.8.8.8") == 1
        assert len(results) == 3

    @patch("yslow.checks.dns._measure_resolver")
    @patch("yslow.checks.dns.get_system_nameservers")
    def test_configured_resolver_comes_first(self, mock_ns, mock_measure):
        mock_ns.return_value = ["10.0.0.1"]

        def side_effect(server, label, is_configured):
            return DnsResult(server, label, 2.0, 60.0, is_configured)

        mock_measure.side_effect = side_effect
        results = run()
        assert results[0].server == "10.0.0.1"
        assert results[0].label == "10.0.0.1 (DNS)"
        assert results[0].is_configured is True


class TestSummary:
    def test_all_available(self):
        results = [
            DnsResult("10.0.0.1", "10.0.0.1", 1.0, 50.0, True),
            DnsResult("8.8.8.8", "Google DNS", 2.0, 60.0, False),
        ]
        text = summary(results)
        assert "10.0.0.1" in text
        assert "Google DNS" in text
        assert "cached" in text
        assert "uncached" in text

    def test_all_unavailable(self):
        results = [DnsResult("10.0.0.1", "10.0.0.1", None, None, True)]
        text = summary(results)
        assert "unavailable" in text
