#!/bin/bash
# ============================================================================
#  FWS — moteur de réparation d'une installation existante depuis le live
#  (« point de restauration à la Windows »).
#
#  L'interface GRAPHIQUE est fws-recovery-gui (GTK3 python — le live embarque
#  python-gobject/gtk3 via Anaconda) ; elle pilote CE script en sous-commandes.
#  Sans argument : assistant CONSOLE (repli sans X, ou usage à la main).
#
#  Sous-commandes (pour la GUI / scripts) :
#    scan                       → une ligne « périphérique|nom » par install FWS
#    repair <action> <dev>      → monte, répare, démonte ; sortie en continu
#                                 action ∈ grub|initramfs|pacman|pacman-update|all
#    shell <dev>                → terminal DANS le système (xterm si DISPLAY)
#
#  arch-chroot (arch-install-scripts, présent sur le live) gère les binds.
#
#  Fonctionne depuis le LIVE (autre disque) ET depuis le SYSTÈME INSTALLÉ
#  lui-même (menu d'applications / entrée GRUB « Réparer FWS ») : si la cible
#  est la racine en cours d'exécution, on opère directement, sans mount/chroot.
# ============================================================================
set -u
MNT=/mnt/fws-recovery
ROOTDEV=""
DO_UPDATE=""
SELF=""                                          # cible = système courant
ROOTSRC="$(findmnt -no SOURCE / 2>/dev/null)"    # sur le live : overlay → jamais un lsblk

say()  { printf '\n==> %s\n' "$*"; }
warn() { printf '  ⚠ %s\n' "$*"; }

[ "$(id -u)" -eq 0 ] || { echo "root requis"; exit 1; }

cleanup() { [ -n "$MNT" ] && [ "$MNT" != "/" ] && umount -R "$MNT" 2>/dev/null; }

# Exécute DANS la cible : chroot si elle est montée, directement si c'est nous.
in_target() {
    if [ -n "$SELF" ]; then "$@"; else arch-chroot "$MNT" "$@"; fi
}

# ── Moteur ───────────────────────────────────────────────────────────────────

scan_installs() {
    vgscan --mknodes >/dev/null 2>&1
    vgchange -ay     >/dev/null 2>&1
    udevadm settle --timeout=10 >/dev/null 2>&1
    # Le système en cours d'exécution d'abord (app lancée depuis l'OS installé).
    if [ ! -d /run/archiso ] && grep -qiE '^(ID|NAME)=.*fws' /etc/os-release 2>/dev/null; then
        printf '%s|%s (ce système)\n' "$ROOTSRC" \
            "$(. /etc/os-release 2>/dev/null; echo "${PRETTY_NAME:-FWS}")"
    fi
    mkdir -p "$MNT"
    local dev name
    while read -r dev; do
        [ -b "$dev" ] || continue
        [ "$dev" = "$ROOTSRC" ] && continue      # déjà listé ci-dessus
        mount -o ro "$dev" "$MNT" 2>/dev/null || continue
        if [ -f "$MNT/etc/os-release" ] && [ -d "$MNT/boot" ] \
           && grep -qiE '^(ID|NAME)=.*fws' "$MNT/etc/os-release" 2>/dev/null; then
            name="$(. "$MNT/etc/os-release" 2>/dev/null; echo "${PRETTY_NAME:-FWS}")"
            printf '%s|%s\n' "$dev" "$name"
        fi
        umount "$MNT" 2>/dev/null
    done < <(lsblk -rpno NAME,TYPE | awk '$2=="part"||$2=="lvm"{print $1}')
}

# Monte la cible + /boot + ESP d'après son fstab.
# Cas particulier : la cible EST la racine en cours d'exécution (app lancée
# depuis l'OS installé, ou entrée GRUB « Réparer FWS ») → tout est déjà là,
# on opère en place (MNT="" : les chemins "$MNT/…" pointent sur la vraie racine).
mount_target() {
    ROOTDEV="$1"
    if [ "$ROOTDEV" = "$ROOTSRC" ] && [ ! -d /run/archiso ]; then
        SELF=1; MNT=""
        echo "  cible = système en cours d'exécution (pas de montage)"
        return 0
    fi
    mkdir -p "$MNT"
    mount "$ROOTDEV" "$MNT" || return 1
    local mp src
    for mp in /boot /boot/efi; do
        src="$(awk -v mp="$mp" '$2==mp && $1!~/^#/{print $1; exit}' "$MNT/etc/fstab" 2>/dev/null)"
        [ -n "$src" ] || continue
        mount "$(findfs "$src" 2>/dev/null || echo "$src")" "$MNT$mp" 2>/dev/null \
            && echo "  $mp monté" || warn "$mp non monté (on continue)"
    done
    return 0
}

fix_grub() {
    say "Réinstallation de GRUB…"
    if [ -d /sys/firmware/efi ]; then
        in_target grub-install --target=x86_64-efi \
            --efi-directory=/boot/efi --bootloader-id=FWS --recheck || true
        in_target grub-install --target=x86_64-efi \
            --efi-directory=/boot/efi --bootloader-id=FWS --removable --recheck
    else
        local disk
        disk="$(lsblk -spno NAME,TYPE "$ROOTDEV" 2>/dev/null \
                | awk '$2=="disk"{print $1; exit}' | grep -oE '/dev/[a-z0-9]+')"
        [ -n "$disk" ] || { warn "disque BIOS introuvable"; return 1; }
        in_target grub-install --target=i386-pc --recheck "$disk"
    fi
    in_target grub-mkconfig -o /boot/grub/grub.cfg
}

