#!/usr/bin/env bash
#
# importer-dconf.sh
#
# Importe une archive dconf, sauvegarde la configuration existante
# et permet de conserver ou d'annuler les changements.
#

set -Eeuo pipefail
umask 077

# Ne pas exécuter en root.
if [[ ${EUID} -eq 0 ]]; then
    echo "ERREUR : exécute ce script depuis le compte destinataire."
    exit 1
fi

if [[ $# -ne 1 ]]; then
    echo "Usage : $0 archive-dconf.tar.gz"
    exit 2
fi

ARCHIVE="$1"

if [[ ! -f "$ARCHIVE" || ! -r "$ARCHIVE" ]]; then
    echo "ERREUR : archive introuvable ou illisible : $ARCHIVE"
    exit 1
fi

for cmd in dconf tar date mktemp; do
    if ! command -v "$cmd" >/dev/null 2>&1; then
        echo "ERREUR : commande manquante : $cmd"
        exit 1
    fi
done

# Répertoire des sauvegardes complètes.
BACKUPDIR="${HOME}/dconf-backups"
mkdir -p -- "$BACKUPDIR"
chmod 700 "$BACKUPDIR"

# Horodatage à la seconde, avec suffixe si nécessaire.
STAMP="$(date '+%Y%m%d_%H%M%S')"
BASE="dconf-full-${STAMP}"
BACKUP="${BACKUPDIR}/${BASE}.dconf"

n=1
while [[ -e "$BACKUP" ]]; do
    BACKUP="${BACKUPDIR}/${BASE}_${n}.dconf"
    ((n += 1))
done

# Répertoire temporaire pour l'archive.
WORKDIR="$(mktemp -d)"
trap 'rm -rf -- "$WORKDIR"' EXIT

# Vérification de l'intégrité de l'archive.
if ! tar -tzf "$ARCHIVE" >/dev/null; then
    echo "ERREUR : archive tar.gz invalide."
    exit 1
fi

# Refus des chemins absolus et des chemins contenant ..
while IFS= read -r member; do
    if [[ "$member" == /* ||
          "$member" == ".." ||
          "$member" == ../* ||
          "$member" == */../* ||
          "$member" == */.. ]]; then
        echo "ERREUR : chemin dangereux dans l'archive : $member"
        exit 1
    fi
done < <(tar -tzf "$ARCHIVE")

# Vérification des membres attendus et de leur type.
for member in \
    "MANIFEST.txt" \
    "dconf/org_cinnamon.dconf" \
    "dconf/org_nemo.dconf" \
    "dconf/org_gtk.dconf" \
    "dconf/org_gnome.dconf"
do
    if ! tar -tzf "$ARCHIVE" | grep -Fxq "$member"; then
        echo "ERREUR : fichier absent de l'archive : $member"
        exit 1
    fi

    if [[ "$(tar -tvzf "$ARCHIVE" "$member" | cut -c1)" != "-" ]]; then
        echo "ERREUR : le membre attendu n'est pas un fichier ordinaire : $member"
        exit 1
    fi
done

# Extraction des seuls fichiers attendus.
mkdir -p "$WORKDIR/dconf"

tar -xzf "$ARCHIVE" -C "$WORKDIR" \
    MANIFEST.txt \
    dconf/org_cinnamon.dconf \
    dconf/org_nemo.dconf \
    dconf/org_gtk.dconf \
    dconf/org_gnome.dconf

echo "Archive sélectionnée : $ARCHIVE"
echo

cat "$WORKDIR/MANIFEST.txt"

echo
echo "Une sauvegarde complète de ta configuration dconf va être créée."

# Sauvegarde complète avant toute modification.
if ! dconf dump / > "$BACKUP"; then
    rm -f -- "$BACKUP"
    echo "ERREUR : sauvegarde impossible. Aucune modification effectuée."
    exit 1
fi

chmod 600 "$BACKUP"

echo
echo "Sauvegarde créée :"
echo "  $BACKUP"
echo

# Enregistrer le chemin de la sauvegarde en cas d'interruption.
RESTORE_REQUIRED=0

restore_full_backup() {
    echo
    echo "Restauration de la configuration initiale..."

    # Supprimer les clés utilisateur actuelles, puis recharger
    # toutes les clés présentes dans la sauvegarde.
    dconf reset -f / || {
        echo "ERREUR : réinitialisation dconf impossible."
        echo "Sauvegarde disponible : $BACKUP"
        return 1
    }

    dconf load / < "$BACKUP" || {
        echo "ERREUR : rechargement de la sauvegarde impossible."
        echo "Sauvegarde disponible : $BACKUP"
        return 1
    }

    RESTORE_REQUIRED=0
    echo "Configuration initiale restaurée."
}

# En cas d'interruption après le début des modifications,
# tenter de restaurer l'état précédent.
on_signal() {
    trap - INT TERM HUP
    if [[ "$RESTORE_REQUIRED" -eq 1 ]]; then
        restore_full_backup || true
    fi
    exit 130
}
trap on_signal INT TERM HUP

echo "Les branches suivantes vont être remplacées :"
echo "  /org/cinnamon/"
echo "  /org/nemo/"
echo "  /org/gtk/"
echo "  /org/gnome/"
echo
echo "Les paramètres situés ailleurs ne seront pas modifiés."
echo
read -r -p "Appliquer cette configuration ? [o/N] " answer

case "$answer" in
    o|O|oui|OUI|Oui) ;;
    *)
        echo "Importation annulée. Aucun changement effectué."
        exit 0
        ;;
