## ──────────────────────────────────────────────
##  WGSecure (WGS) — Makefile
##  Compilation Linux & Windows via PyInstaller
## ──────────────────────────────────────────────

APP     := wgsecure
VERSION := 0.4.3
VENV    := .venv
PYTHON  := $(VENV)/bin/python3
PIP     := $(VENV)/bin/pip
SRC     := main.py
DIST    := dist
BUILD   := build
ICON    := /tmp/wgsecure_icon_build.png

# Options PyInstaller communes aux deux plateformes
PI_OPTS := \
	--noconfirm \
	--clean \
	--name $(APP) \
	--hidden-import pyotp \
	--hidden-import qrcode \
	--hidden-import qrcode.image.pil \
	--hidden-import PIL \
	--hidden-import PIL.Image \
	--hidden-import cryptography \
	--hidden-import cryptography.hazmat.primitives.asymmetric.x25519 \
	--collect-submodules PyQt6

.PHONY: all linux windows release version changelog install install-gnome uninstall-gnome run run-admin icon reset-password setup-sudoers check-privileges clean clean-all help venv

# ── Cible par défaut ────────────────────────────────────────────────────────
all: linux

# ── Environnement virtuel ────────────────────────────────────────────────────
# Le venv est reconstruit si la version de Python du système a changé : un venv
# créé pour python3.13 pointe vers .venv/lib/python3.13/site-packages, que
# python3.14 n'ouvre pas. L'interpréteur démarrait alors sans aucune de ses
# dépendances (« No module named PyQt6 ») sans que make ne le remarque.
venv:
	@if [ -x $(PYTHON) ] && \
	   [ "$$($(PYTHON) -c 'import sys;print("%d.%d"%sys.version_info[:2])' 2>/dev/null)" \
	     = "$$(python3 -c 'import sys;print("%d.%d"%sys.version_info[:2])')" ] && \
	   $(PYTHON) -c 'import PyQt6' >/dev/null 2>&1; then \
		echo "  ✅  Environnement virtuel valide ($(VENV))"; \
	else \
		echo "  ♻️   (Re)création de l'environnement virtuel $(VENV)…"; \
		rm -rf $(VENV); \
		python3 -m venv $(VENV); \
		$(PIP) install --upgrade pip --quiet; \
		$(PIP) install -r requirements.txt --quiet; \
	fi

# ── Génération de l'icône PNG (utilisée par PyInstaller) ────────────────────
icon:
	@$(PYTHON) -c "\
import sys; sys.path.insert(0,'.');\
from PyQt6.QtWidgets import QApplication; app=QApplication(sys.argv);\
from app.ui.icons import icon_app;\
pix=icon_app().pixmap(256,256); pix.save('$(ICON)','PNG');\
print('  Icône générée →', '$(ICON)')" 2>/dev/null
	@test -f $(ICON) || (echo "  ❌  Génération icône échouée"; exit 1)

# ── Binaire Linux ────────────────────────────────────────────────────────────
linux: install icon
	@echo ""
	@echo "  🐧  Compilation Linux (PyInstaller onefile)…"
	@echo ""
	$(PYTHON) -m PyInstaller $(PI_OPTS) \
		--onefile \
		--icon=$(ICON) \
		$(SRC)
	@echo ""
	@echo "  ✅  Binaire Linux → $(DIST)/$(APP)"
	@echo ""

# ── Binaire Windows (via Wine + Python Windows) ──────────────────────────────
# Le hook PyQt6 de pyinstaller-hooks-contrib introspecte QLibraryInfo pour
# localiser Qt6/plugins/platforms au moment du build : sous Wine cette
# introspection échoue silencieusement, donc qwindows.dll n'est jamais copié
# et l'exe plante au lancement avec « no Qt platform plugin could be
# initialized ». On force sa copie via --add-data.
WINE_QT_PLUGINS := $(HOME)/.wine/drive_c/users/$(USER)/AppData/Local/Programs/Python/Python311/Lib/site-packages/PyQt6/Qt6/plugins

windows: icon
	@echo ""
	@echo "  🪟  Compilation Windows (Wine + PyInstaller)…"
	@echo ""
	@which wine > /dev/null 2>&1 \
		|| (echo "  ❌  Wine non installé → sudo apt install wine"; exit 1)
	@test -f "$(WINE_QT_PLUGINS)/platforms/qwindows.dll" \
		|| (echo "  ❌  qwindows.dll introuvable dans $(WINE_QT_PLUGINS)/platforms"; \
		    echo "  → Installez PyQt6 dans Wine avec :  make setup-wine"; exit 1)
	@wine $(WINE_PYTHON) -m PyInstaller $(PI_OPTS) \
		--onefile \
		--windowed \
		--icon=$(ICON) \
		--add-data "$(WINE_QT_PLUGINS)/platforms;PyQt6/Qt6/plugins/platforms" \
		--add-data "$(WINE_QT_PLUGINS)/styles;PyQt6/Qt6/plugins/styles" \
		$(SRC) \
	|| (echo ""; \
	    echo "  ❌  Échec : Python Windows introuvable dans Wine."; \
	    echo "  → Installez-le avec :  make setup-wine"; \
	    echo ""; exit 1)
	@echo ""
	@echo "  ✅  Binaire Windows → $(DIST)/$(APP).exe"
	@echo ""

