#!/usr/bin/env bash
# =============================================================================
#  displaypro-setup.sh — transforme un Debian minimal ou un Raspberry Pi OS Lite
#  en écran d'affichage dynamique « OnDiffuse » (appliance kiosque).
#  (Noms techniques internes inchangés depuis la v1.0 « DisplayPro » : utilisateur
#  displaypro, /etc/displaypro, service displaypro-kiosque, commande displaypro —
#  alias « ondiffuse ».)
#
#  Source de vérité unique : ce script est utilisé tel quel
#    - par l'ISO d'installation automatique (late_command du preseed),
#    - par la procédure Raspberry Pi (« curl ... | sudo bash »),
#    - pour mettre à jour un écran déjà installé (il est rejouable sans risque).
#
#  Ce qu'il met en place :
#    - utilisateur dédié « displaypro » (sans mot de passe de connexion) ;
#    - compositeur Wayland kiosque « cage » + Chromium, lancés par une unité
#      systemd sur tty1 (aucun gestionnaire de bureau), relance automatique ;
#    - configuration dans /etc/displaypro/displaypro.conf (URL, rotation...) ;
#    - commande d'administration « displaypro » ;
#    - mises à jour de sécurité automatiques + redémarrage nocturne si besoin ;
#    - watchdog matériel, journald limité, NTP, fuseau, pas de mise en veille,
#      démarrage silencieux, NetworkManager (Ethernet sans rien faire, Wi-Fi
#      par nmtui, « displaypro wifi » ou fichier sur clé USB) ;
#    - SSH désactivé par défaut (activable avec une clé publique).
#
#  Usage :
#    sudo bash displaypro-setup.sh [options]
#      --url ADRESSE        adresse ouverte par l'écran (défaut : page d'appairage)
#      --cle-ssh FICHIER    active SSH (root, par clé uniquement) avec ces clés
#      --rotation N         0, 90, 180 ou 270
#      --fuseau ZONE        ex. Europe/Paris (défaut)
#      --installateur       appelé par l'installateur Debian (chroot, pas de démarrage)
#      --demarrer           démarre le kiosque tout de suite à la fin (sinon : redémarrer)
#      --aide
# =============================================================================
set -euo pipefail

DISPLAYPRO_VERSION="1.2"
URL_DEFAUT="https://ondiffuse.fr/app/pair"
# Ancienne adresse par défaut (v1.0) : migrée automatiquement si elle n'a pas été personnalisée
URL_ANCIENNE_DEFAUT="https://vozit.dev/displaypro-vozit/app/pair"
UTILISATEUR="displaypro"
DOSSIER_UTILISATEUR="/var/lib/displaypro"
CONF="/etc/displaypro/displaypro.conf"
JOURNAL_INSTALL="/var/log/displaypro-installation.log"

# ----------------------------------------------------------------------------
# Options
# ----------------------------------------------------------------------------
OPT_URL="" ; OPT_CLE_SSH="" ; OPT_ROTATION="" ; OPT_FUSEAU="" ; INSTALLATEUR=0 ; DEMARRER=0
while [[ $# -gt 0 ]]; do
  case "$1" in
    --url)          OPT_URL="${2:-}"; shift 2 ;;
    --cle-ssh)      OPT_CLE_SSH="${2:-}"; shift 2 ;;
    --rotation)     OPT_ROTATION="${2:-}"; shift 2 ;;
    --fuseau)       OPT_FUSEAU="${2:-}"; shift 2 ;;
    --installateur) INSTALLATEUR=1; shift ;;
    --demarrer)     DEMARRER=1; shift ;;
    -h|--aide|--help) sed -n '2,35p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
    *) echo "Option inconnue : $1 (voir --aide)" >&2; exit 2 ;;
  esac
done

info()  { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
avert() { printf '\033[1;33m/!\\\033[0m %s\n' "$*" >&2; }
echec() { printf '\033[1;31mERREUR :\033[0m %s\n' "$*" >&2; exit 1; }

[[ $EUID -eq 0 ]] || echec "à lancer en root (sudo bash $0)."

# Tout est aussi consigné dans un journal (utile pour l'installation automatique)
mkdir -p "$(dirname "$JOURNAL_INSTALL")"
exec > >(tee -a "$JOURNAL_INSTALL") 2>&1
info "OnDiffuse ${DISPLAYPRO_VERSION} — installation lancée le $(date '+%d/%m/%Y %H:%M:%S')"

# ----------------------------------------------------------------------------
# Détection de la plateforme
# ----------------------------------------------------------------------------
. /etc/os-release
EST_PI=0
if grep -qi "raspberry pi" /proc/device-tree/model 2>/dev/null; then EST_PI=1; fi
case "${ID:-}" in
  debian|raspbian) ;;
  *) avert "Système « ${PRETTY_NAME:-inconnu} » non prévu (Debian ou Raspberry Pi OS attendus) : on continue." ;;
esac
# Système en fonctionnement (systemd actif) ou chroot de l'installateur ?
if [[ -d /run/systemd/system ]] && [[ $INSTALLATEUR -eq 0 ]]; then EN_DIRECT=1; else EN_DIRECT=0; fi
ARCH="$(dpkg --print-architecture)"
info "Plateforme : ${PRETTY_NAME:-?} (${ARCH}), Raspberry Pi : $([[ $EST_PI -eq 1 ]] && echo oui || echo non), système actif : $([[ $EN_DIRECT -eq 1 ]] && echo oui || echo 'non (installateur)')"

# SSH : si le script est lancé depuis une session SSH, on ne coupe pas la branche
# sur laquelle l'administrateur est assis.
LANCE_PAR_SSH=0
[[ -n "${SSH_CONNECTION:-}" ]] && LANCE_PAR_SSH=1

# ----------------------------------------------------------------------------
# Paquets
# ----------------------------------------------------------------------------
export DEBIAN_FRONTEND=noninteractive
APT_OPTS=(-y -q -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold --no-install-recommends)