esac

RESTORE_REQUIRED=1

# Réinitialisation puis importation de chaque branche.
declare -A FILES=(
    ["/org/cinnamon/"]="$WORKDIR/dconf/org_cinnamon.dconf"
    ["/org/nemo/"]="$WORKDIR/dconf/org_nemo.dconf"
    ["/org/gtk/"]="$WORKDIR/dconf/org_gtk.dconf"
    ["/org/gnome/"]="$WORKDIR/dconf/org_gnome.dconf"
)

echo
echo "Importation des paramètres..."

for branch in \
    "/org/cinnamon/" \
    "/org/nemo/" \
    "/org/gtk/" \
    "/org/gnome/"
do
    echo "  -> $branch"

    if ! dconf reset -f "$branch"; then
        echo "ERREUR lors de la réinitialisation de $branch"
        restore_full_backup || true
        exit 1
    fi

    if ! dconf load "$branch" < "${FILES[$branch]}"; then
        echo "ERREUR lors de l'importation de $branch"
        restore_full_backup || true
        exit 1
    fi
done

echo
echo "Importation terminée."
echo "Teste maintenant ton environnement Cinnamon."
echo
echo "Tu peux notamment vérifier :"
echo "  - le bureau et les panneaux ;"
echo "  - les applets et le menu ;"
echo "  - le comportement de Nemo ;"
echo "  - les préférences des applications."
echo
echo "Si certains changements ne sont pas visibles immédiatement,"
echo "une déconnexion/reconnexion peut être nécessaire."
echo

while true; do
    read -r -p "Conserver les changements ? [o/N] " answer

    case "$answer" in
        o|O|oui|OUI|Oui)
            RESTORE_REQUIRED=0
            echo
            echo "Configuration conservée."
            echo "Sauvegarde précédente : $BACKUP"
            exit 0
            ;;

        n|N|non|NON|Non|"")
            if restore_full_backup; then
                echo
                echo "Configuration initiale restaurée."
                echo "Sauvegarde conservée : $BACKUP"
                exit 0
            else
                echo
                echo "ATTENTION : restauration incomplète."
                echo "Sauvegarde disponible : $BACKUP"
                exit 1
            fi
            ;;

        *)
            echo "Réponds par o ou n."
            ;;
    esac
done

