"""
wivexx_wol.py — Wake-on-LAN helpers  (v1.7)
═══════════════════════════════════════════════════════════════════════
This PC (PCDURITAS / Intel I225-V) was verified WOL-capable on 2026-06-11:
  • "Wake on Magic Packet"          : Enabled
  • "Wake On Magic Packet From S5"  : Enabled  (S5 = fully shut down)
  • Adapter armed in Windows        : yes (powercfg /devicequery wake_armed)
  • Fast Startup (hiberboot)        : disabled (good — it breaks WOL)

What this module does:
  • send_magic_packet(mac)  — wake OTHER machines from this PC
  • get_local_wol_info()    — MAC / IP / hostname of this PC (what a phone
                              app or the Raspberry Pi needs to wake it)
  • status_text()           — spoken summary for the voice query

What it cannot do: wake THIS PC while it is off — the packet must come from
another device on the LAN (phone app on the same Wi-Fi, the future Raspberry
Pi, or a router).  See raspberry_pi_wol.py for the ready-to-use Pi sender.

SECURITY — read before exposing anything to the internet:
  Magic packets are unauthenticated UDP broadcasts. On your home LAN that is
  fine: anyone already inside the network could wake the PC, nothing more.
  NEVER port-forward UDP 7/9 (or the WIVEXX web port) from the router to the
  PC — that hands "turn my PC on" (and worse) to the whole internet, and
  forwarded WOL usually breaks anyway (routers drop broadcasts).
  The safe remote design, when the Pi arrives:
      phone → VPN (Tailscale/WireGuard — free, 10-min setup) → Pi → magic
      packet on the LAN → PC boots → WIVEXX autostarts → phone reaches the
      WIVEXX web page through the same VPN. Zero ports exposed.
"""

import re
import socket

DEFAULT_PORT = 9


def _parse_mac(mac: str) -> bytes:
    clean = re.sub(r"[^0-9a-fA-F]", "", mac or "")
    if len(clean) != 12:
        raise ValueError(f"Invalid MAC address: {mac!r}")
    return bytes.fromhex(clean)


def build_magic_packet(mac: str) -> bytes:
    """6×FF followed by the MAC repeated 16 times (102 bytes)."""
    mac_bytes = _parse_mac(mac)
    return b"\xff" * 6 + mac_bytes * 16


def send_magic_packet(mac: str, broadcast: str = "255.255.255.255",
                      port: int = DEFAULT_PORT, repeats: int = 3) -> bool:
    """Broadcast a WOL magic packet for *mac*. Returns True if sent."""
    try:
        payload = build_magic_packet(mac)
        with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
            s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
            for _ in range(max(1, repeats)):
                s.sendto(payload, (broadcast, port))
        return True
    except Exception:
        return False


def get_local_wol_info() -> dict:
    """MAC / IPv4 / hostname of the adapter that carries the default route —
    i.e. exactly what another device needs to wake this PC."""
    info = {"hostname": socket.gethostname(), "ipv4": "", "mac": ""}
    try:
        with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
            s.connect(("8.8.8.8", 80))       # no traffic actually sent
            info["ipv4"] = s.getsockname()[0]
    except Exception:
        pass
    try:
        import psutil
        for ifname, addrs in psutil.net_if_addrs().items():
            ips  = [a.address for a in addrs if a.family == socket.AF_INET]
            macs = [a.address for a in addrs
                    if getattr(a, "family", None) == getattr(psutil, "AF_LINK", -1)]
            if info["ipv4"] and info["ipv4"] in ips and macs:
                info["mac"] = macs[0].replace("-", ":").upper()
                break
    except Exception:
        pass
    return info


def status_text() -> str:
    """Spoken Wake-on-LAN status for the voice query."""
    info = get_local_wol_info()
    mac = info.get("mac") or "unknown"
    spoken_mac = mac.replace(":", " ")
    return (
        "Wake-on-LAN is enabled on this machine, sir — including from full "
        f"shutdown. My network address is {info.get('ipv4', 'unknown')} and "
        f"the MAC address a wake signal must target is {spoken_mac}. "
        "Any phone WOL app on our Wi-Fi can wake me, and the Raspberry Pi "
        "script is ready in the WIVEXX folder."
    )