# ── Installation de Python dans Wine ─────────────────────────────────────────
WINE_PY_VER   := 3.11.9
WINE_PY_URL   := https://www.python.org/ftp/python/$(WINE_PY_VER)/python-$(WINE_PY_VER)-amd64.exe
WINE_PY_EXE   := /tmp/python-$(WINE_PY_VER)-amd64.exe
WINE_PYTHON   := $(HOME)/.wine/drive_c/users/$(USER)/AppData/Local/Programs/Python/Python311/python.exe

setup-wine:
	@echo "  📦  Installation Python $(WINE_PY_VER) Windows dans Wine…"
	@which wine > /dev/null 2>&1 \
		|| (echo "  ❌  wine non installé → sudo apt install wine"; exit 1)
	@test -f $(WINE_PY_EXE) \
		|| (echo "  ⬇️   Téléchargement Python Windows…" \
		    && curl -L -o $(WINE_PY_EXE) $(WINE_PY_URL))
	@echo "  🔧  Installation silencieuse…"
	wine $(WINE_PY_EXE) /quiet InstallAllUsers=0 PrependPath=1
	@echo "  📦  Installation des dépendances…"
	wine $(WINE_PYTHON) -m pip install --upgrade pip
	wine $(WINE_PYTHON) -m pip install -r requirements.txt
	wine $(WINE_PYTHON) -m pip install pyinstaller
	@echo "  ✅  Python Windows prêt dans Wine"

# ── Release (Linux renommé avec version) ─────────────────────────────────────
release: clean linux
	@mv $(DIST)/$(APP) $(DIST)/$(APP)-$(VERSION)-linux-x86_64
	@echo "  📦  Release → $(DIST)/$(APP)-$(VERSION)-linux-x86_64"

# ── Version courante ──────────────────────────────────────────────────────────
version:
	@echo "$(VERSION)"

# ── Dernière entrée du changelog ─────────────────────────────────────────────
changelog:
	@awk '/^## \[/{if(n++)exit} n' CHANGELOG.md

# ── Installation GNOME (utilisateur local, sans sudo) ────────────────────────
INSTALL_BIN  := $(HOME)/.local/bin
INSTALL_ICON := $(HOME)/.local/share/icons/hicolor/256x256/apps
INSTALL_APPS := $(HOME)/.local/share/applications

install-gnome: linux
	@echo ""
	@echo "  🐧  Installation de WGSecure pour GNOME…"
	@mkdir -p $(INSTALL_BIN) $(INSTALL_ICON) $(INSTALL_APPS)
	@cp $(DIST)/$(APP) $(INSTALL_BIN)/$(APP)
	@chmod +x $(INSTALL_BIN)/$(APP)
	@echo "  ✅  Binaire      → $(INSTALL_BIN)/$(APP)"
	@cp $(ICON) $(INSTALL_ICON)/$(APP).png
	@echo "  ✅  Icône        → $(INSTALL_ICON)/$(APP).png"
	@printf '[Desktop Entry]\nType=Application\nName=WGSecure\nComment=WireGuard GUI avec MFA TOTP\nExec=%s\nIcon=%s\nCategories=Network;Security;\nStartupWMClass=wgsecure\nTerminal=false\n' \
		"$(INSTALL_BIN)/$(APP)" "$(APP)" \
		> $(INSTALL_APPS)/$(APP).desktop
	@echo "  ✅  Lanceur      → $(INSTALL_APPS)/$(APP).desktop"
	@gtk-update-icon-cache -f -t $(HOME)/.local/share/icons/hicolor 2>/dev/null || true
	@update-desktop-database $(INSTALL_APPS) 2>/dev/null || true
	@echo ""
	@echo "  🎉  Installation terminée. WGSecure est disponible dans le menu GNOME."
	@echo ""

uninstall-gnome:
	@echo "  🗑️   Désinstallation de WGSecure…"
	@rm -f $(INSTALL_BIN)/$(APP)
	@rm -f $(INSTALL_ICON)/$(APP).png
	@rm -f $(INSTALL_APPS)/$(APP).desktop
	@gtk-update-icon-cache -f -t $(HOME)/.local/share/icons/hicolor 2>/dev/null || true
	@update-desktop-database $(INSTALL_APPS) 2>/dev/null || true
	@echo "  ✅  WGSecure désinstallé"

# ── Dépendances Python ────────────────────────────────────────────────────────
install: venv
	$(PIP) install -r requirements.txt
	$(PIP) install pyinstaller
	@echo "  ✅  Dépendances installées dans $(VENV)"