# Ajoute le composant non-free-firmware aux sources Debian s'il manque (pilotes
# réseau/graphiques des mini-PC). Rien à faire sur Raspberry Pi OS.
if [[ "${ID:-}" == "debian" && $EST_PI -eq 0 ]]; then
  if [[ -f /etc/apt/sources.list ]]; then
    sed -i -E '/^deb(-src)? .*debian(-security)?\/? .* main/{/non-free-firmware/! s/$/ non-free-firmware/}' /etc/apt/sources.list
  fi
  for f in /etc/apt/sources.list.d/*.sources; do
    [[ -f "$f" ]] || continue
    if grep -q 'debian' "$f"; then sed -i -E '/^Components:/{/non-free-firmware/! s/$/ non-free-firmware/}' "$f"; fi
  done
fi

info "Mise à jour de la liste des paquets..."
apt-get update -q || avert "apt-get update a signalé une erreur (on continue)."

# N'installe que les paquets qui existent dans les dépôts de cette machine
disponibles() {
  local p c out=()
  for p in "$@"; do
    c="$(LC_ALL=C apt-cache policy "$p" 2>/dev/null | awk '/Candidate:/{print $2}')"
    [[ -n "$c" && "$c" != "(none)" ]] && out+=("$p")
  done
  echo "${out[@]}"
}

# Navigateur : « chromium » (Debian et Raspberry Pi OS récents) ou « chromium-browser » (ancien RPi OS)
if [[ -n "$(disponibles chromium)" ]]; then NAVIGATEUR_PAQUET=chromium; else NAVIGATEUR_PAQUET=chromium-browser; fi

PAQUETS=(
  cage wlr-randr grim "$NAVIGATEUR_PAQUET"
  dbus dbus-user-session libpam-systemd
  fonts-dejavu-core fonts-liberation2 fonts-noto-color-emoji
  libgl1-mesa-dri libegl-mesa0
  pipewire pipewire-pulse pipewire-alsa wireplumber
  network-manager wpasupplicant rfkill iw
  systemd-timesyncd unattended-upgrades ca-certificates curl
  openssh-server locales console-setup keyboard-configuration kbd
)
PAQUETS_OPTIONNELS=(chromium-sandbox chromium-l10n fonts-noto-core xdg-utils)
if [[ $EST_PI -eq 0 && "$ARCH" == "amd64" ]]; then
  # Micrologiciels non libres : cartes réseau, Wi-Fi, graphiques des mini-PC
  PAQUETS_OPTIONNELS+=(firmware-linux-nonfree firmware-misc-nonfree firmware-intel-graphics
    firmware-amd-graphics firmware-realtek firmware-iwlwifi firmware-atheros firmware-mediatek
    firmware-intel-sound firmware-sof-signed intel-microcode amd64-microcode
    plymouth plymouth-label)
fi

info "Installation des paquets (navigateur : ${NAVIGATEUR_PAQUET})..."
# shellcheck disable=SC2046
apt-get install "${APT_OPTS[@]}" $(disponibles "${PAQUETS[@]}") || echec "installation des paquets impossible (réseau ? dépôts ?)."
# shellcheck disable=SC2046
apt-get install "${APT_OPTS[@]}" $(disponibles "${PAQUETS_OPTIONNELS[@]}") || avert "certains paquets optionnels n'ont pas pu être installés."

NAVIGATEUR="$(command -v chromium || command -v chromium-browser || true)"
[[ -n "$NAVIGATEUR" ]] || echec "Chromium introuvable après installation."
command -v cage >/dev/null || echec "cage introuvable après installation."

# ----------------------------------------------------------------------------
# Configuration /etc/displaypro/displaypro.conf (conservée si elle existe)
# ----------------------------------------------------------------------------
mkdir -p /etc/displaypro
if [[ ! -f "$CONF" ]]; then
  info "Création de ${CONF}"
  SSH_INITIAL="non"
  [[ $LANCE_PAR_SSH -eq 1 ]] && SSH_INITIAL="oui"
  cat > "$CONF" <<EOF
# =====================================================================
#  Configuration de l'écran OnDiffuse
#  Après modification : « displaypro appliquer » (ou redémarrer l'écran).
#  Valeurs « oui » / « non » en minuscules. Guillemets obligatoires.
# =====================================================================

# Adresse ouverte en plein écran. Par défaut : page d'appairage générique
# (elle affiche un code à 6 chiffres à saisir dans la console).
URL="${URL_DEFAUT}"

# Rotation de l'image : 0, 90, 180 ou 270 (écran en portrait : 90 ou 270)
ROTATION="0"

# Définition forcée, ex. "1920x1080" (vide = celle annoncée par l'écran).
# Utile pour soulager un petit PC branché sur un téléviseur 4K.
RESOLUTION=""

# Facteur de zoom de la page, ex. "1.5" (vide = automatique)
ECHELLE=""

# Fuseau horaire (l'heure exacte est indispensable à la programmation horaire)
FUSEAU="Europe/Paris"

# Serveurs de temps (vide = serveurs par défaut de la distribution)
NTP_SERVEURS=""

# Heure du redémarrage nocturne, uniquement si une mise à jour l'exige
# (noyau...) ou pour recharger un navigateur mis à jour. Vide = jamais.
HEURE_REDEMARRAGE="04:00"

# Accès SSH (root, par clé uniquement — jamais par mot de passe).
# Les clés publiques autorisées sont dans /etc/displaypro/cles-ssh
SSH_ACTIVE="${SSH_INITIAL}"

# Importer au démarrage un réseau Wi-Fi décrit dans un fichier
# « ondiffuse-wifi.txt » (ou « displaypro-wifi.txt ») posé sur une clé USB.
WIFI_USB="oui"

# Autoriser Ctrl+Alt+F2... pour passer sur une console texte (inutile tant
# que root n'a pas de mot de passe : laisser "non" sur un lieu public).
ACCES_CONSOLE="non"

# Secondes d'attente du réseau avant d'ouvrir la page au démarrage
# (la page fonctionne ensuite hors ligne grâce à son cache).
ATTENTE_RESEAU="45"

# Options supplémentaires passées à Chromium (avancé)
CHROMIUM_OPTIONS=""
EOF
fi
chmod 644 "$CONF"
touch /etc/displaypro/cles-ssh; chmod 600 /etc/displaypro/cles-ssh

# Applique les options de la ligne de commande dans la conf
conf_set() { # clé valeur
  local cle="$1" val="$2"
  val="${val//\\/\\\\}"; val="${val//\"/\\\"}"; val="${val//&/\\&}"; val="${val//|/\\|}"
  if grep -q "^${cle}=" "$CONF"; then
    sed -i "s|^${cle}=.*|${cle}=\"${val}\"|" "$CONF"
  else
    echo "${cle}=\"${val}\"" >> "$CONF"
  fi
}
# Migration v1.0 -> v1.2 : l'ancienne adresse par défaut devient la nouvelle ;
# une adresse personnalisée n'est jamais touchée.
if grep -qxF "URL=\"${URL_ANCIENNE_DEFAUT}\"" "$CONF"; then
  conf_set URL "$URL_DEFAUT"
  info "Adresse par défaut migrée : ${URL_ANCIENNE_DEFAUT} -> ${URL_DEFAUT}"
fi
[[ -n "$OPT_URL" ]]      && conf_set URL "$OPT_URL"
[[ -n "$OPT_ROTATION" ]] && conf_set ROTATION "$OPT_ROTATION"
[[ -n "$OPT_FUSEAU" ]]   && conf_set FUSEAU "$OPT_FUSEAU"
if [[ -n "$OPT_CLE_SSH" ]]; then
  [[ -s "$OPT_CLE_SSH" ]] || echec "fichier de clé SSH vide ou absent : $OPT_CLE_SSH"
  grep -E '^(ssh-|ecdsa-|sk-)' "$OPT_CLE_SSH" >> /etc/displaypro/cles-ssh || echec "aucune clé publique valable dans $OPT_CLE_SSH"
  sort -u -o /etc/displaypro/cles-ssh /etc/displaypro/cles-ssh
  conf_set SSH_ACTIVE oui
fi

# ----------------------------------------------------------------------------
# Utilisateur dédié (compte verrouillé : aucune connexion par mot de passe)
# ----------------------------------------------------------------------------
if ! id "$UTILISATEUR" >/dev/null 2>&1; then
  info "Création de l'utilisateur ${UTILISATEUR}"
  useradd --system --create-home --home-dir "$DOSSIER_UTILISATEUR" --shell /usr/sbin/nologin \
          --comment "Ecran OnDiffuse" "$UTILISATEUR"
fi
passwd -l "$UTILISATEUR" >/dev/null 2>&1 || true
for g in video render input audio; do
  if getent group "$g" >/dev/null; then usermod -aG "$g" "$UTILISATEUR"; fi
done
mkdir -p "$DOSSIER_UTILISATEUR"/{chromium,.config/gtk-3.0,.icons/default}
chmod 750 "$DOSSIER_UTILISATEUR"

# Curseur invisible : thème de curseurs entièrement transparents, utilisé par
# cage comme par Chromium (thème « default » de l'utilisateur).
THEME_CURSEUR=/usr/share/icons/displaypro-invisible
mkdir -p "$THEME_CURSEUR/cursors"
echo 'WGN1chAAAAAAAAEAAQAAAAIA/f8YAAAAHAAAACQAAAACAP3/GAAAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAA=' \
  | base64 -d > "$THEME_CURSEUR/cursors/default"
for n in left_ptr arrow top_left_arrow pointer hand hand1 hand2 pointing_hand text xterm ibeam \
         vertical-text wait watch progress left_ptr_watch half-busy crosshair cross tcross move fleur \
         grab grabbing openhand closedhand dnd-move dnd-none dnd-copy dnd-link not-allowed no-drop \
         forbidden crossed_circle help question_arrow whats_this context-menu cell plus copy alias \
         all-scroll col-resize row-resize ew-resize ns-resize nesw-resize nwse-resize n-resize s-resize \
         e-resize w-resize ne-resize nw-resize se-resize sw-resize sb_h_double_arrow sb_v_double_arrow \
         size_hor size_ver size_bdiag size_fdiag size_all split_h split_v h_double_arrow v_double_arrow \
         top_side bottom_side left_side right_side top_left_corner top_right_corner bottom_left_corner \
         bottom_right_corner zoom-in zoom-out pirate X_cursor; do
  ln -sfn default "$THEME_CURSEUR/cursors/$n"
done
printf '[Icon Theme]\nName=displaypro-invisible\nComment=Curseurs transparents (écran OnDiffuse)\n' > "$THEME_CURSEUR/index.theme"
printf '[Icon Theme]\nInherits=displaypro-invisible\n' > "$DOSSIER_UTILISATEUR/.icons/default/index.theme"
printf '[Settings]\ngtk-cursor-theme-name=displaypro-invisible\n' > "$DOSSIER_UTILISATEUR/.config/gtk-3.0/settings.ini"
chown -R "$UTILISATEUR:$UTILISATEUR" "$DOSSIER_UTILISATEUR"

# ----------------------------------------------------------------------------
# Politiques Chromium (pas de traduction, pas de gestionnaire de mots de passe,
# lecture automatique autorisée, pas d'outils de développement...)
# ----------------------------------------------------------------------------
POLITIQUE='{
  "AutoplayAllowed": true,
  "TranslateEnabled": false,
  "PasswordManagerEnabled": false,
  "AutofillAddressEnabled": false,
  "AutofillCreditCardEnabled": false,
  "BrowserSignin": 0,
  "SyncDisabled": true,
  "DefaultBrowserSettingEnabled": false,
  "MetricsReportingEnabled": false,
  "PromotionalTabsEnabled": false,
  "SpellcheckEnabled": false,
  "BackgroundModeEnabled": false,
  "DeveloperToolsAvailability": 2,
  "IncognitoModeAvailability": 1,
  "PrintingEnabled": false,
  "DownloadRestrictions": 3,
  "ShowFullUrlsInAddressBar": false,
  "DefaultNotificationsSetting": 2,
  "DefaultGeolocationSetting": 2,
  "HideWebStoreIcon": true,
  "CommandLineFlagSecurityWarningsEnabled": false,
  "PrivacySandboxPromptEnabled": false,
  "SharedClipboardEnabled": false,
  "MediaRouterCastAllowAllIPs": false,
  "EnableMediaRouter": false
}'
for d in /etc/chromium/policies/managed /etc/chromium-browser/policies/managed; do
  mkdir -p "$d"; echo "$POLITIQUE" > "$d/displaypro.json"
done

# ----------------------------------------------------------------------------
# Programme de session (lancé par cage) : rotation, définition, puis Chromium
# ----------------------------------------------------------------------------
mkdir -p /usr/lib/displaypro
cat > /usr/lib/displaypro/session.sh.nouveau <<'EOF'
#!/usr/bin/env bash
# Session de l'écran OnDiffuse : exécutée DANS le compositeur cage.
# Quand Chromium s'arrête, cage s'arrête et systemd relance le tout.
CONF=/etc/displaypro/displaypro.conf
URL="https://ondiffuse.fr/app/pair"; ROTATION="0"; RESOLUTION=""; ECHELLE=""
ATTENTE_RESEAU="45"; CHROMIUM_OPTIONS=""
# shellcheck disable=SC1090
[ -r "$CONF" ] && . "$CONF"
PROFIL="/var/lib/displaypro/chromium"

# Sorties vidéo (ex. HDMI-A-1) : définition et rotation
if command -v wlr-randr >/dev/null 2>&1; then
  for i in 1 2 3 4 5; do SORTIES="$(wlr-randr 2>/dev/null | awk '/^[^ ]/{print $1}')"; [ -n "$SORTIES" ] && break; sleep 1; done
  case "$ROTATION" in 90|180|270) TRANSFORM="$ROTATION" ;; *) TRANSFORM="normal" ;; esac
  for s in $SORTIES; do
    if [ -n "$RESOLUTION" ]; then wlr-randr --output "$s" --mode "$RESOLUTION" || echo "Définition $RESOLUTION refusée sur $s"; fi
    wlr-randr --output "$s" --transform "$TRANSFORM" || true
  done
fi

# Après une coupure de courant : pas de bandeau « Chromium ne s'est pas fermé correctement »
PREFS="$PROFIL/Default/Preferences"
if [ -f "$PREFS" ]; then
  sed -i 's/"exited_cleanly":false/"exited_cleanly":true/; s/"exit_type":"[^"]*"/"exit_type":"Normal"/' "$PREFS" 2>/dev/null || true
fi
rm -f "$PROFIL"/Singleton* 2>/dev/null || true

# Attend le réseau (route par défaut + résolution DNS) au plus ATTENTE_RESEAU s.
# Sans réseau, la page s'ouvre quand même depuis son cache hors ligne.
HOTE="$(printf '%s' "$URL" | sed -E 's#^[a-z]+://([^/:]+).*#\1#')"
fin=$(( $(date +%s) + ${ATTENTE_RESEAU:-45} ))
while [ "$(date +%s)" -lt "$fin" ]; do
  if ip route show default 2>/dev/null | grep -q . && getent ahosts "$HOTE" >/dev/null 2>&1; then break; fi
  sleep 1
done

echo "Ouverture de $URL (réseau attendu $(( $(date +%s) - fin + ${ATTENTE_RESEAU:-45} )) s)"

OPTIONS=(
  --kiosk --start-fullscreen --no-first-run --no-default-browser-check
  --noerrdialogs --disable-infobars --disable-session-crashed-bubble --hide-crash-restore-bubble
  --autoplay-policy=no-user-gesture-required
  --disable-features=Translate,TranslateUI,MediaRouter,DialMediaRouteProvider,HardwareMediaKeyHandling,GlobalMediaControls
  --check-for-update-interval=31536000 --simulate-outdated-no-au='Tue, 31 Dec 2099 23:59:59 GMT'
  --overscroll-history-navigation=0 --disable-pinch --disable-print-preview
  --password-store=basic --ozone-platform=wayland
  --user-data-dir="$PROFIL" --disk-cache-dir="$PROFIL/cache"
)
[ -n "$ECHELLE" ] && OPTIONS+=(--force-device-scale-factor="$ECHELLE")
NAV="$(command -v chromium || command -v chromium-browser)"
# shellcheck disable=SC2086
exec "$NAV" "${OPTIONS[@]}" $CHROMIUM_OPTIONS "$URL"
EOF
chmod 755 /usr/lib/displaypro/session.sh.nouveau && mv -f /usr/lib/displaypro/session.sh.nouveau /usr/lib/displaypro/session.sh   # remplacement atomique : un exemplaire en cours d'exécution n'est pas corrompu

# Lanceur cage (lit ACCES_CONSOLE pour autoriser ou non le changement de console)
cat > /usr/lib/displaypro/kiosque.sh.nouveau <<'EOF'
#!/usr/bin/env bash
ACCES_CONSOLE="non"
# shellcheck disable=SC1091
[ -r /etc/displaypro/displaypro.conf ] && . /etc/displaypro/displaypro.conf
export XDG_SESSION_TYPE=wayland XCURSOR_THEME=displaypro-invisible XCURSOR_SIZE=24
OPT=(-d)
[ "$ACCES_CONSOLE" = "oui" ] && OPT+=(-s)
exec /usr/bin/cage "${OPT[@]}" -- /usr/lib/displaypro/session.sh
EOF
chmod 755 /usr/lib/displaypro/kiosque.sh.nouveau && mv -f /usr/lib/displaypro/kiosque.sh.nouveau /usr/lib/displaypro/kiosque.sh   # remplacement atomique : un exemplaire en cours d'exécution n'est pas corrompu

# PAM : session logind sur tty1 (donne l'accès à l'écran, au clavier, au son)
cat > /etc/pam.d/displaypro <<'EOF'
# Session du kiosque OnDiffuse (aucune authentification : compte verrouillé)
auth      required  pam_deny.so
account   required  pam_unix.so
session   required  pam_unix.so
session   required  pam_loginuid.so
session   optional  pam_keyinit.so force revoke
session   required  pam_systemd.so
session   optional  pam_env.so
EOF

# ----------------------------------------------------------------------------
# Unités systemd
# ----------------------------------------------------------------------------
cat > /etc/systemd/system/displaypro-kiosque.service <<'EOF'
[Unit]
Description=Ecran OnDiffuse (cage + Chromium en plein ecran sur tty1)
Documentation=file:///etc/displaypro/displaypro.conf
After=systemd-user-sessions.service plymouth-quit-wait.service dbus.socket systemd-logind.service NetworkManager.service
Wants=dbus.socket systemd-logind.service
Conflicts=getty@tty1.service
After=getty@tty1.service
StartLimitIntervalSec=0

[Service]
Type=simple
User=displaypro
PAMName=displaypro
WorkingDirectory=/var/lib/displaypro
TTYPath=/dev/tty1
TTYReset=yes
TTYVHangup=yes
TTYVTDisallocate=yes
StandardInput=tty-fail
StandardOutput=journal
StandardError=journal
UtmpIdentifier=tty1
UtmpMode=user
ExecStart=/usr/lib/displaypro/kiosque.sh
Restart=always
RestartSec=3

[Install]
WantedBy=graphical.target
EOF

# Redémarrage nocturne conditionnel
cat > /etc/systemd/system/displaypro-nuit.service <<'EOF'
[Unit]
Description=OnDiffuse : redemarrage nocturne si une mise a jour l'exige

[Service]
Type=oneshot
ExecStart=/usr/local/bin/displaypro nuit
EOF
cat > /etc/systemd/system/displaypro-nuit.timer <<'EOF'
[Unit]
Description=OnDiffuse : controle nocturne des mises a jour

[Timer]
OnCalendar=*-*-* 04:00:00
Persistent=false

[Install]
WantedBy=timers.target
EOF

# Import d'un Wi-Fi depuis une clé USB au démarrage
cat > /etc/systemd/system/displaypro-wifi-usb.service <<'EOF'
[Unit]
Description=OnDiffuse : import d'un reseau Wi-Fi depuis une cle USB (ondiffuse-wifi.txt)
After=NetworkManager.service local-fs.target
Wants=NetworkManager.service

[Service]
Type=oneshot
ExecStartPre=/bin/sleep 3
ExecStart=/usr/local/bin/displaypro wifi-usb

[Install]
WantedBy=multi-user.target
EOF

# ----------------------------------------------------------------------------
# Commande d'administration « displaypro »
# ----------------------------------------------------------------------------
cat > /usr/local/bin/displaypro.nouveau <<'EOF'
#!/usr/bin/env bash
# Commande d'administration de l'écran OnDiffuse (aussi appelable « ondiffuse »)
CONF=/etc/displaypro/displaypro.conf
VERSION_FICHIER=/etc/displaypro/version
SERVICE=displaypro-kiosque.service
URL_SCRIPT="https://ondiffuse.fr/telechargements/displaypro-setup.sh"
# Repli si la nouvelle adresse ne répond pas (domaine pas encore actif…)
URL_SCRIPT_SECOURS="https://vozit.dev/displaypro-vozit/telechargements/displaypro-setup.sh"
NOM_CMD="$(basename "$0")"

charger_conf() {
  URL=""; ROTATION="0"; RESOLUTION=""; ECHELLE=""; FUSEAU="Europe/Paris"; NTP_SERVEURS=""
  HEURE_REDEMARRAGE="04:00"; SSH_ACTIVE="non"; WIFI_USB="oui"; ACCES_CONSOLE="non"
  # shellcheck disable=SC1090
  [ -r "$CONF" ] && . "$CONF"
}
racine() { [ "$(id -u)" -eq 0 ] || { echo "Cette commande doit être lancée en root."; exit 1; }; }
en_direct() { [ -d /run/systemd/system ] && ! systemd-detect-virt -q --chroot 2>/dev/null; }
conf_set() {
  local cle="$1" val="$2"
  val="${val//\\/\\\\}"; val="${val//\"/\\\"}"; val="${val//&/\\&}"; val="${val//|/\\|}"
  if grep -q "^${cle}=" "$CONF"; then sed -i "s|^${cle}=.*|${cle}=\"${val}\"|" "$CONF"
  else echo "${cle}=\"${val}\"" >> "$CONF"; fi
}
relancer_kiosque() {
  if en_direct; then systemctl restart "$SERVICE" && echo "Écran relancé."; fi
  return 0
}

appliquer() {
  racine; charger_conf
  # Fuseau horaire
  if [ -n "$FUSEAU" ] && [ -f "/usr/share/zoneinfo/$FUSEAU" ]; then
    ln -sfn "/usr/share/zoneinfo/$FUSEAU" /etc/localtime; echo "$FUSEAU" > /etc/timezone
  else echo "Fuseau inconnu : $FUSEAU (ignoré)"; fi
  # NTP
  mkdir -p /etc/systemd/timesyncd.conf.d
  if [ -n "$NTP_SERVEURS" ]; then printf '[Time]\nNTP=%s\n' "$NTP_SERVEURS" > /etc/systemd/timesyncd.conf.d/displaypro.conf
  else rm -f /etc/systemd/timesyncd.conf.d/displaypro.conf; fi
  # Heure du contrôle nocturne
  mkdir -p /etc/systemd/system/displaypro-nuit.timer.d
  if [ -n "$HEURE_REDEMARRAGE" ]; then
    printf '[Timer]\nOnCalendar=\nOnCalendar=*-*-* %s:00\n' "$HEURE_REDEMARRAGE" > /etc/systemd/system/displaypro-nuit.timer.d/heure.conf
    systemctl enable displaypro-nuit.timer >/dev/null 2>&1
  else
    systemctl disable displaypro-nuit.timer >/dev/null 2>&1
  fi
  # Wi-Fi par clé USB
  if [ "$WIFI_USB" = "oui" ]; then systemctl enable displaypro-wifi-usb.service >/dev/null 2>&1
  else systemctl disable displaypro-wifi-usb.service >/dev/null 2>&1; fi
  # SSH : root par clé uniquement
  mkdir -p /etc/ssh/sshd_config.d
  cat > /etc/ssh/sshd_config.d/displaypro.conf <<'SSHD'
# Géré par « displaypro » : connexion root par clé uniquement
PermitRootLogin prohibit-password
PasswordAuthentication no
KbdInteractiveAuthentication no
SSHD
  install -d -m 700 /root/.ssh
  if [ -s /etc/displaypro/cles-ssh ]; then install -m 600 /etc/displaypro/cles-ssh /root/.ssh/authorized_keys; fi
  if [ "$SSH_ACTIVE" = "oui" ]; then
    systemctl enable ssh.service >/dev/null 2>&1
    en_direct && systemctl restart ssh.service
    [ -s /etc/displaypro/cles-ssh ] || echo "Attention : SSH actif mais aucune clé dans /etc/displaypro/cles-ssh."
  else
    if [ -n "$SSH_CONNECTION" ]; then
      echo "SSH_ACTIVE=non mais vous êtes connecté par SSH : SSH sera coupé au prochain redémarrage."
      systemctl disable ssh.service ssh.socket >/dev/null 2>&1
    else
      systemctl disable ssh.service ssh.socket >/dev/null 2>&1
      en_direct && systemctl stop ssh.service ssh.socket >/dev/null 2>&1
    fi
  fi
  if en_direct; then
    systemctl daemon-reload
    systemctl restart systemd-timesyncd.service >/dev/null 2>&1
    systemctl restart displaypro-nuit.timer >/dev/null 2>&1
  fi
  echo "Configuration appliquée."
}

statut() {
  charger_conf
  echo "OnDiffuse $(cat "$VERSION_FICHIER" 2>/dev/null)"
  echo "Nom de l'écran   : $(hostname)"
  echo "Adresse ouverte  : $URL"
  echo "Adresses IP      : $(hostname -I 2>/dev/null)"
  echo "Kiosque          : $(systemctl is-active $SERVICE) (depuis $(systemctl show -p ActiveEnterTimestamp --value $SERVICE))"
  echo "Relances kiosque : $(systemctl show -p NRestarts --value $SERVICE)"
  echo "Heure            : $(date '+%d/%m/%Y %H:%M:%S %Z') — synchronisée : $(timedatectl show -p NTPSynchronized --value 2>/dev/null)"
  echo "Rotation         : ${ROTATION}°  Définition : ${RESOLUTION:-auto}"
  echo "SSH              : $(systemctl is-enabled ssh.service 2>/dev/null)"
  echo "Redémarrage nuit : ${HEURE_REDEMARRAGE:-désactivé}$( [ -f /run/reboot-required ] && echo ' (un redémarrage est en attente)')"
  echo "Disque           : $(df -h / | awk 'NR==2{print $3" utilisés / "$2}')"
  echo "Profil navigateur: $(du -sh /var/lib/displaypro/chromium 2>/dev/null | cut -f1)"
  echo "Démarré depuis   : $(uptime -p)"
  if command -v nmcli >/dev/null; then echo "Réseau :"; nmcli -t -f DEVICE,TYPE,STATE,CONNECTION device 2>/dev/null | sed 's/^/  /'; fi
}

wifi_ajouter() { # ssid motdepasse
  local ssid="$1" mdp="$2"
  nmcli connection delete "wifi-$ssid" >/dev/null 2>&1
  if [ -n "$mdp" ]; then
    nmcli connection add type wifi ifname '*' con-name "wifi-$ssid" ssid "$ssid" \
      wifi-sec.key-mgmt wpa-psk wifi-sec.psk "$mdp" connection.autoconnect yes >/dev/null
  else
    nmcli connection add type wifi ifname '*' con-name "wifi-$ssid" ssid "$ssid" connection.autoconnect yes >/dev/null
  fi
  nmcli connection up "wifi-$ssid" >/dev/null 2>&1 &
  echo "Réseau Wi-Fi « $ssid » enregistré."
}

wifi_usb() {
  charger_conf
  [ "$WIFI_USB" = "oui" ] || exit 0
  command -v nmcli >/dev/null || exit 0
  local dev pt fichier ssid mdp pays
  pt="$(mktemp -d)"
  for dev in $(lsblk -rpno NAME,TRAN,TYPE 2>/dev/null | awk '$3=="disk" && $2=="usb"{print $1}'); do
    for part in $(lsblk -rpno NAME "$dev"); do
      mount -o ro "$part" "$pt" 2>/dev/null || continue
      fichier="$(find "$pt" -maxdepth 1 \( -iname 'ondiffuse-wifi.txt' -o -iname 'displaypro-wifi.txt' \) | head -n1)"
      if [ -n "$fichier" ]; then
        ssid="$(sed -n 's/\r$//; s/^SSID=//p' "$fichier" | head -n1)"
        mdp="$(sed -n 's/\r$//; s/^MOT_DE_PASSE=//p' "$fichier" | head -n1)"
        pays="$(sed -n 's/\r$//; s/^PAYS=//p' "$fichier" | head -n1)"
        if [ -n "$ssid" ]; then
          command -v iw >/dev/null && iw reg set "${pays:-FR}" 2>/dev/null
          rfkill unblock wifi 2>/dev/null
          wifi_ajouter "$ssid" "$mdp"
          logger -t displaypro "Wi-Fi « $ssid » importé depuis la clé USB $part"
        fi
      fi
      umount "$pt" 2>/dev/null
    done
  done
  rmdir "$pt" 2>/dev/null
  exit 0
}

nuit() {
  # Redémarre si une mise à jour l'exige ; sinon relance le navigateur s'il a été mis à jour
  if [ -f /run/reboot-required ]; then
    logger -t displaypro "Redémarrage nocturne : $(cat /run/reboot-required.pkgs 2>/dev/null | tr '\n' ' ')"
    systemctl reboot; exit 0
  fi
  local nav debut
  nav="$(readlink -f "$(command -v chromium || command -v chromium-browser)")"
  debut="$(systemctl show -p ActiveEnterTimestamp --value $SERVICE)"
  if [ -n "$debut" ] && [ "$(stat -c %Y "$nav" 2>/dev/null || echo 0)" -gt "$(date -d "$debut" +%s 2>/dev/null || echo 0)" ]; then
    logger -t displaypro "Navigateur mis à jour : relance de l'écran"
    systemctl restart $SERVICE
  fi
}

aide() {
  cat <<AIDE
Usage : ${NOM_CMD} <commande>   (« displaypro » et « ondiffuse » sont équivalents)

  status                  état de l'écran (adresse, réseau, heure, kiosque...)
  url [ADRESSE]           affiche ou change l'adresse ouverte, puis relance l'écran
  restart                 relance le navigateur (sans redémarrer la machine)
  reboot                  redémarre la machine
  rotation 0|90|180|270   tourne l'image, puis relance l'écran
  resolution [LxH|auto]   force une définition (ex. 1920x1080)
  wifi SSID [MOT_DE_PASSE]  enregistre un réseau Wi-Fi (sinon : nmtui)
  ssh activer [CLÉ_PUBLIQUE] | ssh desactiver
  reinitialiser           efface la mémoire du navigateur (l'écran sera à ré-appairer)
  capture [FICHIER]       capture d'écran PNG (défaut /tmp/capture-ecran.png)
  logs                    journal du kiosque (Ctrl+C pour quitter)
  appliquer               relit $CONF et applique tout
  mise-a-jour [--forcer]  télécharge et rejoue la dernière version du script d'installation
                          (refuse une version plus ancienne que l'installée, sauf --forcer)
  version
AIDE
}

cmd="${1:-status}"; shift || true
case "$cmd" in
  status|statut|etat) statut ;;
  url)
    if [ -z "${1:-}" ]; then charger_conf; echo "$URL"; exit 0; fi
    racine
    case "$1" in http://*|https://*) ;; *) echo "Adresse invalide (doit commencer par http:// ou https://)"; exit 1 ;; esac
    conf_set URL "$1"; echo "Nouvelle adresse : $1"; relancer_kiosque ;;
  restart|relancer) racine; relancer_kiosque ;;
  reboot|redemarrer) racine; systemctl reboot ;;
  rotation)
    racine; case "${1:-}" in 0|90|180|270) ;; *) echo "Valeurs possibles : 0, 90, 180, 270"; exit 1 ;; esac
    conf_set ROTATION "$1"; relancer_kiosque ;;
  resolution)
    racine; v="${1:-auto}"; [ "$v" = "auto" ] && v=""
    conf_set RESOLUTION "$v"; relancer_kiosque ;;
  wifi) racine; [ -n "${1:-}" ] || { echo "Usage : displaypro wifi SSID [MOT_DE_PASSE]  (ou : nmtui)"; exit 1; }
        rfkill unblock wifi 2>/dev/null; wifi_ajouter "$1" "${2:-}" ;;
  wifi-usb) racine; wifi_usb ;;
  ssh)
    racine
    case "${1:-}" in
      activer)
        if [ -n "${2:-}" ]; then shift; echo "$*" >> /etc/displaypro/cles-ssh; sort -u -o /etc/displaypro/cles-ssh /etc/displaypro/cles-ssh; fi
        conf_set SSH_ACTIVE oui; appliquer ;;
      desactiver) conf_set SSH_ACTIVE non; appliquer ;;
      *) echo "Usage : displaypro ssh activer [\"ssh-ed25519 AAAA... commentaire\"] | displaypro ssh desactiver"; exit 1 ;;
    esac ;;
  reinitialiser|reset)
    racine
    if [ "${1:-}" != "--oui" ]; then
      read -r -p "Effacer la mémoire du navigateur (appairage et cache hors ligne) ? [o/N] " r
      case "$r" in o|O|oui) ;; *) echo "Annulé."; exit 0 ;; esac
    fi
    en_direct && systemctl stop $SERVICE
    rm -rf /var/lib/displaypro/chromium; install -d -o displaypro -g displaypro /var/lib/displaypro/chromium
    en_direct && systemctl start $SERVICE; echo "Mémoire effacée : l'écran affiche de nouveau le code d'appairage." ;;
  capture)
    racine; f="${1:-/tmp/capture-ecran.png}"
    uid="$(id -u displaypro)"
    sudo_cmd() { runuser -u displaypro -- env XDG_RUNTIME_DIR="/run/user/$uid" "$@"; }
    sock="$(ls /run/user/"$uid"/ 2>/dev/null | grep -m1 '^wayland-[0-9]*$')"
    [ -n "$sock" ] || { echo "Le kiosque ne tourne pas."; exit 1; }
    sudo_cmd env WAYLAND_DISPLAY="$sock" grim /tmp/displaypro-capture.png && mv /tmp/displaypro-capture.png "$f" && echo "Capture : $f" ;;
  logs|journal) journalctl -u $SERVICE -f -n 100 ;;
  appliquer|apply) appliquer; relancer_kiosque ;;
  nuit) racine; nuit ;;
  mise-a-jour)
    racine; t="$(mktemp)"
    ok=""; actuelle="$(cat "$VERSION_FICHIER" 2>/dev/null || echo 0)"
    for u in "$URL_SCRIPT" "$URL_SCRIPT_SECOURS"; do
      echo "Téléchargement : $u"
      # On exige un vrai script d'installation (pas une page d'erreur ou de parking servie en 200)
      if ! { curl -fsSL --max-time 120 "$u" -o "$t" && head -n1 "$t" | grep -q '^#!' && grep -q '^DISPLAYPRO_VERSION=' "$t"; }; then
        echo "  -> indisponible ou contenu inattendu."; continue
      fi
      v="$(sed -n 's/^DISPLAYPRO_VERSION="\([^"]*\)".*/\1/p' "$t" | head -n1)"
      # Jamais de retour en arrière par erreur (copie ancienne restée sur un site), sauf --forcer
      if [ "${1:-}" != "--forcer" ] && [ "$v" != "$actuelle" ] && [ "$(printf '%s\n%s\n' "$v" "$actuelle" | sort -V | head -n1)" = "$v" ]; then
        echo "  -> version $v plus ancienne que la version installée ($actuelle) : ignorée."; continue
      fi
      ok="$u"; echo "  -> version $v"; break
    done
    [ -n "$ok" ] || { echo "Mise à jour impossible : aucune adresse ne fournit de version utilisable."; rm -f "$t"; exit 1; }
    echo "Script téléchargé depuis $ok (sha256 $(sha256sum "$t" | cut -d' ' -f1)), exécution..."
    bash "$t" && rm -f "$t" && relancer_kiosque ;;
  version|--version) cat "$VERSION_FICHIER" ;;
  aide|help|-h|--help) aide ;;
  *) aide; exit 1 ;;
