#!/bin/bash
# ───────────────────────────────────────────────────────────────
#  averion — outil d'administration d'AVERION Server
#  « averion » seul ouvre le menu ; « averion aide » liste les commandes.
# ───────────────────────────────────────────────────────────────
set -uo pipefail
export LC_ALL=C.UTF-8   # longueurs en caractères (alignement des accents)

VERSION="1.0"
SAUVEGARDES=/var/backups/averion
IDENTIFIANTS=/root/averion-identifiants
CACHE_MAJ=/var/cache/averion/maj

if [ -z "${NO_COLOR:-}" ]; then
	B=$'\e[1m'; D=$'\e[2m'; R=$'\e[0m'
	CY=$'\e[36m'; BCY=$'\e[1;36m'; VE=$'\e[32m'; JA=$'\e[33m'; RO=$'\e[31m'; GR=$'\e[90m'
else
	B=""; D=""; R=""; CY=""; BCY=""; VE=""; JA=""; RO=""; GR=""
fi

ok()     { echo "${VE}✔${R} $*"; }
info()   { echo "${CY}▶${R} $*"; }
alerte() { echo "${JA}!${R} $*"; }
erreur() { echo "${RO}✖ $*${R}" >&2; exit 1; }

exiger_root() {
	[ "$(id -u)" -eq 0 ] && return 0
	command -v sudo >/dev/null && exec sudo "$0" "${ARGS_ORIGINE[@]}"
	erreur "Cette commande doit être lancée en administrateur : sudo averion …"
}

# ═══════════════════════════════════════════════════════════════
#  Catalogue des services
# ═══════════════════════════════════════════════════════════════
SERVICES_LISTE=(ssh pare-feu fail2ban cockpit nginx apache php mariadb postgresql redis ftp docker samba)

php_unite() {
	local f; f=$(ls /usr/lib/systemd/system/php*-fpm.service 2>/dev/null | sort -V | tail -1)
	[ -n "$f" ] && basename "$f" .service
}
php_version() { php_unite | sed -n 's/^php\(.*\)-fpm$/\1/p'; }
php_socket() { echo "/run/php/php$(php_version)-fpm.sock"; }

libelle() {
	case "$1" in
		ssh) echo "SSH" ;; pare-feu) echo "Pare-feu (ufw)" ;; fail2ban) echo "Anti-intrusion" ;;
		cockpit) echo "Console web" ;; nginx) echo "Nginx" ;; apache) echo "Apache" ;;
		php) echo "PHP $(php_version)" ;; mariadb) echo "MariaDB" ;; postgresql) echo "PostgreSQL" ;;
		redis) echo "Redis" ;; ftp) echo "FTP (vsftpd)" ;; docker) echo "Docker" ;; samba) echo "Samba" ;;
		*) return 1 ;;
	esac
}

unites() {
	case "$1" in
		ssh) echo "ssh" ;; pare-feu) echo "ufw" ;; fail2ban) echo "fail2ban" ;;
		cockpit) echo "cockpit.socket" ;; nginx) echo "nginx" ;; apache) echo "apache2" ;;
		php) php_unite ;; mariadb) echo "mariadb" ;; postgresql) echo "postgresql" ;;
		redis) echo "redis-server" ;; ftp) echo "vsftpd" ;;
		docker) echo "containerd docker.socket docker" ;; samba) echo "smbd nmbd" ;;
		*) return 1 ;;
	esac
}

ports() {
	case "$1" in
		cockpit) echo "9090/tcp" ;;
		nginx|apache) echo "80/tcp 443/tcp" ;;
		ftp) echo "21/tcp 40000:40100/tcp" ;;
		samba) echo "139/tcp 445/tcp" ;;
		*) echo "" ;;
	esac
}

unite_principale() { unites "$1" | awk '{print $NF}'; }
est_active()  { systemctl is-active --quiet "$(unite_principale "$1")" 2>/dev/null; }
est_activee() { systemctl is-enabled --quiet "$(unite_principale "$1")" 2>/dev/null; }

verifier_service() {
	libelle "$1" >/dev/null 2>&1 || erreur "Service inconnu : « $1 ». Services : ${SERVICES_LISTE[*]}"
	[ -n "$(unites "$1")" ] || erreur "Le service « $1 » n'est pas installé sur ce serveur."
}

ufw_regle() { # ufw_regle allow|delete <port>
	local action=$1 port=$2
	if [ -n "${AVERION_HORS_LIGNE:-}" ]; then
		sed -i 's/^ENABLED=.*/ENABLED=no/' /etc/ufw/ufw.conf
		if [ "$action" = allow ]; then ufw allow "$port" >/dev/null; else ufw delete allow "$port" >/dev/null; fi
		sed -i 's/^ENABLED=.*/ENABLED=yes/' /etc/ufw/ufw.conf
	elif [ "$action" = allow ]; then ufw allow "$port" >/dev/null
	else ufw delete allow "$port" >/dev/null 2>&1 || true
	fi
}

