#!/bin/bash

# ==============================================================================
# LNMP — Gestionnaire de pile web (Nginx, MariaDB, PHP, SSL) & domaines
# ==============================================================================
# Le script sert trois consommateurs qui partagent la même logique :
#   - le menu interactif (aucun argument) ;
#   - la ligne de commande directe (scriptable) ;
#   - le démon web lnmpd, via le mode --json.
# ==============================================================================

# Version injectée au build par debian/rules (placeholder 1.21-1).
VERSION="1.21-1"
if [[ "$VERSION" == "1.21-1" ]]; then
    VERSION="$(dpkg-query -W -f='${Version}' lnmp 2>/dev/null || echo 'dev')"
fi

# ---- Valeurs par défaut (surchargées par /etc/lnmp/lnmp.conf) ----------------
PHP_VERSION="8.3"
DEFAULT_DOMAIN=""
ADMIN_PORT="8787"          # port du démon lnmpd (toujours sur 127.0.0.1)
ADMIN_HOST="admin.lnmp"    # nom d'hôte utilisé dans le lien du panneau
ADMIN_LISTEN="127.0.0.1"   # interface d'écoute du vhost admin (127.0.0.1 / 0.0.0.0 / IP)

# ---- Chemins -----------------------------------------------------------------
CONFIG_DIR="/etc/lnmp"
LNMP_CONF="$CONFIG_DIR/lnmp.conf"
DOMAINS_FILE="$CONFIG_DIR/domains.list"
ADMIN_CONF="$CONFIG_DIR/admin.conf"
STATE_FILE="/var/log/lnmp_install_state"
APPS_REGISTRY="/etc/nginx/lnmp_apps.list"
HOSTS_FILE="/etc/hosts"
CERTS_DIR="$CONFIG_DIR/certs"
WEBUI_SRC="/usr/share/lnmp/webui"
WEBUI_DEST="/var/www/html/lnmp"

GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'

# ==============================================================================
# ANALYSE DES ARGUMENTS (options nommées + --json + positionnels)
# ==============================================================================
JSON_MODE=0
declare -A OPT
POSITIONAL=()
while [ $# -gt 0 ]; do
    case "$1" in
        --json)            JSON_MODE=1; shift ;;
        -y|--yes)          OPT[yes]=1; shift ;;
        --apex)            OPT[apex]=1; shift ;;
        --local)           OPT[local]=1; shift ;;
        --nginx)           OPT[nginx]=1; shift ;;
        --mariadb)         OPT[mariadb]=1; shift ;;
        --php)             OPT[php]=1; shift ;;
        --apps)            OPT[apps]=1; shift ;;
        -h|--help)         POSITIONAL+=("help"); shift ;;
        -v|--version)      POSITIONAL+=("version"); shift ;;
        --id|--domain|--path|--sub|--email|--password|--name|--type|--admin-host|--listen)
                           key="${1#--}"; OPT[$key]="$2"; shift 2 ;;
        --)                shift; while [ $# -gt 0 ]; do POSITIONAL+=("$1"); shift; done ;;
        *)                 POSITIONAL+=("$1"); shift ;;
    esac
done
set -- "${POSITIONAL[@]}"

# ==============================================================================
# GARDE ROOT + UTILISATEUR RÉEL + CONFIG
# ==============================================================================
if [ "$EUID" -ne 0 ]; then
    echo -e "${RED}Erreur : Ce script doit être exécuté en tant que root (sudo).${NC}"
    exit 1
fi

REAL_USER=${SUDO_USER:-$(logname 2>/dev/null || echo "$USER")}
if [ "$REAL_USER" = "root" ]; then
    REAL_USER=$(awk -F: '$3>=1000 && $1!="nobody" {print $1; exit}' /etc/passwd)
fi

# Charge la configuration persistée (surcharge les valeurs par défaut ci-dessus).
[ -f "$LNMP_CONF" ] && . "$LNMP_CONF"

mkdir -p "$CONFIG_DIR" 2>/dev/null
touch "$APPS_REGISTRY" "$DOMAINS_FILE" 2>/dev/null