esac
EOF
chmod 755 /usr/local/bin/displaypro.nouveau && mv -f /usr/local/bin/displaypro.nouveau /usr/local/bin/displaypro   # remplacement atomique : un exemplaire en cours d'exécution n'est pas corrompu
ln -sfn displaypro /usr/local/bin/ondiffuse   # alias au nom du produit
echo "$DISPLAYPRO_VERSION" > /etc/displaypro/version

# ----------------------------------------------------------------------------
# Système : veille, watchdog, journald, mises à jour, réseau, console
# ----------------------------------------------------------------------------
info "Réglages système (veille, watchdog, journald, mises à jour...)"

# Pas de mise en veille
mkdir -p /etc/systemd/logind.conf.d
cat > /etc/systemd/logind.conf.d/displaypro.conf <<'EOF'
# Ecran d'affichage : jamais de mise en veille. Bouton marche = arrêt propre.
[Login]
HandleSuspendKey=ignore
HandleHibernateKey=ignore
HandleLidSwitch=ignore
HandleLidSwitchExternalPower=ignore
HandleLidSwitchDocked=ignore
HandlePowerKey=poweroff
IdleAction=ignore
EOF
systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target suspend-then-hibernate.target >/dev/null 2>&1 || true

# Watchdog matériel : si le système se fige, la carte redémarre toute seule.
# 15 s est accepté par tous les watchdogs courants (Intel iTCO, AMD, Raspberry Pi).
mkdir -p /etc/systemd/system.conf.d
cat > /etc/systemd/system.conf.d/displaypro.conf <<'EOF'
[Manager]
RuntimeWatchdogSec=15s
RebootWatchdogSec=2min
EOF

