"""
Port UDP d'écoute WireGuard choisi par l'utilisateur, mémorisé localement.

Le défaut 51820 est la convention WireGuard, mais il est indisponible dès qu'un autre
tunnel WireGuard tourne déjà sur la machine (application WireGuard officielle, client
VPN d'entreprise...). Cas rencontré en conditions réelles le 2026-07-29 : le service du
tunnel Meshamoto était installé avec succès puis mourait aussitôt, faute de pouvoir se
lier au port — sans aucun message, le client affichant ses pairs comme si tout allait
bien (voir wireguard_backend_windows._verify_service_started, qui détecte désormais ce
cas et oriente vers ce réglage).

Réglage purement local, sans coordination entre pairs : chaque machine annonce son
propre port aux autres dans son endpoint, deux pairs peuvent donc écouter sur des ports
différents.
"""

from __future__ import annotations

import json
import logging
from pathlib import Path

logger = logging.getLogger("meshamoto-client.wireguard_port_settings")

# Ports non privilégiés uniquement : se lier en dessous de 1024 exigerait des droits
# supplémentaires sur Linux sans aucun bénéfice pour un tunnel WireGuard.
MIN_PORT = 1024
MAX_PORT = 65535


def is_valid_port(port: int) -> bool:
    return MIN_PORT <= port <= MAX_PORT


def load_listen_port(path: Path, default: int) -> int:
    """
    Retourne le port mémorisé, ou `default` si le fichier est absent, illisible ou
    contient une valeur hors plage — un réglage corrompu ne doit jamais empêcher le
    client de démarrer.
    """
    if not path.exists():
        return default
    try:
        data = json.loads(path.read_text(encoding="utf-8"))
        port = int(data["listen_port"])
        if not is_valid_port(port):
            raise ValueError(port)
        return port
    except (OSError, json.JSONDecodeError, KeyError, ValueError, TypeError):
        logger.warning("Impossible de lire %s, port mémorisé ignoré", path)
        return default


def save_listen_port(path: Path, port: int) -> None:
    if not is_valid_port(port):
        raise ValueError(
            f"Port hors de la plage autorisée ({MIN_PORT}-{MAX_PORT}) : {port}"
        )
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps({"listen_port": port}), encoding="utf-8")