# ── Diagnostic des droits ────────────────────────────────────────────────────
check-privileges:
	@echo ""
	@echo "  🔐  Droits requis par WGSecure :"
	@$(PYTHON) -c "\
import sys; sys.path.insert(0, '.'); \
from app.utils.platform_utils import privilege_report; \
rows = privilege_report(); \
print('     (exécution en root : aucun droit à configurer)') if not rows else None; \
[print('     ' + ('✅' if ok else '⚠️ ') + '  ' + name.ljust(12) + ('sans mot de passe' if ok else 'dialogue polkit à chaque appel').ljust(32) + '— ' + role) for name, ok, role in rows]; \
print() if not rows else print(chr(10) + '     ⚠️  → lancez  make setup-sudoers  pour supprimer les dialogues.' if any(not ok for _, ok, _ in rows) else chr(10) + '     ✅  Tout est configuré.')"
	@echo ""

# ── Droits sudo wg-quick sans dialogue de mot de passe ───────────────────────
setup-sudoers:
	@echo ""
	@echo "  🔧  Configuration sudoers (wg-quick + nettoyage réseau, sans mot de passe)…"
	@$(PYTHON) -c "\
import os, subprocess, tempfile, shutil; \
user = os.environ.get('USER', os.path.basename(os.path.expanduser('~'))); \
w = lambda n: shutil.which(n) or ''; \
cmds = [c for c in ( \
    w('wg-quick'), \
    w('wg'), \
    w('ip') + ' link delete dev *' if w('ip') else '', \
    w('resolvconf') + ' -d *' if w('resolvconf') else '', \
    w('resolvectl') + ' revert *' if w('resolvectl') else '', \
    w('install') + ' -D -m 600 -o root -g root * /etc/wireguard/*' if w('install') else '', \
) if c]; \
rules = ''.join(user + ' ALL=(ALL) NOPASSWD: ' + c + chr(10) for c in cmds); \
fd, tmp = tempfile.mkstemp(prefix='wgsecure_sudoers_', dir=os.path.expanduser('~')); \
os.write(fd, rules.encode()); os.close(fd); os.chmod(tmp, 0o600); \
chk = subprocess.run(['visudo', '-cqf', tmp], capture_output=True); \
ok = chk.returncode == 0; \
r = subprocess.run(['pkexec', 'bash', '-c', 'install -m 440 -o root -g root ' + tmp + ' /etc/sudoers.d/wgsecure']) if ok else None; \
os.unlink(tmp); \
print('  ❌  Règles sudoers invalides, rien installé : ' + chk.stderr.decode().strip()) if not ok else \
print('  ✅  /etc/sudoers.d/wgsecure configuré — plus de dialogue admin.' if r.returncode == 0 else '  ❌  Échec.')"
	@echo ""

# ── Réinitialisation du mot de passe administrateur ──────────────────────────
reset-password:
	@echo ""
	@echo "  ⚠️   Réinitialisation du mot de passe administrateur WGSecure…"
	@$(PYTHON) -c "\
import sys; sys.path.insert(0,'.');\
from app.core.config import Config;\
cfg = Config();\
cfg.set_admin_password('');\
print('  ✅  Mot de passe supprimé — accès admin sans restriction au prochain démarrage.')"
	@echo ""

# ── Lancer l'application ─────────────────────────────────────────────────────
run: venv
	DISPLAY=:0 $(PYTHON) $(SRC)

run-admin: venv
	DISPLAY=:0 $(PYTHON) $(SRC) --admin

# ── Nettoyage ────────────────────────────────────────────────────────────────
clean:
	@echo "  🧹  Nettoyage…"
	@rm -rf $(BUILD) $(DIST) *.spec
	@find . -name "__pycache__" -type d -exec rm -rf {} + 2>/dev/null; true
	@find . -name "*.pyc" -delete 2>/dev/null; true
	@echo "  ✅  Artefacts supprimés"

clean-all: clean
	@rm -rf $(VENV)
	@echo "  ✅  Venv supprimé"

# ── Aide ─────────────────────────────────────────────────────────────────────
help:
	@echo ""
	@echo "  ╔══════════════════════════════════════════════╗"
	@echo "  ║  🛡️  WGSecure v$(VERSION) — Makefile             ║"
	@echo "  ╠══════════════════════════════════════════════╣"
	@echo "  ║  make install         Installe les dépendances ║"
	@echo "  ║  make linux           Binaire Linux (onefile)  ║"
	@echo "  ║  make windows         Binaire Windows (Wine)   ║"
	@echo "  ║  make release         Linux + nommage release  ║"
	@echo "  ║  make version         Affiche la version       ║"
	@echo "  ║  make changelog       Dernière entrée changelog║"
	@echo "  ║  make setup-wine      Python Windows dans Wine ║"
	@echo "  ║  make install-gnome   Installe dans GNOME      ║"
	@echo "  ║  make uninstall-gnome Désinstalle de GNOME     ║"
	@echo "  ║  make setup-sudoers   wg-quick sans sudo         ║"
	@echo "  ║  make reset-password  Supprime le MDP admin     ║"
	@echo "  ║  make run             Lance l'application      ║"
	@echo "  ║  make run-admin       Lance en mode admin      ║"
	@echo "  ║  make clean           Supprime les artefacts   ║"
	@echo "  ╚══════════════════════════════════════════════╝"
	@echo ""