# journald limité (carte SD du Raspberry Pi : journal en mémoire seulement)
mkdir -p /etc/systemd/journald.conf.d
if [[ $EST_PI -eq 1 ]]; then
  printf '[Journal]\nStorage=volatile\nRuntimeMaxUse=32M\n' > /etc/systemd/journald.conf.d/displaypro.conf
else
  printf '[Journal]\nStorage=persistent\nSystemMaxUse=100M\nRuntimeMaxUse=32M\nMaxRetentionSec=1month\n' > /etc/systemd/journald.conf.d/displaypro.conf
fi

# Messages du noyau hors de la console
echo 'kernel.printk = 3 3 3 3' > /etc/sysctl.d/90-displaypro.conf

# Mises à jour de sécurité automatiques ; le redémarrage éventuel est fait la
# nuit par displaypro-nuit.timer (pas par unattended-upgrades).
cat > /etc/apt/apt.conf.d/20auto-upgrades <<'EOF'
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::AutocleanInterval "7";
EOF
cat > /etc/apt/apt.conf.d/52displaypro-unattended <<'EOF'
// OnDiffuse : mises à jour de sécurité automatiques
Unattended-Upgrade::Automatic-Reboot "false";
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::MinimalSteps "true";
EOF
if [[ $EST_PI -eq 1 ]]; then
  # Chromium et le noyau du Raspberry Pi viennent du dépôt de la fondation
  cat >> /etc/apt/apt.conf.d/52displaypro-unattended <<'EOF'