fix_initramfs() {
    say "Régénération de l'initramfs (mkinitcpio -P)…"
    echo "  NB : les avertissements « Possibly missing firmware for module … » sont"
    echo "  NORMAUX sur Arch : firmware de matériel exotique (cartes SCSI/fibre…)"
    echo "  non fourni par linux-firmware. Sans effet, sauf si tu AS ce matériel."
    in_target mkinitcpio -P
}

fix_pacman() {
    say "Réparation de pacman…"
    rm -f "$MNT/var/lib/pacman/db.lck" && echo "  verrou db.lck retiré (s'il existait)"
    in_target pacman-key --init
    in_target pacman-key --populate archlinux
    if [ -n "$DO_UPDATE" ]; then
        say "Mise à jour complète (pacman -Syu)…"
        [ -z "$SELF" ] && cp -f /etc/resolv.conf "$MNT/etc/resolv.conf" 2>/dev/null
        in_target pacman -Syu --noconfirm
    fi
}

fix_all() { fix_initramfs && fix_grub; }

# ── Sous-commandes (pilotées par fws-recovery-gui) ───────────────────────────
case "${1:-}" in
    scan)
        scan_installs
        exit 0 ;;
    repair)
        action="${2:?action manquante}"; dev="${3:?périphérique manquant}"
        trap cleanup EXIT
        say "Montage de $dev…"
        mount_target "$dev" || { warn "montage impossible"; exit 1; }
        rc=0
        case "$action" in
            grub)          fix_grub          || rc=$? ;;
            initramfs)     fix_initramfs     || rc=$? ;;
            pacman)        fix_pacman        || rc=$? ;;
            pacman-update) DO_UPDATE=1; fix_pacman || rc=$? ;;
            all)           fix_all           || rc=$? ;;
            *)             warn "action inconnue : $action"; rc=2 ;;
        esac
        cleanup; trap - EXIT
        [ "$rc" -eq 0 ] && say "Terminé ✔" || say "Échec (code $rc)"
        exit "$rc" ;;
    shell)
        dev="${2:?périphérique manquant}"
        trap cleanup EXIT
        mount_target "$dev" || { warn "montage impossible"; exit 1; }
        if [ -z "$SELF" ] && [ -n "${DISPLAY:-}" ] && command -v xterm >/dev/null 2>&1; then
            xterm -title "FWS — terminal dans le système (chroot)" \
                  -e arch-chroot "$MNT" /bin/bash
        else
            in_target /bin/bash
        fi
        cleanup; trap - EXIT
        exit 0 ;;
    ""|tui) : ;;  # assistant console ci-dessous
    *) echo "usage : fws-recovery [scan|repair <action> <dev>|shell <dev>]"; exit 2 ;;
esac

# ── Assistant CONSOLE (repli sans X / usage manuel) ──────────────────────────
trap cleanup EXIT
say "Recherche des installations FWS…"
mapfile -t CANDIDATES < <(scan_installs)

if [ "${#CANDIDATES[@]}" -eq 0 ]; then
    warn "Aucune installation FWS trouvée. Disques visibles :"
    lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS
    read -rp "Entrée pour ouvrir un shell (exit pour redémarrer). " _
    bash; exit 0
fi

echo; i=1
for c in "${CANDIDATES[@]}"; do echo "  $i) ${c%%|*}   —   ${c##*|}"; i=$((i+1)); done
read -rp "Installation à réparer [1] : " pick; pick="${pick:-1}"
dev="${CANDIDATES[$((pick-1))]%%|*}"
[ -n "$dev" ] || exit 1
say "Montage de $dev…"
mount_target "$dev" || { warn "montage impossible"; exit 1; }

while :; do
    cat <<'MENU'

  ╔════════════════════════════════════════════════════════╗
  ║              FWS — Réparation du système                ║
  ╠════════════════════════════════════════════════════════╣
  ║  1) Réinstaller GRUB (le système ne démarre plus)       ║
  ║  2) Régénérer l'initramfs (mkinitcpio -P)               ║
  ║  3) Réparer pacman (verrou, clés, mise à jour)          ║
  ║  4) Tout réparer (initramfs + GRUB)                     ║
  ║  5) Ouvrir un shell DANS le système (chroot)            ║
  ║  q) Quitter et redémarrer                               ║
  ╚════════════════════════════════════════════════════════╝
MENU
    read -rp "  Choix : " choice
    case "$choice" in
        1) fix_grub ;;
        2) fix_initramfs ;;
        3) DO_UPDATE=""
           read -rp "  Mise à jour complète aussi (réseau) ? [o/N] " _u
           case "$_u" in o|O|oui) DO_UPDATE=1 ;; esac
           fix_pacman ;;
        4) fix_all ;;
        5) say "Shell chroot — « exit » pour revenir au menu."
           in_target /bin/bash ;;
        q|Q) break ;;
        *) warn "choix inconnu" ;;
    esac
done

cleanup; trap - EXIT
say "Réparation terminée."
read -rp "Redémarrer maintenant ? [O/n] " _r
case "$_r" in n|N) bash ;; *) systemctl reboot ;; esac