cmd_activer() {
	exiger_root
	local svc=${1:-} u p
	[ -n "$svc" ] || erreur "Usage : averion activer <service>   (services : ${SERVICES_LISTE[*]})"
	verifier_service "$svc"
	case "$svc" in
		nginx)  est_activee apache && erreur "Apache est activé et utilise déjà le port 80. Lancez d'abord : averion desactiver apache" ;;
		apache) est_activee nginx && erreur "Nginx est activé et utilise déjà le port 80. Lancez d'abord : averion desactiver nginx" ;;
	esac
	for u in $(unites "$svc"); do
		if [ -n "${AVERION_HORS_LIGNE:-}" ]; then systemctl enable "$u" >/dev/null 2>&1
		else systemctl enable --now "$u" >/dev/null 2>&1 || erreur "Impossible de démarrer $u (journalctl -u $u)"
		fi
	done
	for p in $(ports "$svc"); do ufw_regle allow "$p"; done
	[ "$svc" = ftp ] && [ -f /etc/fail2ban/jail.d/averion.conf ] && \
		sed -i '/^\[vsftpd\]/,/^\[/ s/^enabled = .*/enabled = true/' /etc/fail2ban/jail.d/averion.conf
	if [ -z "${AVERION_HORS_LIGNE:-}" ] && [ "$svc" = ftp ]; then systemctl try-reload-or-restart fail2ban 2>/dev/null; fi
	ok "$(libelle "$svc") activé$( [ -n "$(ports "$svc")" ] && echo " (ports ouverts : $(ports "$svc"))")"
	case "$svc" in
		cockpit) info "Console web : https://$(adresse_ip):9090  (identifiants de votre compte)" ;;
		nginx|apache) info "Créez un site avec : sudo averion site creer mon-domaine.fr" ;;
		mariadb|postgresql) info "Créez une base avec : sudo averion bdd creer ma_base" ;;
		ftp) info "Ajoutez un compte avec : sudo averion ftp ajouter nom_du_compte" ;;
		docker) info "Ajoutez un utilisateur au groupe docker : sudo usermod -aG docker <identifiant>" ;;
	esac
}

cmd_desactiver() {
	exiger_root
	local svc=${1:-} u p
	[ -n "$svc" ] || erreur "Usage : averion desactiver <service>"
	verifier_service "$svc"
	if [ "$svc" = ssh ] || [ "$svc" = pare-feu ]; then
		read -rp "${JA}Désactiver $(libelle "$svc") peut vous couper l'accès ou exposer le serveur. Confirmer ? [o/N] ${R}" rep
		[[ "$rep" =~ ^[oO] ]] || exit 0
	fi
	for u in $(unites "$svc" | tr ' ' '\n' | tac); do systemctl disable --now "$u" >/dev/null 2>&1; done
	for p in $(ports "$svc"); do ufw_regle delete "$p"; done
	ok "$(libelle "$svc") désactivé"
}

cmd_services() {
	local s etat
	echo "${B}Services d'AVERION Server${R}"
	for s in "${SERVICES_LISTE[@]}"; do
		[ -n "$(unites "$s")" ] || continue
		if est_active "$s"; then etat="${VE}● actif${R}"
		elif est_activee "$s"; then etat="${JA}● activé, arrêté${R}"
		else etat="${GR}○ désactivé${R}"; fi
		printf "  %-12s %-20s %s\n" "$s" "$(libelle "$s")" "$etat"
	done
	echo
	echo "${D}Activer : sudo averion activer <service>   ·   Désactiver : sudo averion desactiver <service>${R}"
}