Unattended-Upgrade::Origins-Pattern {
        "origin=Raspberry Pi Foundation,codename=${distro_codename}";
};
EOF
fi

# Réseau : NetworkManager gère tout (Ethernet en DHCP sans rien faire).
# On retire les interfaces DHCP de /etc/network/interfaces (posées par
# l'installateur) ; une configuration statique existante est conservée.
if [[ -f /etc/network/interfaces ]] && command -v NetworkManager >/dev/null; then
  if grep -qE '^\s*iface\s+\S+\s+inet6?\s+(static|manual)' /etc/network/interfaces && \
     grep -vE '^\s*iface\s+lo\s' /etc/network/interfaces | grep -qE '^\s*iface\s+\S+\s+inet6?\s+static'; then
    avert "Adresse IP fixe trouvée dans /etc/network/interfaces : conservée (gérée par ifupdown)."
  elif grep -vE '^\s*(auto|allow-hotplug|iface)\s+lo\b' /etc/network/interfaces | grep -qE '^\s*(auto|allow-hotplug|iface)\s'; then
    cp -n /etc/network/interfaces /etc/network/interfaces.avant-displaypro
    cat > /etc/network/interfaces <<'EOF'
# Le réseau de l'écran est géré par NetworkManager (nmtui, nmcli, « displaypro wifi »).
# Ancienne configuration : /etc/network/interfaces.avant-displaypro
source /etc/network/interfaces.d/*

auto lo
iface lo inet loopback
EOF
  fi
fi
systemctl enable NetworkManager.service >/dev/null 2>&1 || true

# Nom de l'écran : ondiffuse-XXXXXX (fin de l'adresse MAC), pour le reconnaître
# sur le réseau. Seulement si le nom actuel est générique.
NOM_ACTUEL="$(cat /etc/hostname 2>/dev/null || hostname)"
case "$NOM_ACTUEL" in
  ""|localhost|debian|raspberrypi|displaypro|ondiffuse)
    MAC="$( (cat /sys/class/net/e*/address /sys/class/net/w*/address 2>/dev/null || true) | grep -v '^00:00:00' | head -n1 | tr -d ':' || true)"
    if [[ -n "$MAC" ]]; then NOUVEAU="ondiffuse-${MAC: -6}"
    else NOUVEAU="ondiffuse-$(od -An -N3 -tx1 /dev/urandom | tr -d ' \n')"; fi
    echo "$NOUVEAU" > /etc/hostname
    if grep -q '^127\.0\.1\.1' /etc/hosts; then sed -i "s/^127\.0\.1\.1.*/127.0.1.1\t${NOUVEAU}/" /etc/hosts
    else printf '127.0.1.1\t%s\n' "$NOUVEAU" >> /etc/hosts; fi
    if [[ $EN_DIRECT -eq 1 ]]; then hostname "$NOUVEAU"; fi
    info "Nom de l'écran : $NOUVEAU" ;;
