import subprocess
import platform

from .probe import tcp_ping, icmp_ping

GATEWAY_PORTS = [80, 443, 53, 22]


def get_default_gateway() -> str | None:
    """Get the default gateway IP from the routing table."""
    if platform.system() == "Darwin":
        try:
            out = subprocess.check_output(["netstat", "-rn"], text=True, timeout=5)
            for line in out.strip().splitlines():
                parts = line.split()
                try:
                    if "default" in parts:
                        return parts[1]
                except IndexError:
                    pass
        except (subprocess.SubprocessError, ValueError, OSError):
            pass
    else:
        try:
            out = subprocess.check_output(
                ["ip", "route", "show", "default"], text=True, timeout=5
            )
            for line in out.strip().splitlines():
                parts = line.split()
                if "via" in parts:
                    return parts[parts.index("via") + 1]
        except (subprocess.SubprocessError, ValueError, IndexError, OSError):
            pass
    return None


def get_system_nameservers() -> list[str]:
    """Get the DNS nameservers the system is actually using.

    Tries multiple sources in order of reliability:
    1. resolvectl (systemd-resolved) — what the stub resolver forwards to
    2. nmcli (NetworkManager) — what DHCP provided
    3. /etc/resolv.conf — direct parsing as fallback

    Filters out local stub resolvers (127.0.0.53).
    """
    for source in [
        _get_nameservers_resolvectl,
        _get_nameservers_nmcli,
        _get_nameservers_resolv_conf,
    ]:
        servers = source()
        servers = [s for s in servers if s != "127.0.0.53"]
        if servers:
            return servers
    return []


def _get_nameservers_resolvectl() -> list[str]:
    """Parse 'resolvectl dns' for upstream nameservers."""
    try:
        out = subprocess.check_output(
            ["resolvectl", "dns"], text=True, timeout=5, stderr=subprocess.DEVNULL
        )
        servers: list[str] = []
        for line in out.strip().splitlines():
            # Lines look like: "Link 2 (wlp1s0): 8.8.8.8 8.8.4.4"
            if ":" not in line:
                continue
            _, _, addrs = line.partition(":")
            for addr in addrs.split():
                if addr and addr not in servers:
                    servers.append(addr)
        return servers
    except (subprocess.SubprocessError, OSError):
        return []


def _get_nameservers_nmcli() -> list[str]:
    """Get DNS servers from NetworkManager (typically DHCP-provided)."""
    try:
        out = subprocess.check_output(
            ["nmcli", "-t", "-f", "IP4.DNS", "dev", "show"],
            text=True,
            timeout=5,
            stderr=subprocess.DEVNULL,
        )
        servers: list[str] = []
        for line in out.strip().splitlines():
            # Lines look like: "IP4.DNS[1]:8.8.8.8"
            if ":" not in line:
                continue
            _, _, addr = line.partition(":")
            addr = addr.strip()
            if addr and addr not in servers:
                servers.append(addr)
        return servers
    except (subprocess.SubprocessError, OSError):
        return []


def _get_nameservers_resolv_conf() -> list[str]:
    """Parse /etc/resolv.conf for nameserver entries."""
    try:
        with open("/etc/resolv.conf") as f:
            servers: list[str] = []
            for line in f:
                line = line.strip()
                if line.startswith("nameserver"):
                    parts = line.split()
                    if len(parts) >= 2 and parts[1] not in servers:
                        servers.append(parts[1])
            return servers
    except OSError:
        return []


def find_gateway_probe_method(gateway: str) -> tuple[str, int | None]:
    """Find a working TCP port on the gateway, or fall back to ICMP."""
    for port in GATEWAY_PORTS:
        if tcp_ping(gateway, port) is not None:
            return ("tcp", port)
    if icmp_ping(gateway) is not None:
        return ("icmp", None)
    return ("none", None)
