"""
Rejoindre en un clic la partie hébergée par un pair.

Supprime le copier-coller : jusqu'ici le lobby proposait « Copier adresse », à
recoller ensuite dans la console du jeu. C'est le point faible partagé par
Radmin VPN et Hamachi — ils ne connaissent que des noms de réseau, jamais les
parties qui s'y jouent.

Simple lanceur d'URL, dans le même esprit que `file_browser.py` : Meshamoto ne
pilote pas le jeu, il délègue à Steam via son schéma `steam://`. **Aucune
dépendance ajoutée.**

`steam://connect/<hôte>:<port>` laisse Steam identifier le jeu en interrogeant
lui-même le serveur — ce qui fonctionne parce que la requête part en **unicast**
à travers le tunnel. C'est la même raison qui rend `a2s_client` possible alors
que la découverte par diffusion est condamnée (point 36).
"""

from __future__ import annotations

import logging
import shutil
import subprocess
import sys

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


class SteamIntrouvableError(RuntimeError):
    """
    Steam n'est pas installé : l'appelant doit retomber sur la copie d'adresse.

    Exception dédiée plutôt qu'un booléen de retour, pour que l'appelant ne
    puisse pas confondre « lancé » et « pas lancé, débrouille-toi » — un pair
    peut parfaitement héberger un jeu hors Steam (Minecraft, Palworld), auquel
    cas le bouton doit rester utile au lieu de disparaître.
    """


def steam_url(virtual_ip: str, port: int) -> str:
    """URL de connexion directe. Toujours l'IP virtuelle, jamais le nom DNS
    interne : marche identiquement que le DNS interne soit activé ou non (même
    raisonnement que « Copier adresse » et que `file_browser`)."""
    return f"steam://connect/{virtual_ip}:{port}"


def steam_disponible() -> bool:
    """
    Vrai si Steam semble installé sur cette machine.

    Sous Windows on ne peut pas se fier à la présence d'un exécutable dans le
    PATH (Steam ne s'y met pas) : c'est l'enregistrement du schéma `steam://`
    qui compte, et il est posé par l'installateur. On teste donc la clé de
    registre correspondante, faute de quoi on lancerait une URL que personne ne
    sait ouvrir — Windows afficherait alors sa propre boîte « rechercher une
    application », déroutante et sans rapport avec Meshamoto.
    """
    if sys.platform == "win32":
        try:
            import winreg

            with winreg.OpenKey(winreg.HKEY_CLASSES_ROOT, "steam"):
                return True
        except (ImportError, OSError):
            return False

    if sys.platform.startswith("linux"):
        # Le binaire `steam` est un script de lancement placé dans le PATH par
        # tous les modes d'installation courants (paquet, Flatpak via alias).
        return shutil.which("steam") is not None

    return False


def rejoindre(virtual_ip: str, port: int) -> str:
    """
    Lance Steam sur la partie du pair. Renvoie l'URL utilisée (pour le journal
    et la barre d'état), ou lève `SteamIntrouvableError` si Steam est absent.

    Ne bloque jamais et ne vérifie aucun code de retour : `explorer.exe` en
    renvoie un non nul même en cas de succès — piège déjà rencontré et documenté
    dans `file_browser.open_peer_files`.
    """
    if not steam_disponible():
        raise SteamIntrouvableError(
            "Steam ne semble pas installé sur cette machine : impossible de lancer "
            "la partie automatiquement."
        )

    url = steam_url(virtual_ip, port)

    if sys.platform == "win32":
        # `explorer.exe <url>` délègue au handler enregistré du schéma. On évite
        # `os.startfile` : le client tourne élevé (uac_admin), et lancer Steam
        # dans ce contexte lui ferait hériter des droits administrateur.
        subprocess.Popen(["explorer.exe", url])
        return url

    if sys.platform.startswith("linux"):
        xdg_open = shutil.which("xdg-open")
        if xdg_open is None:
            raise SteamIntrouvableError(
                "xdg-open introuvable — installez xdg-utils pour lancer la partie "
                f"automatiquement, ou ouvrez {url} manuellement."
            )
        subprocess.Popen([xdg_open, url])
        return url

    raise RuntimeError(
        f"Plateforme non supportée : {sys.platform}. "
        "Seuls Windows et Linux sont pris en charge par ce client."
    )