esac

# Langue et clavier français (messages système, clavier AZERTY si on en branche un)
if [[ -f /etc/locale.gen ]] && ! locale -a 2>/dev/null | grep -qi '^fr_FR.utf8$'; then
  sed -i 's/^# *fr_FR.UTF-8 UTF-8/fr_FR.UTF-8 UTF-8/' /etc/locale.gen; locale-gen >/dev/null 2>&1 || true
fi
grep -q '^LANG=' /etc/default/locale 2>/dev/null || echo 'LANG=fr_FR.UTF-8' >> /etc/default/locale

# Démarrage : pas de console de connexion sur tty1 (c'est l'écran du kiosque)
systemctl mask getty@tty1.service >/dev/null 2>&1 || true
systemctl set-default graphical.target >/dev/null 2>&1 || true

# ----------------------------------------------------------------------------
# Démarrage silencieux
# ----------------------------------------------------------------------------
if [[ $EST_PI -eq 1 ]]; then
  CMDLINE=/boot/firmware/cmdline.txt; [[ -f $CMDLINE ]] || CMDLINE=/boot/cmdline.txt
  CONFIGTXT=/boot/firmware/config.txt; [[ -f $CONFIGTXT ]] || CONFIGTXT=/boot/config.txt
  if [[ -f $CMDLINE ]]; then
    for p in quiet loglevel=3 logo.nologo vt.global_cursor_default=0 consoleblank=0 systemd.show_status=false; do
      grep -qw -- "$p" "$CMDLINE" || sed -i "1 s/\$/ $p/" "$CMDLINE"
    done
  fi
  if [[ -f $CONFIGTXT ]] && ! grep -q '^# DisplayPro' "$CONFIGTXT"; then
    printf '\n# DisplayPro : pas d\x27écran arc-en-ciel ni d\x27icônes d\x27alerte sur l\x27affichage\n[all]\ndisable_splash=1\navoid_warnings=1\n' >> "$CONFIGTXT"
  fi
  if command -v raspi-config >/dev/null; then
    raspi-config nonint do_blanking 1 >/dev/null 2>&1 || true      # pas de mise en veille de l'écran
    raspi-config nonint do_wifi_country FR >/dev/null 2>&1 || true  # débloque le Wi-Fi
  fi
