#!/bin/bash
# ───────────────────────────────────────────────────────────────
#  averion-installer — installateur texte d'AVERION Server
#  Lancé automatiquement sur la première console de l'ISO.
# ───────────────────────────────────────────────────────────────
set -uo pipefail

VERSION="1.0"
SQUASH="${AVERION_SQUASH:-/run/live/medium/live/filesystem.squashfs}"
CIBLE="${AVERION_CIBLE:-/mnt/averion}"
PAQUETS=/usr/share/averion/paquets
JOURNAL=/var/log/averion-installer.log
ECHEC=/tmp/averion-installer.echec
FOND="AVERION Server $VERSION  ·  Installation  ·  agorea.fr"
H=20; L=76

export TERM="${TERM:-linux}"
export NEWT_COLORS='
root=white,black
roottext=brightcyan,black
border=cyan,black
window=white,black
shadow=black,black
title=brightcyan,black
button=black,cyan
actbutton=black,brightcyan
compactbutton=white,black
checkbox=white,black
actcheckbox=black,cyan
entry=white,black
disentry=gray,black
label=white,black
listbox=white,black
actlistbox=black,cyan
sellistbox=black,cyan
actsellistbox=black,brightcyan
textbox=white,black
acttextbox=black,cyan
helpline=black,cyan
emptyscale=,gray
fullscale=,cyan
'

# ── Réponses de l'utilisateur ──────────────────────────────────
CLAVIER="fr"; XKB_LAYOUT="fr"; XKB_VARIANT=""
DISQUE=""; DISQUE_INFO=""
MACHINE="averion-server"; NOM_COMPLET=""; UTILISATEUR=""; MOTDEPASSE=""
CLES_SSH=""; SSH_MDP="oui"
INTERFACE=""; RESEAU="dhcp"; ADRESSE=""; PASSERELLE=""; DNS="1.1.1.1 9.9.9.9"
SERVICES=(cockpit)
FUSEAU="Europe/Paris"
UEFI=0; [ -d /sys/firmware/efi ] && UEFI=1

# ── Boîtes de dialogue ─────────────────────────────────────────
wt() { whiptail --backtitle "$FOND" "$@" 3>&1 1>&2 2>&3; }
msg() { wt --title "$1" --msgbox "$2" "$H" "$L"; }
oui_non() { wt --title "$1" --yes-button "Oui" --no-button "Non" --yesno "$2" "$H" "$L"; }
saisie() { wt --title "$1" --ok-button "Suivant" --cancel-button "Retour" --inputbox "$2" 12 "$L" "$3"; }
secret() { wt --title "$1" --ok-button "Suivant" --cancel-button "Retour" --passwordbox "$2" 12 "$L"; }
journal() { echo "[$(date +%T)] $*" >> "$JOURNAL"; }

# ── Utilitaires ────────────────────────────────────────────────
partition() { # partition N du disque (gère /dev/nvme0n1p1, /dev/mmcblk0p1, /dev/sda1)
	if [[ "$DISQUE" =~ [0-9]$ ]]; then echo "${DISQUE}p$1"; else echo "${DISQUE}$1"; fi
}

disque_live() { # disque qui porte l'ISO, à ne jamais proposer
	local src; src=$(findmnt -no SOURCE /run/live/medium 2>/dev/null) || return 0
	lsblk -no PKNAME "$src" 2>/dev/null | head -1 | sed 's|^|/dev/|'
	echo "$src"
}

taille_humaine() { numfmt --to=iec --suffix=o "$1" 2>/dev/null | sed 's/\([0-9]\)\([KMGT]\)/\1 \2/' || echo "$1 o"; }

valide_ipv4_cidr() {
	[[ "$1" =~ ^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/([0-9]{1,2})$ ]] || return 1
	local i; for i in 1 2 3 4; do (( BASH_REMATCH[i] <= 255 )) || return 1; done
	(( BASH_REMATCH[5] >= 1 && BASH_REMATCH[5] <= 32 ))
}
valide_ipv4() { valide_ipv4_cidr "$1/24"; }

# ═══════════════════════════════════════════════════════════════
#  Étapes (chacune renvoie 0 pour « suivant », 1 pour « retour »)
# ═══════════════════════════════════════════════════════════════