# ═══════════════════════════════════════════════════════════════
#  Tableau de bord
# ═══════════════════════════════════════════════════════════════
ligne() { # ligne <libellé> <valeur…> — aligné par caractères
	local k=$1; shift
	printf '  %s%*s %s\n' "$k" $(( 13 - ${#k} )) "" "$*"
}

adresse_ip() { ip -4 -o addr show scope global 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | head -1; }

barre() { # barre <pourcentage>
	local p=$1 n i s="" c=$VE
	(( p >= 75 )) && c=$JA; (( p >= 90 )) && c=$RO
	n=$(( p / 5 ))
	for ((i = 0; i < 20; i++)); do if (( i < n )); then s+="█"; else s+="░"; fi; done
	printf '%s%s%s' "$c" "$s" "$R"
}

go() { awk -v o="$1" 'BEGIN { if (o >= 1073741824) printf "%.1f Go", o/1073741824; else printf "%d Mo", o/1048576 }'; }

duree_service() {
	local s j h m
	s=$(cut -d. -f1 /proc/uptime); j=$(( s / 86400 )); h=$(( s % 86400 / 3600 )); m=$(( s % 3600 / 60 ))
	if (( j > 0 )); then echo "$j j $h h"; elif (( h > 0 )); then echo "$h h $m min"; else echo "$m min"; fi
}

mises_a_jour() { # « total securite », mis en cache une heure
	local f="$CACHE_MAJ"
	if [ -f "$f" ] && [ $(( $(date +%s) - $(stat -c %Y "$f") )) -lt 3600 ]; then cat "$f"; return; fi
	local sortie total secu
	sortie=$(apt-get -s -o Debug::NoLocking=1 dist-upgrade 2>/dev/null | grep '^Inst ')
	total=$(grep -c . <<<"$sortie"); secu=$(grep -ci security <<<"$sortie")
	if [ -w "$(dirname "$f")" ] || mkdir -p "$(dirname "$f")" 2>/dev/null; then echo "$total $secu" > "$f" 2>/dev/null; fi
	echo "$total $secu"
}

cmd_etat() {
	local motd=0; [ "${1:-}" = "--motd" ] && motd=1
	local nom machine charge coeurs mt mu md dt du dp maj secu s actifs=()
	nom=$(. /etc/os-release; echo "$PRETTY_NAME")
	machine=$(hostname)
	read -r charge _ < /proc/loadavg; charge=$(cut -d' ' -f1-3 /proc/loadavg)
	coeurs=$(nproc)
	read -r mt mu < <(free -b | awk '/^Mem:/ {print $2, $3}')
	read -r dt du < <(df -B1 --output=size,used / | tail -1)
	dp=$(( du * 100 / dt )); md=$(( mu * 100 / mt ))
	read -r maj secu < <(mises_a_jour)

	if (( motd )); then
		echo
		[ -f /usr/share/averion/banniere.txt ] && sed "s/^/  /; s/^/${BCY}/; s/$/${R}/" /usr/share/averion/banniere.txt
		echo "  ${D}S E R V E R   ·   $nom${R}"
		echo
	else
		echo "${BCY}AVERION Server${R} ${D}— tableau de bord${R}"
		echo
	fi
	ligne "Machine" "${B}$machine${R}  ${D}$( ((motd)) || echo "· $nom")${R}"
	ligne "En service" "depuis $(duree_service)"
	ligne "Charge" "$charge  ${D}($coeurs cœurs)${R}"
	ligne "Mémoire" "$(barre "$md")" "$(go "$mu") / $(go "$mt")  ${D}($md %)${R}"
	ligne "Disque /" "$(barre "$dp")" "$(go "$du") / $(go "$dt")  ${D}($dp %)${R}"
	while read -r ifn adr; do
		ligne "Adresse IP" "${CY}${adr%/*}${R}  ${D}($ifn)${R}"
	done < <(ip -4 -o addr show scope global 2>/dev/null | awk '{print $2, $4}')
	est_active cockpit && ligne "Console web" "${CY}https://$(adresse_ip):9090${R}"
	if (( maj > 0 )); then
		ligne "Mises à jour" "${JA}$maj disponible(s)${R}$( ((secu > 0)) && echo ", ${RO}dont $secu de sécurité${R}")  ${D}→ sudo averion maj${R}"
	else
		ligne "Mises à jour" "${VE}système à jour${R}"
	fi
	[ -f /run/reboot-required ] && ligne "Redémarrage" "${JA}nécessaire pour terminer une mise à jour${R}"
	local echecs; echecs=$(systemctl --failed --no-legend --plain 2>/dev/null | awk '{print $1}' | tr '\n' ' ')
	[ -n "$echecs" ] && ligne "En échec" "${RO}$echecs${R}"

	echo
	local marque couleur lib
	for s in "${SERVICES_LISTE[@]}"; do
		[ -n "$(unites "$s")" ] || continue
		if est_active "$s"; then marque="${VE}●${R}"; couleur=""
		elif (( ! motd )); then marque="${GR}○"; couleur="$GR"
		else continue; fi
		actifs+=("$marque|$couleur|$(libelle "$s")")
	done
	printf '  %s%*s ' "Services" 5 ""
	local n=0 it
	for it in "${actifs[@]}"; do
		(( n > 0 && n % 4 == 0 )) && printf '\n%16s' ""
		marque=${it%%|*}; it=${it#*|}; couleur=${it%%|*}; lib=${it#*|}
		printf '%s %s%s%s%*s' "$marque" "$couleur" "$lib" "$R" $(( 17 - ${#lib} )) ""
		n=$((n + 1))
	done
	(( n )) || printf '%s' "${D}aucun service optionnel actif${R}"
	echo; echo
	(( motd )) && echo "  ${D}Administration : ${R}${CY}sudo averion${R}${D}  ·  aide : ${R}${CY}averion aide${R}" && echo
	return 0
}

# ═══════════════════════════════════════════════════════════════
#  Mises à jour
# ═══════════════════════════════════════════════════════════════
cmd_maj() {
	exiger_root
	export DEBIAN_FRONTEND=noninteractive
	local echec=0
	info "Recherche des mises à jour…"; apt-get update || echec=1
	info "Installation…"
	apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold full-upgrade || echec=1
	info "Nettoyage…"; apt-get -y autoremove --purge >/dev/null; apt-get clean
	rm -f "$CACHE_MAJ"
	if (( echec )); then erreur "Certaines mises à jour ont échoué. Vérifiez la connexion et relancez."; fi
	ok "AVERION Server est à jour."
	[ -f /run/reboot-required ] && alerte "Redémarrage conseillé : sudo reboot"
	return 0
}

# ═══════════════════════════════════════════════════════════════
#  Sites web
# ═══════════════════════════════════════════════════════════════
serveur_web() {
	if est_activee nginx; then echo nginx
	elif est_activee apache; then echo apache
	fi
}

valider_domaine() {
	[[ "$1" =~ ^([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}$ ]] || erreur "Nom de domaine invalide : « $1 » (ex. exemple.fr, blog.exemple.fr)"
}

page_attente() { # page_attente <domaine> <php 0|1>
	local d=$1 php=$2
	cat <<EOF
<!doctype html>
<html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>$d</title>
<style>body{margin:0;min-height:100vh;display:grid;place-items:center;font:16px/1.6 system-ui,sans-serif;
background:linear-gradient(180deg,#031b32,#06396a) fixed;color:#dbeafe}main{text-align:center;padding:24px}h1{margin:0 0 8px;font-size:30px}
p{color:#9fbcd9;margin:4px 0}code{color:#7ddcff}</style></head>
<body><main><h1>$d</h1><p>Ce site est prêt sur AVERION Server.</p>
<p>Déposez vos fichiers dans <code>/var/www/$d/public</code></p>
EOF
	(( php )) && echo "<p>PHP <?php echo PHP_VERSION; ?> est actif.</p>"
	echo "</main></body></html>"
}

cmd_site() {
	local action=${1:-liste}; shift || true
	case "$action" in
		creer|créer) site_creer "$@" ;;
		liste) site_liste ;;
		supprimer) site_supprimer "$@" ;;
		*) erreur "Usage : averion site creer|liste|supprimer …" ;;
	esac
}

site_creer() {
	exiger_root
	local d="" php=0 ssl=0 www=0 email="" srv racine conf sock
	while (( $# )); do
		case "$1" in
			--php) php=1 ;; --ssl) ssl=1 ;; --www) www=1 ;;
			--email) email=${2:-}; shift ;;
			-*) erreur "Option inconnue : $1" ;;
			*) d=${1,,} ;;
		esac
		shift
	done
	[ -n "$d" ] || erreur "Usage : averion site creer <domaine> [--php] [--ssl] [--www] [--email vous@exemple.fr]"
	valider_domaine "$d"
	srv=$(serveur_web)
	[ -n "$srv" ] || erreur "Aucun serveur web activé. Lancez d'abord : sudo averion activer nginx   (ou apache)"
	if (( php )) && ! est_activee php; then info "Activation de PHP…"; cmd_activer php >/dev/null; fi
	racine="/var/www/$d/public"
	[ -e "/var/www/$d" ] && erreur "Le dossier /var/www/$d existe déjà."
	local noms="$d"; (( www )) && noms="$d www.$d"

	mkdir -p "$racine"
	if (( php )); then page_attente "$d" 1 > "$racine/index.php"; else page_attente "$d" 0 > "$racine/index.html"; fi
	chown -R www-data:www-data "/var/www/$d"
	chmod 2775 "/var/www/$d" "$racine"
	sock=$(php_socket)

	if [ "$srv" = nginx ]; then
		conf="/etc/nginx/sites-available/$d"
		{
			echo "# Site $d — créé par averion le $(date +%F)"
			echo "server {"
			echo "    listen 80;"
			echo "    listen [::]:80;"
			echo "    server_name $noms;"
			echo "    root $racine;"
			echo "    index $( ((php)) && echo "index.php ") index.html;"
			echo "    access_log /var/log/nginx/$d.access.log;"
			echo "    error_log  /var/log/nginx/$d.error.log;"
			echo
			echo "    location / {"
			echo "        try_files \$uri \$uri/ $( ((php)) && echo "/index.php?\$query_string" || echo "=404");"
			echo "    }"
			if (( php )); then
				echo "    location ~ \\.php\$ {"
				echo "        include snippets/fastcgi-php.conf;"
				echo "        fastcgi_pass unix:$sock;"
				echo "    }"
			fi
			echo "    location ~ /\\.(?!well-known) { deny all; }"
			echo "}"
		} > "$conf"
		ln -sf "$conf" "/etc/nginx/sites-enabled/$d"
		rm -f /etc/nginx/sites-enabled/default
		nginx -t >/dev/null 2>&1 || { rm -f "/etc/nginx/sites-enabled/$d"; erreur "Configuration Nginx invalide (nginx -t)."; }
		systemctl reload nginx
	else
		conf="/etc/apache2/sites-available/$d.conf"
		{
			echo "# Site $d — créé par averion le $(date +%F)"
			echo "<VirtualHost *:80>"
			echo "    ServerName $d"
			(( www )) && echo "    ServerAlias www.$d"
			echo "    DocumentRoot $racine"
			echo "    <Directory $racine>"
			echo "        Options -Indexes +FollowSymLinks"
			echo "        AllowOverride All"
			echo "        Require all granted"
			echo "    </Directory>"
			if (( php )); then
				echo "    <FilesMatch \\.php\$>"
				echo "        SetHandler \"proxy:unix:$sock|fcgi://localhost\""
				echo "    </FilesMatch>"
				echo "    DirectoryIndex index.php index.html"
			fi
			echo "    ErrorLog \${APACHE_LOG_DIR}/$d.error.log"
			echo "    CustomLog \${APACHE_LOG_DIR}/$d.access.log combined"
			echo "</VirtualHost>"
		} > "$conf"
		a2ensite -q "$d" >/dev/null
		a2dissite -q 000-default >/dev/null 2>&1 || true
		apache2ctl configtest >/dev/null 2>&1 || { a2dissite -q "$d" >/dev/null; erreur "Configuration Apache invalide (apache2ctl configtest)."; }
		systemctl reload apache2
	fi
	ok "Site ${B}$d${R} créé ($srv$( ((php)) && echo " + PHP"))"
	echo "    Dossier : $racine"

	if (( ssl )); then
		info "Demande du certificat HTTPS (Let's Encrypt)…"
		local args=(--non-interactive --agree-tos --redirect)
		if [ -n "$email" ]; then args+=(-m "$email"); else args+=(--register-unsafely-without-email); fi
		for n in $noms; do args+=(-d "$n"); done
		if certbot "--$srv" "${args[@]}"; then
			ok "HTTPS actif : https://$d"
		else
			alerte "Le certificat n'a pas pu être obtenu. Vérifiez que $d pointe vers ce serveur (DNS) et que le port 80 est joignable, puis : sudo averion ssl $d"
		fi
	else
		echo "    Adresse : http://$d   ${D}(HTTPS : sudo averion ssl $d)${R}"
	fi
}

cmd_ssl() {
	exiger_root
	local d=${1:-} srv
	[ -n "$d" ] || erreur "Usage : averion ssl <domaine> [--email vous@exemple.fr]"
	srv=$(serveur_web); [ -n "$srv" ] || erreur "Aucun serveur web activé."
	if [ "${2:-}" = "--email" ] && [ -n "${3:-}" ]; then
		certbot "--$srv" --non-interactive --agree-tos --redirect -m "$3" -d "$d"
	else
		certbot "--$srv" --non-interactive --agree-tos --redirect --register-unsafely-without-email -d "$d"
	fi && ok "HTTPS actif : https://$d"
}

site_liste() {
	local f d n=0
	echo "${B}Sites web${R}"
	for f in /etc/nginx/sites-enabled/* /etc/apache2/sites-enabled/*; do
		[ -e "$f" ] || continue
		d=$(basename "$f" .conf)
		case "$d" in default|000-default|default-ssl) continue ;; esac
		[[ "$d" == *-le-ssl ]] && continue
		local web; [[ "$f" == /etc/nginx/* ]] && web=nginx || web=apache
		local https=""; [ -d "/etc/letsencrypt/live/$d" ] && https=" ${VE}HTTPS${R}"
		printf "  %-34s %-7s %s%s\n" "$d" "$web" "${D}/var/www/$d${R}" "$https"
		n=$((n + 1))
	done
	(( n )) || echo "  ${D}Aucun site. Créez-en un : sudo averion site creer mon-domaine.fr${R}"
}

site_supprimer() {
	exiger_root
	local d=${1:-} rep
	[ -n "$d" ] || erreur "Usage : averion site supprimer <domaine>"
	local trouve=0
	if [ -e "/etc/nginx/sites-available/$d" ]; then
		rm -f "/etc/nginx/sites-enabled/$d" "/etc/nginx/sites-available/$d"; trouve=1
		systemctl reload nginx 2>/dev/null
	fi
	if [ -e "/etc/apache2/sites-available/$d.conf" ]; then
		a2dissite -q "$d" "$d-le-ssl" >/dev/null 2>&1
		rm -f "/etc/apache2/sites-available/$d.conf" "/etc/apache2/sites-available/$d-le-ssl.conf"; trouve=1
		systemctl reload apache2 2>/dev/null
	fi
	(( trouve )) || erreur "Aucun site « $d »."
	ok "Site $d retiré du serveur web."
	if [ -d "/var/www/$d" ]; then
		read -rp "Supprimer aussi les fichiers de /var/www/$d ? [o/N] " rep
		if [[ "$rep" =~ ^[oO] ]]; then rm -rf "/var/www/$d"; ok "Fichiers supprimés."
		else info "Fichiers conservés dans /var/www/$d"; fi
	fi
}

# ═══════════════════════════════════════════════════════════════
#  Bases de données
# ═══════════════════════════════════════════════════════════════
mot_de_passe() { tr -dc 'A-Za-z0-9' < /dev/urandom | head -c "${1:-24}"; }

enregistrer_identifiants() { # fichier contenu
	install -d -m 700 "$IDENTIFIANTS"
	umask 077; printf '%s\n' "$2" > "$IDENTIFIANTS/$1"
}

cmd_bdd() {
	local action=${1:-liste}; shift || true
	case "$action" in
		creer|créer) bdd_creer "$@" ;;
		liste) bdd_liste ;;
		supprimer) bdd_supprimer "$@" ;;
		*) erreur "Usage : averion bdd creer|liste|supprimer …" ;;
	esac
}

moteur_bdd() { # choisit le moteur : --postgres / --mariadb ou celui qui est actif
	case "${1:-}" in
		--postgres|--postgresql) echo postgresql; return ;;
		--mariadb|--mysql) echo mariadb; return ;;
	esac
	if est_active mariadb; then echo mariadb
	elif est_active postgresql; then echo postgresql
	fi
}

bdd_creer() {
	exiger_root
	local nom=${1:-} moteur mdp texte
	[ -n "$nom" ] || erreur "Usage : averion bdd creer <nom> [--postgres]"
	[[ "$nom" =~ ^[a-z_][a-z0-9_]{0,31}$ ]] || erreur "Nom invalide : minuscules, chiffres et _ (32 caractères max)."
	moteur=$(moteur_bdd "${2:-}")
	[ -n "$moteur" ] || erreur "Aucune base de données active. Lancez : sudo averion activer mariadb   (ou postgresql)"
	est_active "$moteur" || erreur "$(libelle "$moteur") n'est pas démarré : sudo averion activer $moteur"
	mdp=$(mot_de_passe 24)
	if [ "$moteur" = mariadb ]; then
		mariadb -e "CREATE DATABASE \`$nom\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
			CREATE USER '$nom'@'localhost' IDENTIFIED BY '$mdp';
			GRANT ALL PRIVILEGES ON \`$nom\`.* TO '$nom'@'localhost'; FLUSH PRIVILEGES;" \
			|| erreur "Création impossible (la base ou l'utilisateur existe peut-être déjà)."
		texte="Moteur      : MariaDB (port 3306, local)
Base        : $nom
Utilisateur : $nom
Mot de passe: $mdp
Hôte        : localhost
DSN PHP     : mysql:host=localhost;dbname=$nom;charset=utf8mb4"
	else
		runuser -u postgres -- psql -q -v ON_ERROR_STOP=1 \
			-c "CREATE ROLE \"$nom\" LOGIN PASSWORD '$mdp';" \
			-c "CREATE DATABASE \"$nom\" OWNER \"$nom\" ENCODING 'UTF8';" \
			|| erreur "Création impossible (la base ou le rôle existe peut-être déjà)."
		texte="Moteur      : PostgreSQL (port 5432, local)
Base        : $nom
Utilisateur : $nom
Mot de passe: $mdp
Hôte        : localhost
URL         : postgresql://$nom:$mdp@localhost:5432/$nom"
	fi
	enregistrer_identifiants "bdd-$nom.txt" "$texte"
	ok "Base ${B}$nom${R} créée"
	sed 's/^/    /' <<<"$texte"
	echo "    ${D}(copie dans $IDENTIFIANTS/bdd-$nom.txt, lisible par root uniquement)${R}"
}

bdd_liste() {
	exiger_root
	local n=0
	echo "${B}Bases de données${R}"
	if est_active mariadb; then
		while read -r b; do printf "  %-30s %s\n" "$b" "${D}MariaDB${R}"; n=$((n + 1)); done < <(
			mariadb -N -e "SHOW DATABASES" 2>/dev/null | grep -vxE 'information_schema|performance_schema|mysql|sys')
	fi
	if est_active postgresql; then
		while read -r b; do printf "  %-30s %s\n" "$b" "${D}PostgreSQL${R}"; n=$((n + 1)); done < <(
			runuser -u postgres -- psql -Atc "SELECT datname FROM pg_database WHERE NOT datistemplate AND datname <> 'postgres'" 2>/dev/null)
	fi
	(( n )) || echo "  ${D}Aucune base. Créez-en une : sudo averion bdd creer ma_base${R}"
}

bdd_supprimer() {
	exiger_root
	local nom=${1:-} moteur rep
	[ -n "$nom" ] || erreur "Usage : averion bdd supprimer <nom> [--postgres]"
	[[ "$nom" =~ ^[a-z_][a-z0-9_]{0,31}$ ]] || erreur "Nom invalide."
	moteur=$(moteur_bdd "${2:-}"); [ -n "$moteur" ] || erreur "Aucune base de données active."
	read -rp "${RO}Supprimer définitivement la base « $nom » ($moteur) et son utilisateur ? [o/N] ${R}" rep
	[[ "$rep" =~ ^[oO] ]] || exit 0
	if [ "$moteur" = mariadb ]; then
		mariadb -e "DROP DATABASE IF EXISTS \`$nom\`; DROP USER IF EXISTS '$nom'@'localhost';"
	else
		runuser -u postgres -- psql -q -c "DROP DATABASE IF EXISTS \"$nom\";" -c "DROP ROLE IF EXISTS \"$nom\";"
	fi
	rm -f "$IDENTIFIANTS/bdd-$nom.txt"
	ok "Base $nom supprimée."
}

# ═══════════════════════════════════════════════════════════════
#  Comptes FTP
# ═══════════════════════════════════════════════════════════════
cmd_ftp() {
	local action=${1:-liste}; shift || true
	case "$action" in
		ajouter) ftp_ajouter "$@" ;;
		supprimer) ftp_supprimer "$@" ;;
		liste) ftp_liste ;;
		mdp) ftp_mdp "$@" ;;
		*) erreur "Usage : averion ftp ajouter|supprimer|liste|mdp …" ;;
	esac
}

ftp_ajouter() {
	exiger_root
	local compte="" dossier="" mdp
	while (( $# )); do
		case "$1" in
			--dossier) dossier=${2:-}; shift ;;
			-*) erreur "Option inconnue : $1" ;;
			*) compte=$1 ;;
		esac
		shift
	done
	[ -n "$compte" ] || erreur "Usage : averion ftp ajouter <compte> [--dossier /var/www/mon-site.fr]"
	[[ "$compte" =~ ^[a-z_][a-z0-9_-]{0,31}$ ]] || erreur "Nom de compte invalide."
	id "$compte" >/dev/null 2>&1 && erreur "Le compte « $compte » existe déjà."
	dossier=${dossier:-/srv/ftp/$compte}
	mkdir -p "$dossier"
	useradd -d "$dossier" -M -s /usr/sbin/nologin -G averion-ftp,www-data -c "Compte FTP AVERION" "$compte" \
		|| erreur "Création du compte impossible."
	chown "$compte:www-data" "$dossier"; chmod 2775 "$dossier"
	mdp=$(mot_de_passe 16)
	printf '%s:%s\n' "$compte" "$mdp" | chpasswd
	grep -qx "$compte" /etc/vsftpd.user_list 2>/dev/null || echo "$compte" >> /etc/vsftpd.user_list
	enregistrer_identifiants "ftp-$compte.txt" "Serveur : $(adresse_ip)  (port 21, FTP avec TLS explicite)
Compte  : $compte
Mot de passe : $mdp
Dossier : $dossier"
	ok "Compte FTP ${B}$compte${R} créé"
	echo "    Serveur      : $(adresse_ip)   port 21 (FTPS recommandé)"
	echo "    Mot de passe : $mdp"
	echo "    Dossier      : $dossier"
	est_active ftp || alerte "Le serveur FTP est éteint : sudo averion activer ftp"
}

ftp_mdp() {
	exiger_root
	local compte=${1:-} mdp
	id -nG "$compte" 2>/dev/null | grep -qw averion-ftp || erreur "« $compte » n'est pas un compte FTP AVERION."
	mdp=$(mot_de_passe 16); printf '%s:%s\n' "$compte" "$mdp" | chpasswd
	ok "Nouveau mot de passe de $compte : $mdp"
}

ftp_supprimer() {
	exiger_root
	local compte=${1:-}
	[ -n "$compte" ] || erreur "Usage : averion ftp supprimer <compte>"
	id -nG "$compte" 2>/dev/null | grep -qw averion-ftp || erreur "« $compte » n'est pas un compte FTP AVERION."
	userdel "$compte" || erreur "Suppression impossible."
	sed -i "/^${compte}\$/d" /etc/vsftpd.user_list
	rm -f "$IDENTIFIANTS/ftp-$compte.txt"
	ok "Compte FTP $compte supprimé (ses fichiers sont conservés)."
}

ftp_liste() {
	local membres m n=0
	echo "${B}Comptes FTP${R}"
	membres=$(getent group averion-ftp | cut -d: -f4 | tr ',' ' ')
	for m in $membres; do
		printf "  %-20s %s\n" "$m" "${D}$(getent passwd "$m" | cut -d: -f6)${R}"; n=$((n + 1))
	done
	(( n )) || echo "  ${D}Aucun compte. Créez-en un : sudo averion ftp ajouter nom_du_compte${R}"
}

# ═══════════════════════════════════════════════════════════════
#  Pare-feu, sauvegardes, sécurité
# ═══════════════════════════════════════════════════════════════
cmd_pare_feu() {
	exiger_root
	local action=${1:-etat} port=${2:-}
	case "$action" in
		etat|état) ufw status verbose ;;
		ouvrir) [ -n "$port" ] || erreur "Usage : averion pare-feu ouvrir <port>[/tcp|/udp]"; ufw allow "$port" && ok "Port $port ouvert." ;;
		fermer) [ -n "$port" ] || erreur "Usage : averion pare-feu fermer <port>[/tcp|/udp]"; ufw delete allow "$port" && ok "Port $port fermé." ;;
		bannis) fail2ban-client status sshd 2>/dev/null || erreur "fail2ban n'est pas actif." ;;
		*) erreur "Usage : averion pare-feu [etat|ouvrir <port>|fermer <port>|bannis]" ;;
	esac
}

cmd_sauvegarde() {
	exiger_root
	case "${1:-}" in
		liste)
			echo "${B}Sauvegardes${R}  ${D}($SAUVEGARDES)${R}"
			ls -1d "$SAUVEGARDES"/20* 2>/dev/null | while read -r s; do
				printf "  %-20s %s\n" "$(basename "$s")" "$(du -sh "$s" | cut -f1)"
			done
			systemctl is-enabled --quiet averion-sauvegarde.timer 2>/dev/null \
				&& echo "  ${VE}Sauvegarde automatique quotidienne active${R}" \
				|| echo "  ${D}Automatique : sudo averion sauvegarde auto oui${R}"
			return ;;
		auto)
			case "${2:-}" in
				oui|on) systemctl enable --now averion-sauvegarde.timer >/dev/null && ok "Sauvegarde automatique chaque nuit (3 h 15)." ;;
				non|off) systemctl disable --now averion-sauvegarde.timer >/dev/null && ok "Sauvegarde automatique désactivée." ;;
				*) erreur "Usage : averion sauvegarde auto oui|non" ;;
			esac
			return ;;
	esac

	local garder=7 dest
	[ "${1:-}" = "--garder" ] && garder=${2:-7}
	dest="$SAUVEGARDES/$(date +%Y-%m-%d_%H%M)"
	install -d -m 700 "$dest"
	info "Sauvegarde vers $dest"
	tar --zstd -cpf "$dest/etc.tar.zst" /etc 2>/dev/null && ok "Configuration (/etc)"
	[ -d /var/www ] && tar --zstd -cpf "$dest/www.tar.zst" /var/www 2>/dev/null && ok "Sites web (/var/www)"
	[ -d /srv ] && [ -n "$(ls -A /srv 2>/dev/null)" ] && tar --zstd -cpf "$dest/srv.tar.zst" /srv 2>/dev/null && ok "Données (/srv)"
	[ -d "$IDENTIFIANTS" ] && tar --zstd -cpf "$dest/identifiants.tar.zst" "$IDENTIFIANTS" 2>/dev/null
	if est_active mariadb; then
		mariadb-dump --all-databases --single-transaction --routines --events 2>/dev/null | zstd -q > "$dest/mariadb.sql.zst" \
			&& ok "Bases MariaDB"
	fi
	if est_active postgresql; then
		runuser -u postgres -- pg_dumpall 2>/dev/null | zstd -q > "$dest/postgresql.sql.zst" && ok "Bases PostgreSQL"
	fi
	chmod -R go-rwx "$dest"
	ls -1d "$SAUVEGARDES"/20* 2>/dev/null | head -n "-$garder" | xargs -r rm -rf
	ok "Sauvegarde terminée ($(du -sh "$dest" | cut -f1)) — $garder dernières conservées."
}

cmd_securite() {
	exiger_root
	info "Audit de sécurité (Lynis)…"
	lynis audit system --quick --no-log 2>/dev/null | sed -n '/Lynis security scan details/,$p'
	echo
	info "Adresses bannies par l'anti-intrusion :"
	fail2ban-client status sshd 2>/dev/null | grep -E 'Currently banned|Banned IP' || echo "  fail2ban inactif"
}

# ═══════════════════════════════════════════════════════════════
#  Menu interactif
# ═══════════════════════════════════════════════════════════════
export NEWT_COLORS='root=white,black
border=cyan,black
window=white,black
title=brightcyan,black
button=black,cyan
actbutton=black,brightcyan
checkbox=white,black
actcheckbox=black,cyan
entry=white,black
listbox=white,black
actlistbox=black,cyan
actsellistbox=black,brightcyan
textbox=white,black
roottext=brightcyan,black'

pause() { echo; read -rp "${D}Entrée pour revenir au menu…${R}" _; }
demander() { whiptail --backtitle "AVERION Server · $(hostname)" --title "$1" --inputbox "$2" 10 70 "${3:-}" 3>&1 1>&2 2>&3; }

menu_services() {
	local s args=() choix
	for s in cockpit nginx apache php mariadb postgresql redis ftp docker samba; do
		[ -n "$(unites "$s")" ] || continue
		args+=("$s" "$(libelle "$s")" "$(est_activee "$s" && echo ON || echo OFF)")
	done
	choix=$(whiptail --backtitle "AVERION Server · $(hostname)" --title "Services" --separate-output \
		--checklist "Cochez les services à faire tourner (Espace pour cocher) :" 20 70 11 "${args[@]}" 3>&1 1>&2 2>&3) || return
	clear
	for s in cockpit nginx apache php mariadb postgresql redis ftp docker samba; do
		[ -n "$(unites "$s")" ] || continue
		if grep -qx "$s" <<<"$choix"; then est_activee "$s" || ( cmd_activer "$s" )
		else est_activee "$s" && ( cmd_desactiver "$s" ); fi
	done
	pause
}

cmd_menu() {
	exiger_root
	local choix v v2
	while :; do
		choix=$(whiptail --backtitle "AVERION Server $VERSION · $(hostname) · $(adresse_ip)" --title "Administration" \
			--ok-button "Ouvrir" --cancel-button "Quitter" --menu "Que voulez-vous faire ?" 22 70 14 \
			etat       "Tableau de bord" \
			services   "Activer / désactiver des services" \
			maj        "Installer les mises à jour" \
			site       "Créer un site web" \
			sites      "Lister les sites web" \
			bdd        "Créer une base de données" \
			bdds       "Lister les bases de données" \
			ftp        "Ajouter un compte FTP" \
			ftps       "Lister les comptes FTP" \
			pare-feu   "État du pare-feu" \
			sauvegarde "Faire une sauvegarde maintenant" \
			securite   "Audit de sécurité" \
			infos      "Fiche du système (fastfetch)" 3>&1 1>&2 2>&3) || { clear; exit 0; }
		clear
		case "$choix" in
			etat) cmd_etat; pause ;;
			services) menu_services ;;
			maj) ( cmd_maj ); pause ;;
			site)
				v=$(demander "Nouveau site" "Nom de domaine (ex. exemple.fr) :") || continue
				v2=""
				whiptail --title "Nouveau site" --yesno "Activer PHP pour $v ?" 8 60 && v2+=" --php"
				whiptail --title "Nouveau site" --yesno "Demander un certificat HTTPS gratuit (Let's Encrypt) ?\n\nLe domaine doit déjà pointer vers ce serveur." 10 60 && v2+=" --ssl"
				clear
				# shellcheck disable=SC2086
				( site_creer "$v" $v2 ); pause ;;
			sites) site_liste; pause ;;
			bdd)
				v=$(demander "Nouvelle base" "Nom de la base (minuscules, chiffres, _) :") || continue
				clear; ( bdd_creer "$v" ); pause ;;
			bdds) ( bdd_liste ); pause ;;
			ftp)
				v=$(demander "Nouveau compte FTP" "Nom du compte :") || continue
				v2=$(demander "Nouveau compte FTP" "Dossier accessible :" "/srv/ftp/$v") || continue
				clear; ( ftp_ajouter "$v" --dossier "$v2" ); pause ;;
			ftps) ftp_liste; pause ;;
			pare-feu) ufw status verbose; pause ;;
			sauvegarde) ( cmd_sauvegarde ); pause ;;
			securite) ( cmd_securite ) | less -R ;;
			infos) fastfetch; pause ;;
		esac
	done
}

cmd_aide() {
	cat <<EOF
${BCY}averion${R} ${D}— administration d'AVERION Server $VERSION${R}

  ${B}averion${R}                              menu interactif
  ${B}averion etat${R}                         tableau de bord
  ${B}averion maj${R}                          installer les mises à jour

  ${CY}Services${R}
  averion services                     état de tous les services
  averion activer <service>            démarre et active au démarrage (+ ouvre les ports)
  averion desactiver <service>         arrête et désactive (+ ferme les ports)
      ${D}services : ${SERVICES_LISTE[*]}${R}

  ${CY}Sites web${R}
  averion site creer <domaine> [--php] [--ssl] [--www] [--email vous@exemple.fr]
  averion site liste
  averion site supprimer <domaine>
  averion ssl <domaine>                certificat HTTPS Let's Encrypt

  ${CY}Bases de données${R}
  averion bdd creer <nom> [--postgres] crée base + utilisateur + mot de passe
  averion bdd liste
  averion bdd supprimer <nom>

  ${CY}FTP${R}
  averion ftp ajouter <compte> [--dossier /var/www/site]
  averion ftp liste | supprimer <compte> | mdp <compte>

  ${CY}Sécurité et sauvegarde${R}
  averion pare-feu [etat|ouvrir <port>|fermer <port>|bannis]
  averion sauvegarde [--garder N]      /etc, /var/www, /srv et toutes les bases
  averion sauvegarde liste | auto oui|non
  averion securite                     audit Lynis + adresses bannies

  ${D}Identifiants générés : $IDENTIFIANTS (root uniquement)
  Console web : https://<adresse-du-serveur>:9090   ·   Aide : https://agorea.fr${R}
EOF
}

# ═══════════════════════════════════════════════════════════════
ARGS_ORIGINE=("$@")
commande=${1:-menu}; shift || true
case "$commande" in
	menu) cmd_menu ;;
	etat|état|status) cmd_etat "$@" ;;
	maj|mise-a-jour|update) cmd_maj ;;
	services) cmd_services ;;
	activer|enable) cmd_activer "$@" ;;
	desactiver|désactiver|disable) cmd_desactiver "$@" ;;
	site|sites) cmd_site "$@" ;;
	ssl|https) cmd_ssl "$@" ;;
	bdd|db) cmd_bdd "$@" ;;
	ftp) cmd_ftp "$@" ;;
	pare-feu|parefeu|firewall) cmd_pare_feu "$@" ;;
	sauvegarde|backup) cmd_sauvegarde "$@" ;;
	securite|sécurité|audit) cmd_securite ;;
	aide|help|-h|--help) cmd_aide ;;
	version|--version) echo "averion $VERSION" ;;
	*) echo "Commande inconnue : $commande"; echo; cmd_aide; exit 1 ;;
esac