else
  mkdir -p /etc/default/grub.d
  SPLASH=""
  if command -v plymouth-set-default-theme >/dev/null; then
    SPLASH="splash"
    mkdir -p /usr/share/plymouth/themes/displaypro
    cat > /usr/share/plymouth/themes/displaypro/displaypro.plymouth <<'EOF'
[Plymouth Theme]
Name=OnDiffuse
Description=Ecran de demarrage sobre OnDiffuse
ModuleName=script

[script]
ImageDir=/usr/share/plymouth/themes/displaypro
ScriptFile=/usr/share/plymouth/themes/displaypro/displaypro.script
EOF
    cat > /usr/share/plymouth/themes/displaypro/displaypro.script <<'EOF'
Window.SetBackgroundTopColor(0, 0, 0);
Window.SetBackgroundBottomColor(0, 0, 0);
titre = Image.Text("OnDiffuse", 1, 1, 1, 1, "Sans Bold 36");
sous = Image.Text("Démarrage de l'écran…", 0.6, 0.6, 0.6, 1, "Sans 14");
st = Sprite(titre);
st.SetPosition(Window.GetWidth() / 2 - titre.GetWidth() / 2, Window.GetHeight() / 2 - titre.GetHeight(), 1);
ss = Sprite(sous);
ss.SetPosition(Window.GetWidth() / 2 - sous.GetWidth() / 2, Window.GetHeight() / 2 + sous.GetHeight(), 1);
EOF
    plymouth-set-default-theme displaypro >/dev/null 2>&1 || true
  fi
  cat > /etc/default/grub.d/displaypro.cfg <<EOF