etape_clavier() {
	local choix
	choix=$(wt --title "Clavier" --ok-button "Suivant" --cancel-button "Retour" --default-item "$CLAVIER" \
		--menu "Choisissez la disposition du clavier :" "$H" "$L" 7 \
		fr "Français (AZERTY)" \
		be "Belge (AZERTY)" \
		ch "Suisse romand (QWERTZ)" \
		ca "Canadien français" \
		us "Anglais américain (QWERTY)" \
		gb "Anglais britannique" \
		de "Allemand (QWERTZ)") || return 1
	CLAVIER="$choix"
	case "$choix" in
		fr) XKB_LAYOUT=fr; XKB_VARIANT=""; loadkeys fr >/dev/null 2>&1 ;;
		be) XKB_LAYOUT=be; XKB_VARIANT=""; loadkeys be-latin1 >/dev/null 2>&1 ;;
		ch) XKB_LAYOUT=ch; XKB_VARIANT=fr; loadkeys fr_CH >/dev/null 2>&1 ;;
		ca) XKB_LAYOUT=ca; XKB_VARIANT=fr; loadkeys cf >/dev/null 2>&1 ;;
		us) XKB_LAYOUT=us; XKB_VARIANT=""; loadkeys us >/dev/null 2>&1 ;;
		gb) XKB_LAYOUT=gb; XKB_VARIANT=""; loadkeys uk >/dev/null 2>&1 ;;
		de) XKB_LAYOUT=de; XKB_VARIANT=""; loadkeys de >/dev/null 2>&1 ;;
	esac
	return 0
}

