import secrets
import statistics
import time

import dns.message
import dns.name
import dns.query
import dns.rdatatype

from ..models import DnsResult
from ..net import get_system_nameservers

PUBLIC_RESOLVERS = [
    ("8.8.8.8", "Google DNS"),
    ("208.67.222.222", "OpenDNS"),
    ("1.1.1.1", "Cloudflare DNS"),
]

# Well-known public DNS IPs → label, so we can label configured resolvers
# that happen to be public DNS.
KNOWN_LABELS: dict[str, str] = {
    "8.8.8.8": "Google DNS",
    "8.8.4.4": "Google DNS",
    "1.1.1.1": "Cloudflare DNS",
    "1.0.0.1": "Cloudflare DNS",
    "208.67.222.222": "OpenDNS",
    "208.67.220.220": "OpenDNS",
}

CACHED_DOMAIN = "google.com"
UNCACHED_COUNT = 3
TIMEOUT_S = 3.0

LABEL = "DNS:"


def _query_resolver(server: str, domain: str) -> float | None:
    """Send a DNS A query to a specific resolver and return response time in ms."""
    try:
        qname = dns.name.from_text(domain)
        request = dns.message.make_query(qname, dns.rdatatype.A)
        start = time.monotonic()
        dns.query.udp(request, server, timeout=TIMEOUT_S)
        elapsed = (time.monotonic() - start) * 1000
        return elapsed
    except Exception:
        return None


def _random_subdomain() -> str:
    """Generate a random subdomain of google.com for cache-miss testing."""
    return f"{secrets.token_hex(4)}.{CACHED_DOMAIN}"


def _measure_resolver(server: str, label: str, is_configured: bool) -> DnsResult:
    """Measure cached and uncached DNS performance for a specific resolver."""
    # Warm the cache
    _query_resolver(server, CACHED_DOMAIN)
    # Measure cached
    cached_ms = _query_resolver(server, CACHED_DOMAIN)

    # Measure uncached (median of 3 random subdomains)
    uncached_times = []
    for _ in range(UNCACHED_COUNT):
        t = _query_resolver(server, _random_subdomain())
        if t is not None:
            uncached_times.append(t)

    uncached_ms = statistics.median(uncached_times) if uncached_times else None

    return DnsResult(
        server=server,
        label=label,
        cached_ms=cached_ms,
        uncached_ms=uncached_ms,
        is_configured=is_configured,
    )


def run() -> list[DnsResult]:
    """Measure DNS resolution performance across configured and public resolvers."""
    results: list[DnsResult] = []
    tested: set[str] = set()

    # Test the first configured nameserver (the one the system actually uses)
    configured = get_system_nameservers()
    if configured:
        server = configured[0]
        label = KNOWN_LABELS.get(server, f"{server} (DNS)")
        results.append(_measure_resolver(server, label, is_configured=True))
        tested.add(server)

    # Test public resolvers that aren't already tested
    for server, label in PUBLIC_RESOLVERS:
        if server not in tested:
            results.append(_measure_resolver(server, label, is_configured=False))
            tested.add(server)

    return results


def summary(results: list[DnsResult]) -> str:
    """Multi-line summary showing cached/uncached for each resolver."""
    lbl = f"{LABEL:14s}"
    available = [
        r for r in results if r.cached_ms is not None or r.uncached_ms is not None
    ]
    if not available:
        return f"  {lbl}DNS unavailable"
    lines = []
    for r in available:
        parts = []
        if r.cached_ms is not None:
            parts.append(f"{r.cached_ms:5.1f} ms cached")
        if r.uncached_ms is not None:
            parts.append(f"{r.uncached_ms:5.1f} ms uncached")
        name = f"{r.label:14s}"
        lines.append(f"  {name}{', '.join(parts)}")
    return "\n".join(lines)