# DisplayPro : démarrage direct et silencieux
GRUB_TIMEOUT=0
GRUB_TIMEOUT_STYLE=hidden
GRUB_DISABLE_OS_PROBER=true
GRUB_CMDLINE_LINUX_DEFAULT="quiet ${SPLASH} loglevel=3 rd.udev.log_level=3 systemd.show_status=false vt.global_cursor_default=0 consoleblank=0"
EOF
  if command -v update-initramfs >/dev/null && [[ -n "$SPLASH" ]]; then update-initramfs -u >/dev/null 2>&1 || avert "update-initramfs a échoué"; fi
  if command -v update-grub >/dev/null; then update-grub >/dev/null 2>&1 || avert "update-grub a échoué"; fi
fi

# ----------------------------------------------------------------------------
# Activation
# ----------------------------------------------------------------------------
systemctl daemon-reload >/dev/null 2>&1 || true
systemctl enable displaypro-kiosque.service systemd-timesyncd.service >/dev/null 2>&1 || true
systemctl enable apt-daily.timer apt-daily-upgrade.timer >/dev/null 2>&1 || true
/usr/local/bin/displaypro appliquer || avert "« displaypro appliquer » a signalé une erreur."

# Compte root : verrouillé lors d'une installation par l'ISO (aucun mot de passe
# n'existe). Sur une machine existante, on ne touche pas au compte root.
if [[ $INSTALLATEUR -eq 1 ]]; then passwd -l root >/dev/null; fi

if [[ $EN_DIRECT -eq 1 ]]; then
  systemctl restart systemd-journald.service >/dev/null 2>&1 || true
  sysctl -q -p /etc/sysctl.d/90-displaypro.conf || true
  if [[ $DEMARRER -eq 1 ]]; then
    systemctl stop getty@tty1.service >/dev/null 2>&1 || true
    systemctl restart displaypro-kiosque.service
  fi
fi

info "Terminé. OnDiffuse ${DISPLAYPRO_VERSION} est installé."
if [[ $INSTALLATEUR -eq 0 && $DEMARRER -eq 0 ]]; then
  echo "    Redémarrez la machine (« reboot ») : l'écran s'ouvrira seul sur"
  echo "    $(. "$CONF"; echo "$URL")"
fi
[[ $LANCE_PAR_SSH -eq 1 ]] && echo "    (Vous êtes connecté par SSH : SSH reste actif, voir SSH_ACTIVE dans $CONF.)"
exit 0