# ==============================================================================
# HELPERS JSON / RÉSULTATS
# ==============================================================================
json_escape() {
    local s=${1//\\/\\\\}
    s=${s//\"/\\\"}
    s=${s//$'\n'/\\n}
    s=${s//$'\t'/\\t}
    printf '%s' "$s"
}

# emit_result <ok|err> <message>
# En mode --json : imprime {"ok":bool,"message":"..."} ; sinon un texte coloré.
emit_result() {
    if [ "$JSON_MODE" -eq 1 ]; then
        local okval="true"; [ "$1" = "err" ] && okval="false"
        printf '{"ok":%s,"message":"%s"}\n' "$okval" "$(json_escape "$2")"
    else
        if [ "$1" = "err" ]; then echo -e "${RED}$2${NC}"; else echo -e "${GREEN}$2${NC}"; fi
    fi
}

get_state() {
    if [ -f "$STATE_FILE" ]; then
        local val; val=$(tr -d '[:space:]' < "$STATE_FILE")
        [[ "$val" =~ ^[0-9]$ ]] && echo "$val" || echo "0"
    else
        echo "0"
    fi
}
set_state() { echo "$1" > "$STATE_FILE"; }

# ==============================================================================
# VALIDATION & NGINX
# ==============================================================================
validate_app_id() { [[ "$1" =~ ^[a-zA-Z0-9._-]+$ ]]; }
validate_domain()  { [[ "$1" =~ ^[a-zA-Z0-9.-]+$ ]]; }

reload_nginx() {
    if nginx -t 2>/dev/null; then
        systemctl reload nginx 2>/dev/null
        return 0
    fi
    [ "$JSON_MODE" -eq 0 ] && { echo -e "${RED}Configuration Nginx invalide.${NC}"; nginx -t; }
    return 1
}

# Écrit un vhost PHP standard : write_vhost <id> <server_name> <root>
write_vhost() {
    local id=$1 server_name=$2 root=$3
    cat > /etc/nginx/sites-available/"$id" << EOF
server {
    listen 80;
    server_name ${server_name};
    root ${root};
    index index.html index.php;
    charset utf-8;
    location / { try_files \$uri \$uri/ /index.php?\$query_string; }
    location ~ \.php\$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php${PHP_VERSION}-fpm.sock;
    }
}
EOF
}

# Vhost sécurisé par un certificat local (mkcert) : HTTP redirige vers HTTPS.
write_vhost_ssl() {
    local id=$1 server_name=$2 root=$3 cert=$4 key=$5
    cat > /etc/nginx/sites-available/"$id" << EOF
server {
    listen 80;
    server_name ${server_name};
    return 301 https://\$host\$request_uri;
}
server {
    listen 443 ssl;
    server_name ${server_name};
    ssl_certificate     ${cert};
    ssl_certificate_key ${key};
    root ${root};
    index index.html index.php;
    charset utf-8;
    location / { try_files \$uri \$uri/ /index.php?\$query_string; }
    location ~ \.php\$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php${PHP_VERSION}-fpm.sock;
    }
}
EOF
}

# ==============================================================================
# GESTION /etc/hosts (domaines locaux façon Laragon)
# ==============================================================================
hosts_add() {
    local fqdn=$1 ip=${2:-127.0.0.1}
    grep -qE "[[:space:]]${fqdn//./\\.}([[:space:]]|\$)" "$HOSTS_FILE" 2>/dev/null && return 0
    printf '%s\t%s\t# lnmp\n' "$ip" "$fqdn" >> "$HOSTS_FILE"
}
hosts_remove() {
    local fqdn=$1 esc
    esc=$(printf '%s' "$fqdn" | sed 's/[.[\*^$/]/\\&/g')
    # Supprime la ligne gérée par lnmp pour ce FQDN (bornée par des espaces + marqueur).
    sed -i "/[[:space:]]${esc}[[:space:]].*#[[:space:]]*lnmp/d" "$HOSTS_FILE"
}

# ==============================================================================
# CONFIG : domaine par défaut, écriture de lnmp.conf
# ==============================================================================
write_config() {
    cat > "$LNMP_CONF" << EOF
# Configuration LNMP (généré automatiquement — modifiable)
DEFAULT_DOMAIN="$DEFAULT_DOMAIN"
PHP_VERSION="$PHP_VERSION"
ADMIN_PORT="$ADMIN_PORT"
ADMIN_HOST="$ADMIN_HOST"
ADMIN_LISTEN="$ADMIN_LISTEN"
EOF
    chmod 644 "$LNMP_CONF"
}

# Demande le domaine par défaut si absent (utilisé à l'installation).
ensure_default_domain() {
    [ -n "$DEFAULT_DOMAIN" ] && return 0
    echo -e "${BLUE}--- Configuration du domaine par défaut ---${NC}"
    read -p "Domaine par défaut (ex: mawena.cloud, ou 'test' pour du local) : " dd
    dd=$(echo "$dd" | tr -d ' ')
    [ -z "$dd" ] && dd="test"
    local dtype="remote"
    read -p "Ce domaine est-il local (résolu via /etc/hosts) ? (y/n) : " isloc
    [[ "$isloc" =~ ^[Yy]$ ]] && dtype="local"
    DEFAULT_DOMAIN="$dd"
    grep -qE "^${dd}:" "$DOMAINS_FILE" || echo "${dd}:${dtype}" >> "$DOMAINS_FILE"
    write_config
    echo -e "${GREEN}Domaine par défaut : ${dd} (${dtype})${NC}"
}

# ==============================================================================
# GESTION DES DOMAINES DE BASE
# ==============================================================================
domain_is_local() { grep -qiE "^$1:local$" "$DOMAINS_FILE"; }

domain_list() {
    if [ "$JSON_MODE" -eq 1 ]; then
        local first=1
        printf '{"ok":true,"default":"%s","domains":[' "$(json_escape "$DEFAULT_DOMAIN")"
        while IFS=: read -r name type; do
            [ -z "$name" ] && continue
            [ $first -eq 0 ] && printf ','
            printf '{"name":"%s","type":"%s"}' "$(json_escape "$name")" "$(json_escape "$type")"
            first=0
        done < "$DOMAINS_FILE"
        printf ']}\n'
        return
    fi
    echo -e "${BLUE}=== Domaines de base ===${NC}"
    if [ ! -s "$DOMAINS_FILE" ]; then echo "  Aucun domaine enregistré."; return; fi
    while IFS=: read -r name type; do
        [ -z "$name" ] && continue
        local tag="distant"; [ "$type" = "local" ] && tag="local"
        local star=""; [ "$name" = "$DEFAULT_DOMAIN" ] && star=" ${YELLOW}(défaut)${NC}"
        echo -e "  ${GREEN}$name${NC}  [$tag]$star"
    done < "$DOMAINS_FILE"
}

domain_add() {
    local name=$1 type=$2
    [ -z "$name" ] && { emit_result err "Nom de domaine requis."; return 1; }
    if ! validate_domain "$name"; then emit_result err "Nom de domaine invalide."; return 1; fi
    [ "$type" != "local" ] && type="remote"
    if grep -qiE "^${name}:" "$DOMAINS_FILE"; then emit_result err "Le domaine '$name' existe déjà."; return 1; fi
    echo "${name}:${type}" >> "$DOMAINS_FILE"
    [ -z "$DEFAULT_DOMAIN" ] && { DEFAULT_DOMAIN="$name"; write_config; }
    emit_result ok "Domaine '$name' ($type) ajouté."
}

domain_remove() {
    local name=$1 esc
    [ -z "$name" ] && { emit_result err "Nom de domaine requis."; return 1; }
    esc=$(printf '%s' "$name" | sed 's/[.[\*^$/]/\\&/g')
    sed -i "/^${esc}:/d" "$DOMAINS_FILE"
    emit_result ok "Domaine '$name' retiré."
}

domain_add_interactive() {
    read -p "Nom du domaine de base (ex: test, api.mawena.cloud) : " name
    local type="remote"
    read -p "Local (résolu via /etc/hosts) ? (y/n) : " isloc
    [[ "$isloc" =~ ^[Yy]$ ]] && type="local"
    domain_add "$name" "$type"
}

# ==============================================================================
# RELOAD-APP (reconstruction du registre depuis Nginx)
# ==============================================================================
reload_app() {
    local tmp_registry="/tmp/lnmp_apps.tmp"
    rm -f "$tmp_registry"; touch "$tmp_registry"

    if [ ! -d /etc/nginx/sites-available ]; then
        emit_result err "Nginx ne semble pas installé ou configuré."
        return 1
    fi

    for vhost_file in /etc/nginx/sites-available/*; do
        [ -e "$vhost_file" ] || continue
        local app_id; app_id=$(basename "$vhost_file")
        [ "$app_id" = "default" ] && continue
        [ "$app_id" = "admin.lnmp" ] && continue

        local domain root_path base
        domain=$(grep -E '^[[:space:]]*server_name' "$vhost_file" | head -n 1 | awk '{print $2}' | tr -d ';')
        root_path=$(grep -E '^[[:space:]]*root' "$vhost_file" | head -n 1 | awk '{print $2}' | tr -d ';')
        [ -z "$domain" ] && domain="inconnu"
        [ -z "$root_path" ] && root_path="/var/www/html"
        # Domaine de base = ce qui suit le premier point (heuristique)
        base="${domain#*.}"
        echo "$app_id:$domain:$root_path:$base" >> "$tmp_registry"
    done

    mv "$tmp_registry" "$APPS_REGISTRY"
    chmod 644 "$APPS_REGISTRY"
    emit_result ok "Registre réaligné ($(wc -l < "$APPS_REGISTRY") applications)."
}

# ==============================================================================
# GESTION DES APPLICATIONS
# ==============================================================================
add_web_app() {
    local app_id="${OPT[id]}"
    local base_domain="${OPT[domain]}"
    local sub="${OPT[sub]}"
    local app_path="${OPT[path]}"
    local use_apex=0; [ -n "${OPT[apex]}" ] && use_apex=1

    if [ -z "$app_id" ]; then
        if [ "$JSON_MODE" -eq 1 ]; then emit_result err "Paramètre --id requis."; return 1; fi
        echo -e "${BLUE}=== Ajouter une nouvelle application Web ===${NC}"
        read -p "ID unique de l'app (ex: blog) : " app_id
        app_id=$(echo "$app_id" | tr -d ' ')
    fi
    [ -z "$app_id" ] && return 1
    if ! validate_app_id "$app_id"; then emit_result err "ID invalide (lettres, chiffres, . _ -)."; return 1; fi
    if [ -f "/etc/nginx/sites-available/$app_id" ]; then emit_result err "L'ID '$app_id' existe déjà."; return 1; fi

    # --- Domaine de base ---
    if [ -z "$base_domain" ]; then
        if [ "$JSON_MODE" -eq 1 ]; then
            base_domain="$DEFAULT_DOMAIN"
        else
            domain_list
            read -p "Domaine de base [${DEFAULT_DOMAIN}] : " base_domain
            [ -z "$base_domain" ] && base_domain="$DEFAULT_DOMAIN"
        fi
    fi
    [ -z "$base_domain" ] && { emit_result err "Aucun domaine de base disponible (ajoutez-en un)."; return 1; }
    if ! grep -qiE "^${base_domain}:" "$DOMAINS_FILE"; then
        if [ "$JSON_MODE" -eq 1 ]; then emit_result err "Domaine de base inconnu : $base_domain"; return 1; fi
        echo "${base_domain}:remote" >> "$DOMAINS_FILE"
    fi

    # --- FQDN ---
    local fqdn
    if [ "$use_apex" -eq 1 ]; then
        fqdn="$base_domain"
    else
        [ -z "$sub" ] && sub="$app_id"
        fqdn="${sub}.${base_domain}"
    fi
    if ! validate_domain "$fqdn"; then emit_result err "FQDN invalide : $fqdn"; return 1; fi

    # --- Chemin ---
    if [ -z "$app_path" ]; then
        if [ "$JSON_MODE" -eq 1 ]; then
            app_path="/var/www/html/${app_id}/public"
        else
            read -p "Chemin absolu [/var/www/html/${app_id}/public] : " app_path
            [ -z "$app_path" ] && app_path="/var/www/html/${app_id}/public"
        fi
    fi
    if [[ "$app_path" != /* ]]; then emit_result err "Le chemin doit être absolu."; return 1; fi

    mkdir -p "$app_path"
    chown -R www-data:webdev "$app_path" 2>/dev/null || true

    write_vhost "$app_id" "$fqdn" "$app_path"
    ln -sf /etc/nginx/sites-available/"$app_id" /etc/nginx/sites-enabled/
    if ! reload_nginx; then
        rm -f /etc/nginx/sites-enabled/"$app_id" /etc/nginx/sites-available/"$app_id"
        emit_result err "Configuration Nginx invalide, ajout annulé."
        return 1
    fi

    domain_is_local "$base_domain" && hosts_add "$fqdn"

    echo "${app_id}:${fqdn}:${app_path}:${base_domain}" >> "$APPS_REGISTRY"
    emit_result ok "Application '$app_id' ajoutée : http://${fqdn}"

    if [ "$JSON_MODE" -eq 0 ]; then
        local gen_ssl
        if domain_is_local "$base_domain"; then
            read -p "Générer un certificat SSL local (mkcert) ? (y/n) : " gen_ssl
        else
            read -p "Générer un certificat SSL Let's Encrypt ? (y/n) : " gen_ssl
        fi
        [[ "$gen_ssl" =~ ^[Yy]$ ]] && add_ssl_cert "$app_id"
    fi
}

list_web_apps() {
    if [ "$JSON_MODE" -eq 1 ]; then
        local first=1
        printf '{"ok":true,"apps":['
        while IFS=: read -r id fqdn path base; do
            [ -z "$id" ] && continue
            local active=false
            { [ -L "/etc/nginx/sites-enabled/$id" ] || [ -e "/etc/nginx/sites-enabled/$id" ]; } && active=true
            [ $first -eq 0 ] && printf ','
            printf '{"id":"%s","domain":"%s","path":"%s","base":"%s","active":%s}' \
                "$(json_escape "$id")" "$(json_escape "$fqdn")" "$(json_escape "$path")" "$(json_escape "$base")" "$active"
            first=0
        done < "$APPS_REGISTRY"
        printf ']}\n'
        return
    fi
    if [ ! -s "$APPS_REGISTRY" ]; then echo "Aucune application enregistrée."; return; fi
    echo -e "ID App\t\t| Statut\t| Domaine"
    echo "------------------------------------------------------------------------"
    while IFS=: read -r id fqdn path base; do
        [ -z "$id" ] && continue
        local status="${RED}Inactif${NC}"
        { [ -L "/etc/nginx/sites-enabled/$id" ] || [ -e "/etc/nginx/sites-enabled/$id" ]; } && status="${GREEN}Actif${NC}"
        echo -e "${GREEN}$id${NC}\t| $status\t| $fqdn"
    done < "$APPS_REGISTRY"
}

enable_web_app() {
    local target_id="${OPT[id]:-$1}"
    if [ -z "$target_id" ] && [ "$JSON_MODE" -eq 0 ]; then
        echo "Applications disponibles :"; ls -1 /etc/nginx/sites-available/
        read -p "ID de l'application à activer : " target_id
    fi
    [ -z "$target_id" ] && { emit_result err "ID requis."; return 1; }
    if [ ! -f "/etc/nginx/sites-available/$target_id" ]; then emit_result err "Le vhost '$target_id' n'existe pas."; return 1; fi
    if [ -e "/etc/nginx/sites-enabled/$target_id" ]; then emit_result ok "Application '$target_id' déjà active."; return 0; fi
    ln -sf /etc/nginx/sites-available/"$target_id" /etc/nginx/sites-enabled/
    if ! reload_nginx; then rm -f /etc/nginx/sites-enabled/"$target_id"; emit_result err "Config invalide, activation annulée."; return 1; fi
    emit_result ok "Application '$target_id' activée."
}

disable_web_app() {
    local target_id="${OPT[id]:-$1}"
    if [ -z "$target_id" ] && [ "$JSON_MODE" -eq 0 ]; then
        list_web_apps
        read -p "ID de l'application à désactiver : " target_id
    fi
    [ -z "$target_id" ] && { emit_result err "ID requis."; return 1; }
    if [ ! -e "/etc/nginx/sites-enabled/$target_id" ]; then emit_result ok "Application '$target_id' déjà inactive."; return 0; fi
    rm -f /etc/nginx/sites-enabled/"$target_id"
    reload_nginx
    emit_result ok "Application '$target_id' désactivée."
}

delete_web_app() {
    local target_id="${OPT[id]:-$1}"
    if [ -z "$target_id" ] && [ "$JSON_MODE" -eq 0 ]; then
        list_web_apps
        read -p "ID de l'application à supprimer : " target_id
    fi
    [ -z "$target_id" ] && { emit_result err "ID requis."; return 1; }

    local line fqdn base
    line=$(grep -E "^${target_id}:" "$APPS_REGISTRY" | head -n1)
    fqdn=$(echo "$line" | cut -d: -f2)
    base=$(echo "$line" | cut -d: -f4)

    rm -f /etc/nginx/sites-enabled/"$target_id" /etc/nginx/sites-available/"$target_id"
    sed -i "/^${target_id}:/d" "$APPS_REGISTRY"
    [ -n "$fqdn" ] && [ -n "$base" ] && domain_is_local "$base" && hosts_remove "$fqdn"
    reload_nginx
    emit_result ok "Application '$target_id' supprimée."
}

# Installe la CA locale mkcert une seule fois (idempotent).
ensure_mkcert_ca() {
    if ! command -v mkcert >/dev/null 2>&1; then
        emit_result err "mkcert n'est pas installé (sudo apt install mkcert libnss3-tools)."
        return 1
    fi
    if [ ! -f "$CONFIG_DIR/.mkcert-ca-installed" ]; then
        mkcert -install >/dev/null 2>&1 || true
        mkdir -p "$CONFIG_DIR"; touch "$CONFIG_DIR/.mkcert-ca-installed"
    fi
    return 0
}

# SSL d'un domaine LOCAL via mkcert (certificat de confiance locale).
add_ssl_local() {
    local id=$1 domain=$2 root
    ensure_mkcert_ca || return 1
    root=$(grep -E "^${id}:" "$APPS_REGISTRY" | head -n1 | cut -d: -f3)
    [ -z "$root" ] && root="/var/www/html/$id"
    mkdir -p "$CERTS_DIR"
    local cert="$CERTS_DIR/${domain}.pem" key="$CERTS_DIR/${domain}-key.pem"
    if ! mkcert -cert-file "$cert" -key-file "$key" "$domain" >/dev/null 2>&1; then
        emit_result err "Échec de la génération du certificat mkcert pour $domain."
        return 1
    fi
    chmod 640 "$key" 2>/dev/null || true
    write_vhost_ssl "$id" "$domain" "$root" "$cert" "$key"
    ln -sf /etc/nginx/sites-available/"$id" /etc/nginx/sites-enabled/
    if ! reload_nginx; then
        emit_result err "Config Nginx invalide après ajout SSL local."
        return 1
    fi
    emit_result ok "SSL local (mkcert) activé pour https://$domain"
}

add_ssl_cert() {
    local target_id="${OPT[id]:-$1}"
    if [ -z "$target_id" ] && [ "$JSON_MODE" -eq 0 ]; then
        read -p "ID de l'application à sécuriser : " target_id
    fi
    [ -z "$target_id" ] && { emit_result err "ID requis."; return 1; }
    local line domain base
    line=$(grep -E "^${target_id}:" "$APPS_REGISTRY" | head -n1)
    domain=$(echo "$line" | cut -d: -f2)
    base=$(echo "$line" | cut -d: -f4)
    if [ -z "$domain" ]; then emit_result err "ID inconnu."; return 1; fi

    # Domaine local → mkcert ; domaine distant → Let's Encrypt (certbot).
    if [ -n "$base" ] && domain_is_local "$base"; then
        add_ssl_local "$target_id" "$domain"
        return
    fi

    local le_email="${OPT[email]}"
    if [ "$JSON_MODE" -eq 1 ]; then
        if [ -n "$le_email" ]; then
            certbot --nginx -d "$domain" --redirect --agree-tos --non-interactive -m "$le_email" >/dev/null 2>&1
        else
            certbot --nginx -d "$domain" --redirect --agree-tos --non-interactive --register-unsafely-without-email >/dev/null 2>&1
        fi
        [ $? -eq 0 ] && emit_result ok "SSL activé pour $domain." || emit_result err "Échec Certbot pour $domain."
        return
    fi
    [ -z "$le_email" ] && read -p "Email pour les alertes d'expiration (vide pour ignorer) : " le_email
    if [ -n "$le_email" ]; then
        certbot --nginx -d "$domain" --redirect --agree-tos --non-interactive -m "$le_email"
    else
        certbot --nginx -d "$domain" --redirect --agree-tos --non-interactive --register-unsafely-without-email
    fi
}

remove_ssl_cert() {
    local target_id="${OPT[id]:-$1}"
    if [ -z "$target_id" ] && [ "$JSON_MODE" -eq 0 ]; then
        read -p "ID de l'application : " target_id
    fi
    [ -z "$target_id" ] && { emit_result err "ID requis."; return 1; }
    local line domain base root
    line=$(grep -E "^${target_id}:" "$APPS_REGISTRY" | head -n1)
    domain=$(echo "$line" | cut -d: -f2)
    root=$(echo "$line" | cut -d: -f3)
    base=$(echo "$line" | cut -d: -f4)
    if [ -z "$domain" ]; then emit_result err "ID inconnu."; return 1; fi

    # Domaine local : supprimer les fichiers mkcert + rétablir le vhost HTTP.
    if [ -n "$base" ] && domain_is_local "$base"; then
        rm -f "$CERTS_DIR/${domain}.pem" "$CERTS_DIR/${domain}-key.pem"
        [ -z "$root" ] && root="/var/www/html/$target_id"
        write_vhost "$target_id" "$domain" "$root"
        ln -sf /etc/nginx/sites-available/"$target_id" /etc/nginx/sites-enabled/
        reload_nginx
        emit_result ok "SSL local retiré pour $domain (retour en HTTP)."
        return
    fi

    certbot delete --cert-name "$domain" --non-interactive >/dev/null 2>&1
    reload_nginx
    emit_result ok "SSL retiré pour $domain."
}

# ==============================================================================
# DIAGNOSTIC / STATUT
# ==============================================================================
stack_status() {
    if [ "$JSON_MODE" -eq 1 ]; then
        svc() { systemctl is-active --quiet "$1" 2>/dev/null && echo true || echo false; }
        local ng=false; nginx -t >/dev/null 2>&1 && ng=true
        # grep -c écrit toujours un nombre sur stdout mais renvoie 1 quand le
        # compte est 0 : on ne peut donc pas utiliser « || echo 0 » (double sortie).
        local apps_count domains_count
        apps_count=$(grep -cvE '^[[:space:]]*$' "$APPS_REGISTRY" 2>/dev/null); apps_count=${apps_count:-0}
        domains_count=$(grep -cvE '^[[:space:]]*$' "$DOMAINS_FILE" 2>/dev/null); domains_count=${domains_count:-0}
        printf '{"ok":true,"version":"%s","services":{"nginx":%s,"mariadb":%s,"php":%s},"nginx_config":%s,"apps":%s,"domains":%s}\n' \
            "$(json_escape "$VERSION")" "$(svc nginx)" "$(svc mariadb)" "$(svc "php${PHP_VERSION}-fpm")" \
            "$ng" "$apps_count" "$domains_count"
        return
    fi
    doctor
}

doctor() {
    echo -e "${BLUE}=== Diagnostic de la pile LNMP ===${NC}"
    echo -e "\n${YELLOW}Services :${NC}"
    for svc in nginx mariadb "php${PHP_VERSION}-fpm" lnmp-admin; do
        if systemctl is-active --quiet "$svc" 2>/dev/null; then
            echo -e "  ${GREEN}●${NC} $svc : actif"
        else
            echo -e "  ${RED}○${NC} $svc : inactif ou absent"
        fi
    done
    echo -e "\n${YELLOW}Configuration Nginx :${NC}"
    if nginx -t 2>/dev/null; then echo -e "  ${GREEN}●${NC} Syntaxe valide"; else echo -e "  ${RED}○${NC} Syntaxe invalide"; fi
    echo -e "\n${YELLOW}Domaines :${NC}"; domain_list
    echo -e "\n${YELLOW}Applications :${NC}"
    if [ -s "$APPS_REGISTRY" ]; then list_web_apps; else echo "  Aucune."; fi
    echo -e "\n${YELLOW}Certificats SSL (distants — Let's Encrypt) :${NC}"
    if command -v certbot >/dev/null 2>&1; then
        certbot certificates 2>/dev/null | grep -E "Certificate Name|Expiry Date" | sed 's/^/  /' || echo "  Aucun."
    else
        echo "  Certbot non installé."
    fi
    echo -e "\n${YELLOW}Certificats SSL (locaux — mkcert) :${NC}"
    if command -v mkcert >/dev/null 2>&1; then
        if ls "$CERTS_DIR"/*.pem >/dev/null 2>&1; then
            for c in "$CERTS_DIR"/*.pem; do
                case "$c" in *-key.pem) continue ;; esac
                echo "  ● $(basename "$c" .pem)"
            done
        else
            echo "  Aucun."
        fi
    else
        echo "  mkcert non installé (sudo apt install mkcert libnss3-tools)."
    fi
}

# ==============================================================================
# BASES DE DONNÉES (MariaDB)
# ==============================================================================
db_create() {
    local db_name="${OPT[name]:-$1}"
    if [ -z "$db_name" ] && [ "$JSON_MODE" -eq 0 ]; then read -p "Nom de la base à créer : " db_name; fi
    [ -z "$db_name" ] && { emit_result err "Nom de base requis."; return 1; }
    if ! validate_app_id "$db_name"; then emit_result err "Nom de base invalide."; return 1; fi

    local db_user="${db_name}_user" db_pass
    db_pass=$(openssl rand -base64 18 2>/dev/null | tr -d '/+=' | cut -c1-20)
    [ -z "$db_pass" ] && db_pass=$(head -c 15 /dev/urandom | base64 | tr -d '/+=' | cut -c1-20)

    if ! mariadb -e "CREATE DATABASE IF NOT EXISTS \`$db_name\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" 2>/dev/null; then
        emit_result err "Impossible de créer la base (MariaDB accessible ?)."; return 1
    fi
    mariadb -e "CREATE USER IF NOT EXISTS '$db_user'@'localhost' IDENTIFIED BY '$db_pass';" 2>/dev/null
    mariadb -e "GRANT ALL PRIVILEGES ON \`$db_name\`.* TO '$db_user'@'localhost';" 2>/dev/null
    mariadb -e "FLUSH PRIVILEGES;" 2>/dev/null

    if [ "$JSON_MODE" -eq 1 ]; then
        printf '{"ok":true,"database":"%s","user":"%s","password":"%s"}\n' \
            "$(json_escape "$db_name")" "$(json_escape "$db_user")" "$(json_escape "$db_pass")"
        return
    fi
    echo -e "${GREEN}Base de données créée.${NC}"
    echo -e "  Base        : ${BLUE}$db_name${NC}"
    echo -e "  Utilisateur : ${BLUE}$db_user${NC}"
    echo -e "  Mot de passe: ${YELLOW}$db_pass${NC}"
    echo -e "${RED}Notez ce mot de passe : il ne sera plus affiché.${NC}"
}

db_list() {
    if [ "$JSON_MODE" -eq 1 ]; then
        local first=1; printf '{"ok":true,"databases":['
        while read -r name; do
            [ -z "$name" ] && continue
            [ $first -eq 0 ] && printf ','
            printf '"%s"' "$(json_escape "$name")"; first=0
        done < <(mariadb -N -e "SHOW DATABASES;" 2>/dev/null | grep -vE '^(information_schema|performance_schema|mysql|sys)$')
        printf ']}\n'; return
    fi
    echo -e "${BLUE}=== Bases de données ===${NC}"
    mariadb -e "SHOW DATABASES;" 2>/dev/null | grep -vE '^(Database|information_schema|performance_schema|mysql|sys)$' \
        || echo -e "${RED}MariaDB inaccessible.${NC}"
}

db_drop() {
    local db_name="${OPT[name]:-$1}"
    if [ -z "$db_name" ] && [ "$JSON_MODE" -eq 0 ]; then db_list; read -p "Nom de la base à supprimer : " db_name; fi
    [ -z "$db_name" ] && { emit_result err "Nom de base requis."; return 1; }
    if ! validate_app_id "$db_name"; then emit_result err "Nom de base invalide."; return 1; fi
    if [ "$JSON_MODE" -eq 0 ] && [ -z "${OPT[yes]}" ]; then
        read -p "Supprimer DÉFINITIVEMENT la base '$db_name' et son utilisateur ? (y/n) : " confirm
        [[ "$confirm" =~ ^[Yy]$ ]] || return 1
    fi
    mariadb -e "DROP DATABASE IF EXISTS \`$db_name\`;" 2>/dev/null
    mariadb -e "DROP USER IF EXISTS '${db_name}_user'@'localhost';" 2>/dev/null
    mariadb -e "FLUSH PRIVILEGES;" 2>/dev/null
    emit_result ok "Base '$db_name' supprimée."
}

# ==============================================================================
# INTERFACE WEB D'ADMINISTRATION
# ==============================================================================
# Renvoie l'IP à écrire dans /etc/hosts pour le nom d'hôte du panneau.
admin_hosts_ip() {
    case "$ADMIN_LISTEN" in
        127.0.0.1|0.0.0.0|"") echo "127.0.0.1" ;;
        *)                    echo "$ADMIN_LISTEN" ;;
    esac
}

# Demande le lien (nom d'hôte) et l'interface d'écoute du panneau web.
configure_admin_access() {
    # Non interactif si --json ou si au moins une option est fournie.
    if [ "$JSON_MODE" -eq 1 ] || [ -n "${OPT[admin-host]}" ] || [ -n "${OPT[listen]}" ]; then
        [ -n "${OPT[admin-host]}" ] && ADMIN_HOST="${OPT[admin-host]}"
        [ -n "${OPT[listen]}" ]     && ADMIN_LISTEN="${OPT[listen]}"
        write_config
        return 0
    fi
    echo -e "${BLUE}--- Accès au panneau web d'administration ---${NC}"
    local h
    read -p "Nom d'hôte pour le lien [${ADMIN_HOST}] : " h
    h=$(echo "$h" | tr -d ' ')
    if [ -n "$h" ]; then
        if validate_domain "$h"; then ADMIN_HOST="$h"
        else echo -e "${RED}Nom d'hôte invalide, valeur conservée : ${ADMIN_HOST}${NC}"; fi
    fi

    echo "Sur quelle interface le panneau doit-il être disponible ?"
    echo "  1) 127.0.0.1  — localhost uniquement (recommandé ; accès distant par tunnel SSH)"
    echo "  2) 0.0.0.0    — toutes les interfaces (accessible depuis le réseau) [!]"
    echo "  3) une IP précise de ce serveur"
    local ips; ips=$(ip -o -4 addr show scope global 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | paste -sd', ' -)
    [ -n "$ips" ] && echo -e "     IP disponibles : ${YELLOW}${ips}${NC}"
    local c
    read -p "Choix [1] : " c
    case "$c" in
        2) ADMIN_LISTEN="0.0.0.0"
           echo -e "${YELLOW}[!] Le panneau sera joignable depuis le réseau : seul le mot de passe le protège. Préférez un tunnel SSH ou du HTTPS + accès restreint.${NC}" ;;
        3) local ip; read -p "     IP à utiliser : " ip; ip=$(echo "$ip" | tr -d ' ')
           if [[ "$ip" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]]; then ADMIN_LISTEN="$ip"
           else echo -e "${RED}IP invalide, retour à 127.0.0.1${NC}"; ADMIN_LISTEN="127.0.0.1"; fi ;;
        *) ADMIN_LISTEN="127.0.0.1" ;;
    esac
    write_config
}

write_admin_vhost() {
    local note="lié à ${ADMIN_LISTEN}"
    [ "$ADMIN_LISTEN" = "127.0.0.1" ] && note="lié à 127.0.0.1 (accès distant via tunnel SSH)"
    cat > /etc/nginx/sites-available/admin.lnmp << EOF
# Panneau d'administration LNMP — ${note}.
# Le démon lnmpd reste toujours sur 127.0.0.1 ; nginx relaie /api/ vers lui.
server {
    listen ${ADMIN_LISTEN}:80;
    server_name ${ADMIN_HOST};
    root ${WEBUI_DEST};
    index index.html;
    charset utf-8;

    location /api/ {
        proxy_pass http://127.0.0.1:${ADMIN_PORT};
        proxy_http_version 1.1;
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
    }
    location / { try_files \$uri \$uri/ /index.html; }
}
EOF
}

# Définit (ou réinitialise) le mot de passe du panneau web.
set_admin_password() {
    local pass="${OPT[password]:-$1}"
    if [ -z "$pass" ] && [ "$JSON_MODE" -eq 0 ]; then
        read -r -s -p "Mot de passe admin du panneau web : " pass; echo
        local pass2; read -r -s -p "Confirmez le mot de passe : " pass2; echo
        [ "$pass" != "$pass2" ] && { emit_result err "Les mots de passe ne correspondent pas."; return 1; }
    fi
    [ -z "$pass" ] && { emit_result err "Mot de passe vide."; return 1; }

    local hash secret
    hash=$(python3 -c 'import hashlib,os,sys; s=os.urandom(16); i=200000; dk=hashlib.pbkdf2_hmac("sha256", sys.argv[1].encode(), s, i); print("pbkdf2_sha256$%d$%s$%s" % (i, s.hex(), dk.hex()))' "$pass")
    secret=$(grep -E '^SESSION_SECRET=' "$ADMIN_CONF" 2>/dev/null | cut -d'"' -f2)
    [ -z "$secret" ] && secret=$(python3 -c 'import secrets; print(secrets.token_hex(32))')

    cat > "$ADMIN_CONF" << EOF
PASSWORD_HASH="$hash"
SESSION_SECRET="$secret"
EOF
    chmod 600 "$ADMIN_CONF"
    emit_result ok "Mot de passe admin défini."
}

install_web_admin() {
    echo -e "${YELLOW}[8/8] Interface web d'administration...${NC}"

    # 1. Choix du lien (nom d'hôte) et de l'interface d'écoute
    configure_admin_access

    # 2. Déployer le panneau statique
    mkdir -p "$WEBUI_DEST"
    [ -d "$WEBUI_SRC" ] && cp -a "$WEBUI_SRC/." "$WEBUI_DEST/"
    chown -R www-data:webdev "$WEBUI_DEST" 2>/dev/null || true

    # 3. Mot de passe admin (si absent)
    if [ ! -f "$ADMIN_CONF" ]; then
        echo -e "${BLUE}Définissez le mot de passe du panneau web (http://${ADMIN_HOST})${NC}"
        set_admin_password
    fi

    # 4. Vhost admin + /etc/hosts (le nom d'hôte pointe vers l'IP d'écoute)
    write_admin_vhost
    ln -sf /etc/nginx/sites-available/admin.lnmp /etc/nginx/sites-enabled/
    hosts_remove "$ADMIN_HOST"
    hosts_add "$ADMIN_HOST" "$(admin_hosts_ip)"

    # 5. Démon d'administration
    systemctl enable --now lnmp-admin 2>/dev/null

    reload_nginx
    echo -e "${GREEN}Panneau web prêt : http://${ADMIN_HOST}  (écoute ${ADMIN_LISTEN}:80)${NC}"
}

# Reconfigure le lien / l'interface du panneau après l'installation.
reconfigure_admin() {
    if [ ! -f "$WEBUI_DEST/index.html" ]; then
        emit_result err "Le panneau n'est pas installé (lancez 'lnmp install')."; return 1
    fi
    configure_admin_access
    write_admin_vhost
    ln -sf /etc/nginx/sites-available/admin.lnmp /etc/nginx/sites-enabled/
    hosts_remove "$ADMIN_HOST"
    hosts_add "$ADMIN_HOST" "$(admin_hosts_ip)"
    if ! reload_nginx; then emit_result err "Config Nginx invalide, changement non appliqué."; return 1; fi
    emit_result ok "Panneau : http://${ADMIN_HOST} (écoute ${ADMIN_LISTEN}:80)"
}

# ==============================================================================
# INSTALLATION / DÉSINSTALLATION
# ==============================================================================

# Aligne PHP_VERSION sur la version de PHP réellement installée (socket FPM).
detect_php_version() {
    local v
    v=$(ls -1 /etc/php 2>/dev/null | grep -E '^[0-9]+\.[0-9]+$' | sort -V | tail -n1)
    [ -n "$v" ] && PHP_VERSION="$v"
}

# Configuration de la pile SANS apt (les paquets sont fournis par les
# dépendances du paquet). Idempotent et non interactif : utilisé par le
# postinst du paquet pour un « apt install lnmp » complet.
configure_stack() {
    detect_php_version
    echo "-> Configuration de la pile LNMP..."

    # Domaine par défaut (non interactif) si absent
    if [ -z "$DEFAULT_DOMAIN" ]; then
        DEFAULT_DOMAIN="localhost"
        grep -qiE '^localhost:' "$DOMAINS_FILE" 2>/dev/null || echo "localhost:remote" >> "$DOMAINS_FILE"
        write_config
    fi

    # Groupe webdev + droits sur /var/www/html (idempotent)
    getent group webdev >/dev/null || groupadd webdev
    [ -n "$REAL_USER" ] && [ "$REAL_USER" != "root" ] && usermod -aG webdev "$REAL_USER" 2>/dev/null || true
    id -u www-data >/dev/null 2>&1 && usermod -aG webdev www-data 2>/dev/null || true
    mkdir -p /var/www/html
    chown -R www-data:webdev /var/www/html 2>/dev/null || true
    find /var/www/html -type d -exec chmod g+s {} \; 2>/dev/null || true
    chmod -R 775 /var/www/html 2>/dev/null || true
    if command -v setfacl >/dev/null 2>&1; then
        setfacl -R -d -m g:webdev:rwx /var/www/html 2>/dev/null || true
        setfacl -R -d -m u:www-data:rwx /var/www/html 2>/dev/null || true
    fi

    # Désactive le vhost par défaut livré par Nginx (aucune app témoin créée)
    [ -f /etc/nginx/sites-enabled/default ] && rm -f /etc/nginx/sites-enabled/default

    # Services de la pile
    systemctl enable --now nginx 2>/dev/null || true
    systemctl enable --now mariadb 2>/dev/null || true
    systemctl enable --now "php${PHP_VERSION}-fpm" 2>/dev/null || true

    # Interface web d'administration (valeurs par défaut/config, sans prompt)
    mkdir -p "$WEBUI_DEST"
    [ -d "$WEBUI_SRC" ] && cp -a "$WEBUI_SRC/." "$WEBUI_DEST/"
    chown -R www-data:webdev "$WEBUI_DEST" 2>/dev/null || true
    write_admin_vhost
    ln -sf /etc/nginx/sites-available/admin.lnmp /etc/nginx/sites-enabled/ 2>/dev/null || true
    hosts_remove "$ADMIN_HOST"
    hosts_add "$ADMIN_HOST" "$(admin_hosts_ip)"
    systemctl enable --now lnmp-admin 2>/dev/null || true

    reload_nginx 2>/dev/null || true
}

install_lnmp() {
    local current_step; current_step=$(get_state)
    echo -e "${BLUE}=== Installation de la pile LNMP ===${NC}"

    ensure_default_domain

    if [ "$current_step" -le 1 ]; then
        echo -e "${YELLOW}[1/7] Système et 'acl'...${NC}"
        apt-get update -y && apt-get upgrade -y
        apt-get install -y acl software-properties-common curl wget unzip git
        set_state 1
    fi
    if [ "$current_step" -le 2 ]; then
        echo -e "${YELLOW}[2/7] Nginx...${NC}"
        apt-get install -y nginx nginx-common
        # Auto-réparation : si nginx.conf est absent (état dpkg incohérent hérité
        # d'une ancienne désinstallation), forcer la restauration des fichiers de
        # configuration manquants du paquet nginx-common.
        if [ ! -f /etc/nginx/nginx.conf ]; then
            echo -e "${YELLOW}   -> nginx.conf manquant, restauration des fichiers de conf...${NC}"
            apt-get install -y --reinstall -o 'Dpkg::Options::=--force-confmiss' nginx-common
        fi
        systemctl enable --now nginx
        set_state 2
    fi
    if [ "$current_step" -le 3 ]; then
        echo -e "${YELLOW}[3/7] MariaDB...${NC}"
        apt-get install -y mariadb-server mariadb-client
        systemctl enable --now mariadb
        set_state 3
    fi
    if [ "$current_step" -le 4 ]; then
        echo -e "${YELLOW}[4/7] PHP ${PHP_VERSION}...${NC}"
        add-apt-repository ppa:ondrej/php -y
        apt-get update -y
        apt-get install -y \
            php${PHP_VERSION}-fpm php${PHP_VERSION}-cli php${PHP_VERSION}-mysql \
            php${PHP_VERSION}-curl php${PHP_VERSION}-xml php${PHP_VERSION}-mbstring \
            php${PHP_VERSION}-zip php${PHP_VERSION}-bcmath php${PHP_VERSION}-soap \
            php${PHP_VERSION}-intl php${PHP_VERSION}-readline php${PHP_VERSION}-sqlite3 \
            php${PHP_VERSION}-gd php${PHP_VERSION}-redis
        systemctl enable --now php${PHP_VERSION}-fpm
        set_state 4
    fi
    if [ "$current_step" -le 5 ]; then
        echo -e "${YELLOW}[5/7] Certbot...${NC}"
        apt-get install -y certbot python3-certbot-nginx
        set_state 5
    fi
    if [ "$current_step" -le 6 ]; then
        echo -e "${YELLOW}[6/7] Droits et groupe webdev...${NC}"
        getent group webdev >/dev/null || groupadd webdev
        if [ -n "$REAL_USER" ] && [ "$REAL_USER" != "root" ]; then usermod -aG webdev "$REAL_USER"; fi
        usermod -aG webdev www-data
        mkdir -p /var/www/html
        chown -R www-data:webdev /var/www/html
        find /var/www/html -type d -exec chmod g+s {} \;
        chmod -R 775 /var/www/html
        setfacl -R -d -m g:webdev:rwx /var/www/html
        setfacl -R -d -m u:www-data:rwx /var/www/html
        set_state 6
    fi
    if [ "$current_step" -le 7 ]; then
        echo -e "${YELLOW}[7/8] Nettoyage du vhost par défaut...${NC}"
        [ -f /etc/nginx/sites-enabled/default ] && rm -f /etc/nginx/sites-enabled/default
        reload_nginx
        set_state 7
    fi
    if [ "$current_step" -le 8 ]; then
        install_web_admin
        set_state 8
    fi

    echo -e "${GREEN}=== LNMP installée ===${NC}"
    echo -e "Domaine par défaut : ${BLUE}${DEFAULT_DOMAIN}${NC}"
    echo -e "Panneau web        : ${BLUE}http://${ADMIN_HOST}${NC} (ajoutez '127.0.0.1 ${ADMIN_HOST}' à votre poste, ou tunnel SSH)"
}

uninstall_lnmp() {
    # Désinstallation SÉLECTIVE.
    # Par défaut, seul le service web d'administration LNMP est retiré ; la pile
    # (Nginx, MariaDB, PHP) et les applications sont conservées — ce qui évite de
    # casser /etc/nginx et de provoquer l'erreur « nginx.conf: No such file » à
    # la réinstallation.
    local rm_nginx=0 rm_mariadb=0 rm_php=0 rm_apps=0

    if [ "$JSON_MODE" -eq 1 ]; then
        [ -n "${OPT[nginx]}" ]   && rm_nginx=1
        [ -n "${OPT[mariadb]}" ] && rm_mariadb=1
        [ -n "${OPT[php]}" ]     && rm_php=1
        [ -n "${OPT[apps]}" ]    && rm_apps=1
    elif [ -z "${OPT[yes]}" ]; then
        echo -e "${BLUE}=== Désinstallation LNMP ===${NC}"
        echo -e "Le ${YELLOW}service web d'administration${NC} de LNMP va être retiré."
        echo -e "Indiquez ce qu'il faut supprimer ${YELLOW}en plus${NC} (défaut : non) :"
        local a
        read -p "  Retirer Nginx ?                                  (y/N) : " a; [[ "$a" =~ ^[Yy]$ ]] && rm_nginx=1
        read -p "  Retirer MariaDB ?                                (y/N) : " a; [[ "$a" =~ ^[Yy]$ ]] && rm_mariadb=1
        read -p "  Retirer PHP-FPM ?                                (y/N) : " a; [[ "$a" =~ ^[Yy]$ ]] && rm_php=1
        read -p "  Supprimer les applications et la config LNMP ?   (y/N) : " a; [[ "$a" =~ ^[Yy]$ ]] && rm_apps=1
    fi

    # 1. Toujours : retrait du service web d'administration LNMP
    echo -e "${YELLOW}-> Retrait du service web d'administration LNMP...${NC}"
    systemctl disable --now lnmp-admin 2>/dev/null || true
    rm -f /etc/nginx/sites-enabled/admin.lnmp /etc/nginx/sites-available/admin.lnmp
    rm -rf "$WEBUI_DEST"
    hosts_remove "$ADMIN_HOST"
    rm -f "$ADMIN_CONF"

    # 2. Applications + configuration LNMP (optionnel)
    if [ "$rm_apps" -eq 1 ]; then
        echo -e "${YELLOW}-> Suppression des applications et de la configuration LNMP...${NC}"
        if [ -f "$APPS_REGISTRY" ]; then
            while IFS=: read -r id fqdn path base; do
                [ -z "$id" ] && continue
                rm -f /etc/nginx/sites-enabled/"$id" /etc/nginx/sites-available/"$id"
                [ -n "$fqdn" ] && [ -n "$base" ] && domain_is_local "$base" && hosts_remove "$fqdn"
            done < "$APPS_REGISTRY"
        fi
        rm -f "$APPS_REGISTRY" "$STATE_FILE"
        rm -rf "$CONFIG_DIR"
        sed -i '/#[[:space:]]*lnmp[[:space:]]*$/d' "$HOSTS_FILE" 2>/dev/null || true
    fi

    # 3. Composants de la pile (optionnels — conservés par défaut)
    local pkgs=()
    [ "$rm_nginx" -eq 1 ]   && pkgs+=(nginx nginx-common nginx-core)
    [ "$rm_mariadb" -eq 1 ] && pkgs+=(mariadb-server mariadb-client)
    [ "$rm_php" -eq 1 ]     && pkgs+=("php${PHP_VERSION}*")
    if [ "${#pkgs[@]}" -gt 0 ]; then
        echo -e "${YELLOW}-> Suppression des paquets : ${pkgs[*]}${NC}"
        [ "$rm_nginx" -eq 1 ]   && systemctl stop nginx 2>/dev/null || true
        [ "$rm_mariadb" -eq 1 ] && systemctl stop mariadb 2>/dev/null || true
        [ "$rm_php" -eq 1 ]     && systemctl stop "php${PHP_VERSION}-fpm" 2>/dev/null || true
        apt-get purge -y "${pkgs[@]}"
        apt-get autoremove -y
        # Purger nginx-common assure qu'une future réinstallation recrée nginx.conf.
        [ "$rm_nginx" -eq 1 ] && rm -rf /etc/nginx
    fi

    # Recharger Nginx s'il est conservé (pour prendre en compte le retrait du vhost admin).
    if [ "$rm_nginx" -eq 0 ] && command -v nginx >/dev/null 2>&1 && [ -f /etc/nginx/nginx.conf ]; then
        reload_nginx
    fi

    # Retrait de l'application lnmp elle-même (le paquet). En dernier : le
    # processus bash en cours garde un descripteur ouvert sur /usr/bin/lnmp,
    # donc la suppression du fichier ne l'interrompt pas. Le postrm du paquet
    # nettoie de son côté le service web (idempotent).
    echo -e "${YELLOW}-> Suppression du paquet lnmp...${NC}"
    if command -v apt-get >/dev/null 2>&1; then
        apt-get purge -y lnmp >/dev/null 2>&1 || dpkg --purge lnmp >/dev/null 2>&1 || true
    else
        dpkg --purge lnmp >/dev/null 2>&1 || true
    fi

    emit_result ok "Désinstallation terminée — lnmp a été supprimé."
    [ "$JSON_MODE" -eq 0 ] && exit 0
}

# ==============================================================================
# AIDE / VERSION
# ==============================================================================
show_version() { echo -e "${GREEN}LNMP CLI Manager - Version $VERSION${NC}"; }

show_help() {
    cat << EOF
Usage : lnmp [COMMANDE] [OPTIONS]

Options générales :
  -h, --help            Afficher cette aide
  -v, --version         Afficher la version
  --json                Sortie JSON (pour scripts / démon web)

Global :
  install               Installer / reprendre l'installation de la pile
  uninstall             Désinstaller (sélectif : par défaut, seul le service
                        web LNMP ; --nginx/--mariadb/--php/--apps pour plus)
  doctor                Diagnostiquer l'état de la pile
  status                État synthétique (utile avec --json)

Applications :
  list                  Lister les applications
  add                   Ajouter une application
                        (--id, --domain, --sub, --apex, --path)
  enable  <id>          Activer une application
  disable <id>          Désactiver une application
  delete  <id>          Supprimer une application
  add-ssl <id>          Générer un certificat SSL — Let's Encrypt (distant,
                        --email) ou mkcert (domaine local, automatique)
  remove-ssl <id>       Retirer un certificat SSL
  reload-app            Reconstruire le registre depuis Nginx

Domaines :
  domain list           Lister les domaines de base
  domain add <nom>      Ajouter un domaine (--local pour du local)
  domain remove <nom>   Retirer un domaine

Bases de données :
  db-create <nom>       Créer une base + utilisateur
  db-list               Lister les bases
  db-drop <nom>         Supprimer une base (--yes pour ne pas confirmer)

Interface web :
  set-password          Définir le mot de passe du panneau
  admin-url             Changer le lien et l'interface d'écoute du panneau
                        (--admin-host <nom>, --listen <127.0.0.1|0.0.0.0|IP>)

Sans commande : menu interactif.
EOF
}

# ==============================================================================
# ROUTAGE DES COMMANDES DIRECTES
# ==============================================================================
if [ $# -gt 0 ]; then
    cmd="$1"; arg="$2"
    case "$cmd" in
        help)        show_help; exit 0 ;;
        version)     show_version; exit 0 ;;
        install)     install_lnmp; exit 0 ;;
        uninstall)   uninstall_lnmp; exit $? ;;
        _postinstall) # appelé par le postinst du paquet : configure la pile
                      # (paquets déjà fournis par les dépendances), sans prompt.
                      configure_stack
                      echo "LNMP configuré. Panneau : http://${ADMIN_HOST} (écoute ${ADMIN_LISTEN}:80)"
                      echo "Définissez le mot de passe du panneau : sudo lnmp set-password"
                      exit 0 ;;
        doctor)      doctor; exit 0 ;;
        status)      stack_status; exit 0 ;;
        list)        list_web_apps; exit 0 ;;
        add)         add_web_app; exit $? ;;
        enable)      enable_web_app "$arg"; exit $? ;;
        disable)     disable_web_app "$arg"; exit $? ;;
        delete)      delete_web_app "$arg"; exit $? ;;
        add-ssl)     add_ssl_cert "$arg"; exit $? ;;
        remove-ssl)  remove_ssl_cert "$arg"; exit $? ;;
        reload-app)  reload_app; exit $? ;;
        domain)
            dtype="remote"; [ -n "${OPT[local]}" ] && dtype="local"
            case "$arg" in
                list)   domain_list ;;
                add)    domain_add "${OPT[name]:-$3}" "$dtype" ;;
                remove) domain_remove "${OPT[name]:-$3}" ;;
                *)      emit_result err "Sous-commande : list|add|remove." ;;
            esac
            exit $? ;;
        db-create)   db_create "$arg"; exit $? ;;
        db-list)     db_list; exit $? ;;
        db-drop)     db_drop "$arg"; exit $? ;;
        set-password) set_admin_password "$arg"; exit $? ;;
        admin-url)    reconfigure_admin; exit $? ;;
        *)           emit_result err "Commande inconnue : $cmd"; [ "$JSON_MODE" -eq 0 ] && show_help; exit 1 ;;
    esac
fi

# ==============================================================================
# MENU INTERACTIF
# ==============================================================================
while true; do
    clear
    echo -e "${BLUE}=====================================================${NC}"
    echo -e "${GREEN}             GESTIONNAIRE LNMP AUTOMATIQUE            ${NC}"
    echo -e "${BLUE}=====================================================${NC}"
    echo " 1 - Installer / Reprendre l'installation LNMP"
    echo " 2 - Lister les applications web"
    echo " 3 - Ajouter une application web"
    echo " 4 - Supprimer une application web"
    echo " 5 - Ajouter un certificat SSL à une application"
    echo " 6 - Retirer un certificat SSL à une application"
    echo " 7 - Désinstaller LNMP (sélectif — service web par défaut)"
    echo " 8 - Activer une application (Link vhost)"
    echo " 9 - Désactiver une application (Unlink vhost)"
    echo "10 - Créer une base de données"
    echo "11 - Lister les bases de données"
    echo "12 - Supprimer une base de données"
    echo "13 - Lister les domaines"
    echo "14 - Ajouter un domaine"
    echo "15 - Retirer un domaine"
    echo "16 - Changer le mot de passe du panneau web"
    echo " D - Diagnostic de la pile (doctor)"
    echo " R - Reconstruire le registre (reload-app)"
    echo " 0 - Quitter"
    echo -e "${BLUE}=====================================================${NC}"
    read -p "Choisissez une option : " choice

    case $choice in
        1)  install_lnmp ;;
        2)  echo -e "${BLUE}=== Registre ===${NC}"; list_web_apps ;;
        3)  add_web_app ;;
        4)  delete_web_app ;;
        5)  add_ssl_cert ;;
        6)  remove_ssl_cert ;;
        7)  uninstall_lnmp ;;
        8)  enable_web_app ;;
        9)  disable_web_app ;;
        10) db_create ;;
        11) db_list ;;
        12) db_drop ;;
        13) domain_list ;;
        14) domain_add_interactive ;;
        15) read -p "Nom du domaine à retirer : " dn; domain_remove "$dn" ;;
        16) set_admin_password ;;
        [Dd]) doctor ;;
        [Rr]) reload_app ;;
        0)  exit 0 ;;
        *)  echo -e "${RED}Option invalide.${NC}" ;;
    esac
    echo -e "\nAppuyez sur [ENTRÉE] pour continuer..."
    read
done
