from __future__ import annotations

from dataclasses import dataclass


@dataclass(slots=True)
class NatResult:
    opened: bool
    method: str
    public_ip: str | None = None
    public_port: int | None = None
    note: str = ""


def try_upnp_map(
    internal_port: int,
    external_port: int | None = None,
    description: str = "2xPPg node",
) -> NatResult:
    """Attempt to create a UPnP port map.

    This uses the local router's UPnP interface. If unsupported, we return
    a non-failing result so apps can continue with hole punching or relay.
    """
    try:
        import miniupnpc  # type: ignore
    except Exception:
        return NatResult(
            opened=False,
            method="upnp",
            note="miniupnpc is not installed; skipping UPnP mapping.",
        )

    external = external_port or internal_port

    try:
        client = miniupnpc.UPnP()
        client.discoverdelay = 200
        client.discover()
        client.selectigd()
        public_ip = client.externalipaddress()
        ok = client.addportmapping(
            external, "TCP", client.lanaddr, internal_port, description, ""
        )
        if ok:
            return NatResult(
                opened=True,
                method="upnp",
                public_ip=public_ip,
                public_port=external,
                note="UPnP mapping active.",
            )
        return NatResult(
            opened=False,
            method="upnp",
            note="Router rejected port mapping.",
        )
    except Exception as exc:
        return NatResult(
            opened=False,
            method="upnp",
            note=f"UPnP failed: {exc}",
        )
