"""Información de red local Windows (adaptadores, DHCP, gateway)."""
from __future__ import annotations

import ctypes
import re
import socket
import subprocess
from typing import List, Optional

from .models import NetInfo, guess_cidr


def is_admin() -> bool:
    try:
        return bool(ctypes.windll.shell32.IsUserAnAdmin())
    except Exception:
        return False


def _run(cmd: List[str], timeout: float = 8.0) -> str:
    try:
        p = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            timeout=timeout,
            creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
            encoding="utf-8",
            errors="replace",
        )
        return (p.stdout or "") + "\n" + (p.stderr or "")
    except Exception as e:
        return str(e)


def list_adapters() -> List[dict]:
    """Lista adaptadores con IP IPv4 vía ipconfig."""
    out = _run(["ipconfig", "/all"])
    adapters: List[dict] = []
    current = None
    for line in out.splitlines():
        if not line.strip():
            continue
        # Nuevo adaptador: "Adaptador de Ethernet Ethernet:" / "Adaptador de LAN inalámbrica Wi-Fi:"
        if (line.startswith("Adaptador") or line.startswith("Ethernet adapter")
                or line.startswith("Wireless LAN adapter") or line.startswith("PPP adapter")):
            if current and current.get("ip"):
                adapters.append(current)
            name = line.split(":", 1)[0].strip()
            current = {"name": name, "ip": "", "mask": "", "gateway": "", "dhcp": "", "dns": []}
            continue
        if current is None:
            continue
        low = line.lower()
        if "dirección ipv4" in low or "ipv4 address" in low:
            m = re.search(r"(\d+\.\d+\.\d+\.\d+)", line)
            if m:
                current["ip"] = m.group(1)
        elif "máscara de subred" in low or "subnet mask" in low:
            m = re.search(r"(\d+\.\d+\.\d+\.\d+)", line)
            if m:
                current["mask"] = m.group(1)
        elif "puerta de enlace" in low or "default gateway" in low:
            m = re.search(r"(\d+\.\d+\.\d+\.\d+)", line)
            if m:
                current["gateway"] = m.group(1)
        elif "servidor dhcp" in low or "dhcp server" in low:
            m = re.search(r"(\d+\.\d+\.\d+\.\d+)", line)
            if m:
                current["dhcp"] = m.group(1)
        elif "servidores dns" in low or "dns servers" in low:
            m = re.search(r"(\d+\.\d+\.\d+\.\d+)", line)
            if m:
                current["dns"].append(m.group(1))
        elif re.match(r"^\s+\d+\.\d+\.\d+\.\d+", line) and current.get("dns") is not None:
            m = re.search(r"(\d+\.\d+\.\d+\.\d+)", line)
            if m and "dhcp" not in low:
                # posible DNS adicional
                ip = m.group(1)
                if ip not in current["dns"] and not current.get("gateway") == ip:
                    # heurística: si ya hay DNS, líneas indentadas son más DNS
                    if current["dns"]:
                        current["dns"].append(ip)
    if current and current.get("ip"):
        adapters.append(current)
    return adapters


def get_net_info(preferred_adapter: Optional[str] = None) -> NetInfo:
    adapters = list_adapters()
    info = NetInfo(is_admin=is_admin(), adapters=adapters)
    chosen = None
    if preferred_adapter:
        for a in adapters:
            if preferred_adapter.lower() in a["name"].lower():
                chosen = a
                break
    if not chosen:
        # Preferir Wi-Fi o Ethernet con gateway
        for a in adapters:
            if a.get("gateway") and a.get("ip") and not a["ip"].startswith("169.254"):
                chosen = a
                break
    if not chosen and adapters:
        for a in adapters:
            if a.get("ip") and not a["ip"].startswith("169.254"):
                chosen = a
                break
    if not chosen and adapters:
        chosen = adapters[0]
    if chosen:
        info.adapter = chosen["name"]
        info.ip = chosen.get("ip", "")
        info.mask = chosen.get("mask", "255.255.255.0")
        info.gateway = chosen.get("gateway", "")
        info.dhcp_server = chosen.get("dhcp", "")
        info.dns = list(chosen.get("dns") or [])
        info.dhcp_enabled = bool(info.dhcp_server)
        if info.ip:
            info.cidr = guess_cidr(info.ip, info.mask)
    else:
        # fallback socket
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.connect(("8.8.8.8", 80))
            info.ip = s.getsockname()[0]
            s.close()
            info.mask = "255.255.255.0"
            info.cidr = guess_cidr(info.ip, info.mask)
            info.adapter = "(detectado por socket)"
        except Exception:
            pass
    return info


def suspicious_dhcp_notes(info: NetInfo, camera_ips: List[str]) -> List[str]:
    notes = []
    if info.ip.startswith("169.254"):
        notes.append("El portátil tiene IP APIPA (169.254.x.x): sin DHCP/gateway válido.")
    if info.dhcp_server and info.gateway and info.dhcp_server != info.gateway:
        notes.append(
            f"DHCP server ({info.dhcp_server}) distinto del gateway ({info.gateway}). "
            "Puede haber 2 routers/DHCP en la red."
        )
    if not info.gateway:
        notes.append("No se detectó gateway: posible aislamiento de red.")
    for ip in camera_ips:
        try:
            if info.cidr and info.ip:
                # misma /24 aproximada
                if ".".join(ip.split(".")[:3]) != ".".join(info.ip.split(".")[:3]):
                    notes.append(f"Cámara {ip} fuera de la subred del portátil ({info.cidr}).")
        except Exception:
            pass
    return notes