etape_disque() {
	local exclus lignes=() nom taille modele octets type choix
	exclus=$(disque_live)
	while read -r nom octets type modele; do
		[ "$type" = disk ] || continue
		grep -qxF "$nom" <<<"$exclus" && continue
		(( octets < 8 * 1024 * 1024 * 1024 )) && continue
		taille=$(taille_humaine "$octets")
		lignes+=("$nom" "$(printf '%-9s %s' "$taille" "${modele:-disque}")")
	done < <(lsblk -dbnpo NAME,SIZE,TYPE,MODEL 2>/dev/null)

	if (( ${#lignes[@]} == 0 )); then
		msg "Aucun disque" "Aucun disque d'au moins 8 Go n'a été trouvé.\n\nBranchez ou ajoutez un disque, puis relancez l'installation."
		return 1
	fi
	choix=$(wt --title "Disque d'installation" --ok-button "Suivant" --cancel-button "Retour" \
		--menu "Sur quel disque installer AVERION Server ?\n\nLe disque choisi sera ENTIÈREMENT effacé." \
		"$H" "$L" 8 "${lignes[@]}") || return 1
	DISQUE="$choix"
	DISQUE_INFO=$(lsblk -dno SIZE,MODEL "$DISQUE" | sed 's/  */ /g')
	oui_non "Confirmation" "ATTENTION : toutes les données de\n\n    $DISQUE  ($DISQUE_INFO)\n\nseront définitivement effacées.\n\nContinuer avec ce disque ?" || return 1
	return 0
}

etape_identite() {
	local v v2
	while :; do
		v=$(saisie "Nom du serveur" "Nom de la machine sur le réseau\n(minuscules, chiffres et tirets) :" "$MACHINE") || return 1
		if [[ "$v" =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ ]]; then MACHINE="$v"; break; fi
		msg "Nom invalide" "« $v » n'est pas un nom de machine valide.\nExemples : srv-web, averion01, cloud"
	done
	v=$(saisie "Administrateur" "Nom complet de l'administrateur :" "$NOM_COMPLET") || return 1
	NOM_COMPLET="${v//:/}"
	while :; do
		v=$(saisie "Administrateur" "Identifiant de connexion (minuscules, sans espace) :" "$UTILISATEUR") || return 1
		if [[ "$v" =~ ^[a-z_][a-z0-9_-]{0,31}$ ]] && ! [[ "$v" =~ ^(root|admin|daemon|bin|sys|www-data|nobody|averion-ftp|postgres|mysql)$ ]]; then
			UTILISATEUR="$v"; break
		fi
		msg "Identifiant invalide" "« $v » n'est pas utilisable.\nUtilisez des minuscules, chiffres, - ou _, sans nom réservé (root, admin…)."
	done
	while :; do
		v=$(secret "Mot de passe" "Mot de passe de $UTILISATEUR (8 caractères minimum) :") || return 1
		v2=$(secret "Mot de passe" "Confirmez le mot de passe :") || return 1
		if [ "$v" != "$v2" ]; then msg "Mot de passe" "Les deux mots de passe sont différents."; continue; fi
		if [ "${#v}" -lt 8 ]; then msg "Mot de passe" "Le mot de passe doit contenir au moins 8 caractères."; continue; fi
		MOTDEPASSE="$v"; break
	done
	return 0
}

etape_ssh() {
	local choix v cles
	choix=$(wt --title "Accès SSH" --ok-button "Suivant" --cancel-button "Retour" \
		--menu "Le serveur sera accessible en SSH (port 22).\nComment $UTILISATEUR va-t-il se connecter ?" "$H" "$L" 3 \
		mdp "Avec son mot de passe (simple)" \
		cle "En collant une clé publique SSH (recommandé)" \
		github "En important ses clés depuis GitHub") || return 1
	CLES_SSH=""; SSH_MDP="oui"
	case "$choix" in
		cle)
			v=$(saisie "Clé publique" "Collez la clé publique (ssh-ed25519 AAAA… ou ssh-rsa AAAA…) :" "") || return 1
			if ! [[ "$v" =~ ^(ssh-(ed25519|rsa)|ecdsa-sha2-nistp(256|384|521)|sk-ssh-ed25519@openssh.com)[[:space:]] ]]; then
				msg "Clé invalide" "Cette clé n'est pas reconnue. Elle doit commencer par ssh-ed25519, ssh-rsa ou ecdsa-…"
				return 2
			fi
			CLES_SSH="$v" ;;
		github)
			v=$(saisie "GitHub" "Nom d'utilisateur GitHub :" "") || return 1
			cles=$(curl -fsS --max-time 15 "https://github.com/${v}.keys" 2>>"$JOURNAL")
			if [ -z "$cles" ]; then
				msg "GitHub" "Aucune clé récupérée pour « $v ».\nVérifiez le nom et la connexion Internet."
				return 2
			fi
			CLES_SSH="$cles"
			msg "GitHub" "$(wc -l <<<"$cles") clé(s) importée(s) depuis github.com/$v." ;;
	esac
	if [ -n "$CLES_SSH" ]; then
		if oui_non "Accès SSH" "Désactiver la connexion SSH par mot de passe ?\n\nC'est bien plus sûr : seules les clés fournies pourront se connecter.\n(La console locale accepte toujours le mot de passe.)"; then
			SSH_MDP="non"
		fi
	fi
	return 0
}

etape_reseau() {
	local ifs=() i ip gw choix v
	for i in /sys/class/net/*; do
		i=${i##*/}
		[ "$i" = lo ] && continue
		[ -e "/sys/class/net/$i/device" ] || continue
		[ -d "/sys/class/net/$i/wireless" ] && continue
		ip=$(ip -4 -o addr show dev "$i" 2>/dev/null | awk '{print $4}' | head -1)
		ifs+=("$i" "${ip:-sans adresse}")
	done
	if (( ${#ifs[@]} == 0 )); then
		msg "Réseau" "Aucune carte réseau filaire détectée.\nLe réseau pourra être configuré après l'installation (nmtui ou Cockpit)."
		INTERFACE=""; RESEAU="dhcp"; return 0
	fi
	if (( ${#ifs[@]} == 2 )); then
		INTERFACE="${ifs[0]}"
	else
		INTERFACE=$(wt --title "Réseau" --ok-button "Suivant" --cancel-button "Retour" \
			--menu "Carte réseau principale :" "$H" "$L" 6 "${ifs[@]}") || return 1
	fi
	ip=$(ip -4 -o addr show dev "$INTERFACE" 2>/dev/null | awk '{print $4}' | head -1)
	gw=$(ip -4 route show default dev "$INTERFACE" 2>/dev/null | awk '{print $3}' | head -1)
	choix=$(wt --title "Réseau — $INTERFACE" --ok-button "Suivant" --cancel-button "Retour" --default-item "$RESEAU" \
		--menu "Adresse actuelle : ${ip:-aucune}\n\nComment configurer le réseau ?" "$H" "$L" 2 \
		dhcp "Automatique (DHCP)" \
		fixe "Adresse IP fixe (recommandé pour un serveur)") || return 1
	RESEAU="$choix"
	[ "$RESEAU" = dhcp ] && return 0
	while :; do
		v=$(saisie "Adresse IP fixe" "Adresse IP et masque (ex. 192.168.1.20/24) :" "${ADRESSE:-${ip:-192.168.1.20/24}}") || return 1
		valide_ipv4_cidr "$v" && { ADRESSE="$v"; break; }
		msg "Adresse invalide" "Format attendu : 192.168.1.20/24"
	done
	while :; do
		v=$(saisie "Passerelle" "Adresse de la passerelle (box / routeur) :" "${PASSERELLE:-$gw}") || return 1
		valide_ipv4 "$v" && { PASSERELLE="$v"; break; }
		msg "Adresse invalide" "Format attendu : 192.168.1.1"
	done
	while :; do
		v=$(saisie "Serveurs DNS" "Serveurs DNS séparés par des espaces :" "$DNS") || return 1
		local ok=1 d; for d in $v; do valide_ipv4 "$d" || ok=0; done
		[ -n "$v" ] && (( ok )) && { DNS="$v"; break; }
		msg "DNS invalides" "Exemple : 1.1.1.1 9.9.9.9"
	done
	return 0
}

etape_services() {
	local choix s
	etat() { local x; for x in "${SERVICES[@]}"; do [ "$x" = "$1" ] && { echo ON; return; }; done; echo OFF; }
	while :; do
		choix=$(wt --title "Services" --ok-button "Suivant" --cancel-button "Retour" --separate-output \
			--checklist "Services à activer dès le premier démarrage (Espace pour cocher).\nToujours actifs : SSH, pare-feu, anti-intrusion, correctifs auto.\nTout reste activable plus tard avec : sudo averion activer <service>" \
			"$H" "$L" 10 \
			cockpit    "Console web d'administration (port 9090)" "$(etat cockpit)" \
			nginx      "Serveur web Nginx"                        "$(etat nginx)" \
			apache     "Serveur web Apache"                       "$(etat apache)" \
			php        "PHP-FPM (pour Nginx ou Apache)"           "$(etat php)" \
			mariadb    "Base de données MariaDB (MySQL)"          "$(etat mariadb)" \
			postgresql "Base de données PostgreSQL"               "$(etat postgresql)" \
			redis      "Cache Redis"                              "$(etat redis)" \
			ftp        "Serveur FTP / FTPS (vsftpd)"              "$(etat ftp)" \
			docker     "Docker et Docker Compose"                 "$(etat docker)" \
			samba      "Partage de fichiers Windows (Samba)"      "$(etat samba)") || return 1
		SERVICES=()
		while read -r s; do [ -n "$s" ] && SERVICES+=("$s"); done <<<"$choix"
		if [ "$(etat nginx)" = ON ] && [ "$(etat apache)" = ON ]; then
			msg "Conflit" "Nginx et Apache utilisent tous les deux le port 80.\nChoisissez l'un ou l'autre."
			continue
		fi
		return 0
	done
}

etape_fuseau() {
	local choix v
	choix=$(wt --title "Fuseau horaire" --ok-button "Suivant" --cancel-button "Retour" --default-item "$FUSEAU" \
		--menu "Fuseau horaire du serveur :" "$H" "$L" 8 \
		Europe/Paris "France" \
		Europe/Brussels "Belgique" \
		Europe/Zurich "Suisse" \
		Europe/Luxembourg "Luxembourg" \
		America/Montreal "Québec" \
		UTC "Temps universel (UTC)" \
		autre "Autre…") || return 1
	if [ "$choix" = autre ]; then
		while :; do
			v=$(saisie "Fuseau horaire" "Fuseau au format Région/Ville (ex. Africa/Casablanca) :" "$FUSEAU") || return 1
			[ -f "/usr/share/zoneinfo/$v" ] && { FUSEAU="$v"; break; }
			msg "Fuseau inconnu" "« $v » n'existe pas."
		done
	else
		FUSEAU="$choix"
	fi
	return 0
}

etape_resume() {
	local reseau acces mode
	if [ "$RESEAU" = fixe ]; then reseau="$INTERFACE : $ADRESSE via $PASSERELLE
  DNS           $DNS"
	else reseau="${INTERFACE:-automatique} : DHCP"; fi
	if [ -z "$CLES_SSH" ]; then acces="mot de passe"
	elif [ "$SSH_MDP" = non ]; then acces="clé(s) SSH uniquement"
	else acces="clé(s) SSH + mot de passe"; fi
	mode=$([ "$UEFI" = 1 ] && echo "UEFI" || echo "BIOS")
	wt --title "Récapitulatif" --yes-button "Installer" --no-button "Retour" --yesno \
"Vérifiez avant de lancer l'installation :

  Disque        $DISQUE ($DISQUE_INFO) — sera effacé
  Démarrage     $mode
  Serveur       $MACHINE
  Admin         $UTILISATEUR ($NOM_COMPLET)
  Accès SSH     $acces
  Réseau        $reseau
  Clavier       $CLAVIER    Fuseau : $FUSEAU
  Services      ${SERVICES[*]:-aucun en plus}" 20 "$L" || return 1
	return 0
}

# ═══════════════════════════════════════════════════════════════
#  Installation
# ═══════════════════════════════════════════════════════════════

progres() { printf 'XXX\n%d\n%s\nXXX\n' "$1" "$2"; journal "== $2"; }
echec() { echo "$1" > "$ECHEC"; journal "ERREUR : $1"; exit 1; }
cmd() { journal "\$ $*"; "$@" >>"$JOURNAL" 2>&1; }
dans() { chroot "$CIBLE" /usr/bin/env -i HOME=/root TERM=linux LANG=C.UTF-8 \
	PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin DEBIAN_FRONTEND=noninteractive "$@"; }
dans_cmd() { journal "(cible) \$ $*"; dans "$@" >>"$JOURNAL" 2>&1; }

liberer_disque() {
	local p
	swapoff -a 2>/dev/null
	for p in $(lsblk -lnpo NAME "$DISQUE" | tac); do
		umount -lf "$p" 2>/dev/null
	done
	vgchange -an >/dev/null 2>&1 || true
	mdadm --stop --scan >/dev/null 2>&1 || true
	umount -R "$CIBLE" 2>/dev/null || true
}

monter_systemes() {
	mount -t proc proc "$CIBLE/proc" && \
	mount --rbind /sys "$CIBLE/sys" && mount --make-rslave "$CIBLE/sys" && \
	mount --rbind /dev "$CIBLE/dev" && mount --make-rslave "$CIBLE/dev" && \
	mount --bind /run "$CIBLE/run"
}

installer() {
	local racine efi uuid_racine uuid_efi p s

	# 1. Partitionnement ────────────────────────────────────────
	progres 2 "Préparation du disque $DISQUE…"
	liberer_disque
	cmd wipefs -af "$DISQUE" || echec "Impossible d'effacer $DISQUE."
	cmd sgdisk --zap-all "$DISQUE" || echec "Impossible de réinitialiser la table de partitions."
	if [ "$UEFI" = 1 ]; then
		cmd sgdisk -n1:0:+512M -t1:ef00 -c1:EFI -n2:0:0 -t2:8304 -c2:averion-racine "$DISQUE" \
			|| echec "Échec du partitionnement (UEFI)."
	else
		cmd sgdisk -n1:0:+1M -t1:ef02 -c1:bios-grub -n2:0:0 -t2:8304 -c2:averion-racine "$DISQUE" \
			|| echec "Échec du partitionnement (BIOS)."
	fi
	cmd partprobe "$DISQUE"; udevadm settle; sleep 1
	efi=$(partition 1); racine=$(partition 2)

	progres 5 "Formatage des partitions…"
	cmd mkfs.ext4 -F -L averion-racine "$racine" || echec "Échec du formatage de $racine."
	[ "$UEFI" = 1 ] && { cmd mkfs.vfat -F 32 -n EFI "$efi" || echec "Échec du formatage de $efi."; }
	mkdir -p "$CIBLE"
	cmd mount "$racine" "$CIBLE" || echec "Impossible de monter $racine."
	if [ "$UEFI" = 1 ]; then
		mkdir -p "$CIBLE/boot/efi"
		cmd mount "$efi" "$CIBLE/boot/efi" || echec "Impossible de monter la partition EFI."
	fi

	# 2. Copie du système ───────────────────────────────────────
	progres 8 "Copie d'AVERION Server sur le disque…"
	[ -f "$SQUASH" ] || echec "Image du système introuvable ($SQUASH)."
	unsquashfs -f -percentage -d "$CIBLE" "$SQUASH" 2>>"$JOURNAL" | while read -r p; do
		[[ "$p" =~ ^[0-9]+$ ]] && progres $(( 8 + p * 62 / 100 )) "Copie d'AVERION Server sur le disque… $p %"
	done
	[ "${PIPESTATUS[0]}" -eq 0 ] || echec "La copie du système a échoué (voir le journal)."
	[ -x "$CIBLE/usr/bin/env" ] || echec "La copie du système est incomplète."

	[ -n "${AVERION_SANS_CHROOT:-}" ] && { progres 100 "Test terminé (configuration ignorée)."; return 0; }

	# 3. Configuration ──────────────────────────────────────────
	progres 72 "Configuration du système…"
	monter_systemes || echec "Impossible de préparer l'environnement du système installé."

	uuid_racine=$(blkid -s UUID -o value "$racine")
	{
		echo "# /etc/fstab — AVERION Server"
		echo "UUID=$uuid_racine  /          ext4  errors=remount-ro  0  1"
		if [ "$UEFI" = 1 ]; then
			uuid_efi=$(blkid -s UUID -o value "$efi")
			echo "UUID=$uuid_efi  /boot/efi  vfat  umask=0077  0  1"
		fi
		echo "/swapfile  none  swap  sw  0  0"
	} > "$CIBLE/etc/fstab"

	cmd fallocate -l 2G "$CIBLE/swapfile" && chmod 600 "$CIBLE/swapfile" && cmd mkswap "$CIBLE/swapfile" \
		|| journal "(info) fichier d'échange non créé"

	echo "$MACHINE" > "$CIBLE/etc/hostname"
	cat > "$CIBLE/etc/hosts" <<EOF
127.0.0.1	localhost
127.0.1.1	$MACHINE

::1	localhost ip6-localhost ip6-loopback
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
EOF

	cat > "$CIBLE/etc/default/keyboard" <<EOF
XKBMODEL="pc105"
XKBLAYOUT="$XKB_LAYOUT"
XKBVARIANT="$XKB_VARIANT"
XKBOPTIONS=""
BACKSPACE="guess"
EOF
	ln -sf "/usr/share/zoneinfo/$FUSEAU" "$CIBLE/etc/localtime"
	echo "$FUSEAU" > "$CIBLE/etc/timezone"
	echo 'LANG=fr_FR.UTF-8' > "$CIBLE/etc/default/locale"

	progres 76 "Création du compte $UTILISATEUR…"
	dans_cmd useradd -m -s /bin/bash -c "$NOM_COMPLET" -G sudo,adm,systemd-journal "$UTILISATEUR" \
		|| echec "Impossible de créer le compte $UTILISATEUR."
	printf '%s:%s\n' "$UTILISATEUR" "$MOTDEPASSE" | dans chpasswd 2>>"$JOURNAL" \
		|| echec "Impossible de définir le mot de passe."
	dans_cmd passwd -l root
	if [ -n "$CLES_SSH" ]; then
		install -d -m 700 "$CIBLE/home/$UTILISATEUR/.ssh"
		printf '%s\n' "$CLES_SSH" > "$CIBLE/home/$UTILISATEUR/.ssh/authorized_keys"
		chmod 600 "$CIBLE/home/$UTILISATEUR/.ssh/authorized_keys"
		dans_cmd chown -R "$UTILISATEUR:$UTILISATEUR" "/home/$UTILISATEUR/.ssh"
	fi
	if [ "$SSH_MDP" = non ]; then
		printf '# Créé par l installateur : connexion SSH par clé uniquement\nPasswordAuthentication no\nKbdInteractiveAuthentication no\n' \
			> "$CIBLE/etc/ssh/sshd_config.d/20-averion-cles.conf"
	fi
	rm -f "$CIBLE"/etc/ssh/ssh_host_*
	dans_cmd ssh-keygen -A
	: > "$CIBLE/etc/machine-id"
	rm -f "$CIBLE/var/lib/dbus/machine-id"

	progres 80 "Configuration du réseau…"
	if [ "$RESEAU" = fixe ] && [ -n "$INTERFACE" ]; then
		local dns_nm; dns_nm="$(tr ' ' ';' <<<"$DNS");"
		install -d -m 755 "$CIBLE/etc/NetworkManager/system-connections"
		cat > "$CIBLE/etc/NetworkManager/system-connections/averion-$INTERFACE.nmconnection" <<EOF
[connection]
id=averion-$INTERFACE
type=ethernet
interface-name=$INTERFACE
autoconnect=true

[ipv4]
method=manual
address1=$ADRESSE,$PASSERELLE
dns=$dns_nm

[ipv6]
method=auto
EOF
		chmod 600 "$CIBLE/etc/NetworkManager/system-connections/averion-$INTERFACE.nmconnection"
	fi

	progres 83 "Activation des services…"
	for s in "${SERVICES[@]}"; do
		journal "(cible) averion activer $s"
		dans env AVERION_HORS_LIGNE=1 averion activer "$s" >>"$JOURNAL" 2>&1 || journal "(info) activation de $s incomplète"
	done
	if printf '%s\n' "${SERVICES[@]}" | grep -qx docker; then
		dans_cmd usermod -aG docker "$UTILISATEUR"
	fi

	# 4. Retrait des éléments propres à la session live ─────────
	progres 86 "Retrait des outils de la session live…"
	local live=()
	for p in live-boot live-boot-initramfs-tools live-config live-config-systemd live-tools; do
		dans dpkg -s "$p" >/dev/null 2>&1 && live+=("$p")
	done
	(( ${#live[@]} )) && dans_cmd apt-get purge -y "${live[@]}"
	rm -rf "$CIBLE/etc/systemd/system/getty@tty1.service.d/zz-averion-installateur.conf" \
		"$CIBLE/etc/profile.d/averion-live.sh" "$CIBLE/usr/local/sbin/averion-installer"
	rmdir "$CIBLE/etc/systemd/system/getty@tty1.service.d" 2>/dev/null || true

	# 5. Démarrage (GRUB) ───────────────────────────────────────
	progres 90 "Installation du chargeur de démarrage…"
	mkdir -p "$CIBLE/tmp/averion-grub"
	if [ "$UEFI" = 1 ]; then
		cp "$PAQUETS"/uefi/*.deb "$CIBLE/tmp/averion-grub/" 2>>"$JOURNAL"
		echo "grub-efi-amd64 grub2/force_efi_extra_removable boolean true" | dans debconf-set-selections
		dans_cmd sh -c 'dpkg -i /tmp/averion-grub/*.deb' || { journal "(info) paquet grub-efi non posé"; dans_cmd dpkg --configure -a; }
		dans_cmd grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=averion --recheck \
			|| echec "L'installation de GRUB (UEFI) a échoué."
		dans_cmd grub-install --target=x86_64-efi --efi-directory=/boot/efi --removable --recheck \
			|| journal "(info) copie de secours EFI/BOOT non faite"
	else
		cp "$PAQUETS"/bios/*.deb "$CIBLE/tmp/averion-grub/" 2>>"$JOURNAL"
		echo "grub-pc grub-pc/install_devices multiselect $DISQUE" | dans debconf-set-selections
		dans_cmd sh -c 'dpkg -i /tmp/averion-grub/*.deb' || { journal "(info) paquet grub-pc non posé"; dans_cmd dpkg --configure -a; }
		dans_cmd grub-install --target=i386-pc --recheck "$DISQUE" || echec "L'installation de GRUB (BIOS) a échoué."
	fi
	rm -rf "$CIBLE/tmp/averion-grub" "$CIBLE/usr/share/averion/paquets"

	progres 95 "Finalisation…"
	dans_cmd update-initramfs -u -k all || echec "Impossible de régénérer l'initramfs."
	dans_cmd update-grub || echec "Impossible de générer le menu de démarrage."

	cp "$JOURNAL" "$CIBLE/var/log/averion-installer.log" 2>/dev/null
	sync
	umount -R "$CIBLE" 2>>"$JOURNAL" || umount -lR "$CIBLE" 2>/dev/null
	progres 100 "Installation terminée."
	sleep 1
}

# ═══════════════════════════════════════════════════════════════
#  Déroulé
# ═══════════════════════════════════════════════════════════════

lancer_installation() {
	local etapes=(etape_clavier etape_disque etape_identite etape_ssh etape_reseau etape_services etape_fuseau etape_resume)
	local i=0 r
	while (( i < ${#etapes[@]} )); do
		"${etapes[$i]}"; r=$?
		case $r in
			0) i=$((i + 1)) ;;
			1) (( i == 0 )) && return 1; i=$((i - 1)) ;;
			*) ;; # 2 = recommencer la même étape
		esac
	done

	rm -f "$ECHEC"
	: > "$JOURNAL"
	journal "Installation d'AVERION Server $VERSION sur $DISQUE (UEFI=$UEFI)"
	( installer ) | whiptail --backtitle "$FOND" --title "Installation en cours" --gauge "Démarrage…" 8 "$L" 0

	if [ -f "$ECHEC" ]; then
		umount -R "$CIBLE" 2>/dev/null || umount -lR "$CIBLE" 2>/dev/null
		msg "Échec de l'installation" "$(cat "$ECHEC")\n\nLe détail est dans $JOURNAL\n(menu principal → Ouvrir un terminal, puis : less $JOURNAL)"
		return 1
	fi

	local ip url
	if [ "$RESEAU" = fixe ]; then ip="${ADRESSE%/*}"
	else ip=$(ip -4 -o addr show dev "${INTERFACE:-lo}" 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | head -1); fi
	url=""
	printf '%s\n' "${SERVICES[@]}" | grep -qx cockpit && url="\n  Console web :  https://${ip:-<adresse-du-serveur>}:9090"
	msg "AVERION Server est installé" "L'installation est terminée.

La machine va s'éteindre. Retirez ensuite le support d'installation
(clé USB, ISO), puis rallumez-la.

  Connexion SSH :  ssh $UTILISATEUR@${ip:-<adresse-du-serveur>}$url
  Administration : sudo averion   (menu)   ·   averion aide

Bienvenue sur AVERION Server."
	# Pas d'éjection ici : le système live tourne encore depuis ce support jusqu'à l'arrêt complet.
	if oui_non "Éteindre" "Éteindre maintenant pour retirer le support d'installation ?"; then
		systemctl poweroff
		exit 0
	fi
	return 0
}

[ "$(id -u)" -eq 0 ] || { echo "averion-installer doit être lancé en root."; exit 1; }
setterm --blank 0 --powersave off 2>/dev/null || true

while :; do
	choix=$(wt --title "Bienvenue" --ok-button "Valider" --cancel-button "Éteindre" --menu \
"Bienvenue dans l'installation d'AVERION Server $VERSION.

Système serveur prêt pour le web, le FTP, les bases de données
et Docker, avec une console web et l'outil « averion ».

Mode de démarrage détecté : $([ "$UEFI" = 1 ] && echo UEFI || echo BIOS)" "$H" "$L" 4 \
		installer "Installer AVERION Server" \
		terminal  "Ouvrir un terminal (mode secours)" \
		redemarrer "Redémarrer" \
		eteindre  "Éteindre") || choix=eteindre
	case "$choix" in
		installer) lancer_installation ;;
		terminal)
			clear
			echo -e "\e[1;36mMode secours AVERION Server\e[0m — tapez « exit » pour revenir à l'installateur.\n"
			AVERION_SHELL=1 bash -i
			;;
		redemarrer) systemctl reboot; exit 0 ;;
		eteindre) oui_non "Éteindre" "Éteindre la machine ?" && { systemctl poweroff; exit 0; } ;;
	esac
done
