#!/usr/bin/env bash
# EVOFORGE — L'INSTALLATEUR LINUX (publié sous le nom EvoForge-Setup.sh)
#
#     bash EvoForge-Setup.sh [dossier]        (par défaut : ~/.local/share/EvoForge)
#     curl -fsSL https://join.evoforge.fr/download/EvoForge-Setup.sh | bash
#
# Même principe que l'installateur Windows (EvoForge-Setup.nsi) : il ne contient aucune version. Il
# demande au service le manifeste de la DERNIÈRE VERSION GRATUITE, télécharge chaque fichier et
# vérifie son empreinte. Relancé sur une installation existante, il RÉPARE : ce qui a déjà la bonne
# empreinte n'est pas retéléchargé, et rien de ce que l'application a écrit n'est touché.
#
# DEUX VISAGES. Lancé depuis un terminal, il écrit dans le terminal. Lancé depuis le gestionnaire de
# fichiers (« Exécuter comme un programme »), il n'a pas de terminal : il ouvre alors une fenêtre de
# progression (zenity, présent sur GNOME/Ubuntu ; kdialog pour les messages sous KDE). Sans l'un ni
# l'autre, il travaille en silence et pose son journal dans ~/.cache/evoforge-setup.log.
#
# TOUT LE SCRIPT EST DANS UNE FONCTION, appelée à la dernière ligne : lu depuis un tuyau
# (`curl | bash`), un téléchargement coupé en route n'exécute donc RIEN plutôt qu'une moitié.
set -euo pipefail

main() {
	local api="${EVOFORGE_API:-https://evoforge.fr/api/v1}"
	local dest="${1:-${XDG_DATA_HOME:-$HOME/.local/share}/EvoForge}"
	local fr=0; case "${LANG:-}${LC_ALL:-}${LC_MESSAGES:-}" in *fr_*|fr*) fr=1 ;; esac
	tr_() { if [ "$fr" = 1 ]; then printf '%s' "$1"; else printf '%s' "$2"; fi; }

	# --- terminal, ou fenêtre ?
	local gui=''
	if [ ! -t 1 ] && [ -n "${DISPLAY:-}${WAYLAND_DISPLAY:-}" ] && [ -z "${EVOFORGE_NO_GUI:-}" ]; then
		if command -v zenity >/dev/null; then gui=zenity; elif command -v kdialog >/dev/null; then gui=kdialog; fi
		if [ -z "$gui" ]; then mkdir -p "$HOME/.cache"; exec >>"$HOME/.cache/evoforge-setup.log" 2>&1; fi
	fi
	say() { [ -n "$gui" ] || printf '%s\n' "$(tr_ "$1" "$2")"; }
	# la fenêtre de progression lit des lignes : « 42 » règle la barre, « # texte » règle le libellé
	progress() { if [ "$gui" = zenity ]; then printf '%s\n# %s\n' "$1" "$2" >&3 2>/dev/null || true; elif [ -z "$gui" ] && [ -t 1 ]; then printf '\r\033[K  %3s %%  %s' "$1" "$2"; fi; }
	endline() { if [ -z "$gui" ] && [ -t 1 ]; then printf '\r\033[K'; fi; }
	box() {   # $1 = error|info
		local kind="$1" msg="$2"
		case "$gui" in
			zenity) exec 3>&- 2>/dev/null || true; zenity "--$kind" --title="EvoForge" --width=420 --text="$msg" 2>/dev/null || true ;;
			kdialog) if [ "$kind" = error ]; then kdialog --title EvoForge --error "$msg" || true; else kdialog --title EvoForge --msgbox "$msg" || true; fi ;;
			*) if [ "$kind" = error ]; then printf '❌ %s\n' "$msg" >&2; else printf '✓ %s\n' "$msg"; fi ;;
		esac
	}
	die() { endline; box error "$(tr_ "$1" "$2")"; exit 1; }

	for tool in curl sha256sum; do
		command -v "$tool" >/dev/null || die "« $tool » est nécessaire et introuvable." "'$tool' is required and was not found."
	done
	[ "$(uname -m)" = "x86_64" ] || die "EvoForge n'existe que pour Linux x86_64." "EvoForge is only built for x86_64 Linux."
	if pgrep -f "$dest/EvoForge" >/dev/null 2>&1; then die "EvoForge est ouvert : fermez-le, puis relancez l'installateur." "EvoForge is running: close it, then run the installer again."; fi

	# le chemin est écrit DANS le piège (guillemets doubles) : `tmp` est locale à la fonction, et le
	# piège s'exécute après elle — sous `set -u`, la relire à ce moment-là est une erreur.
	local tmp; tmp="$(mktemp -d)"; trap "rm -rf '$tmp'" EXIT

	# ANNULER, dans la fenêtre, arrête l'installateur (--auto-kill) ; ce qui est déjà téléchargé reste
	# sur le disque et sera repris à la prochaine exécution.
	if [ "$gui" = zenity ]; then
		exec 3> >(zenity --progress --title="EvoForge" --width=460 --percentage=0 --auto-close --auto-kill \
			--text="$(tr_ 'Recherche de la dernière version gratuite…' 'Asking evoforge.fr for the latest free version…')" 2>/dev/null)
	fi
	say "Recherche de la dernière version gratuite…" "Asking evoforge.fr for the latest free version…"
	curl -fsSL --retry 3 --connect-timeout 30 -A "EvoForge-Setup/sh" "$api/update/installer?platform=linux" -o "$tmp/manifest" \
		|| die "Le service EvoForge est injoignable. Vérifiez votre connexion à Internet." "The EvoForge service could not be reached. Check your internet connection."

	local sig version count total
	{ IFS= read -r sig; IFS= read -r version; IFS= read -r count || true; IFS= read -r total || true; } < "$tmp/manifest"
	[ "$sig" = "EVOFORGE-INSTALL 1" ] || die "Réponse du service incomprise : récupérez le dernier installateur sur join.evoforge.fr." "Unexpected answer from the service: get the latest installer from join.evoforge.fr."
	[ "$version" != "NONE" ] && [ "${count:-0}" -ge 1 ] 2>/dev/null || die "Aucune version gratuite n'est publiée pour le moment. Revenez un peu plus tard." "No free version is published at the moment. Please come back later."
	case "${total:-}" in ''|*[!0-9]*|0) total=1 ;; esac
	say "EvoForge $version — $count fichiers → $dest" "EvoForge $version — $count files → $dest"
	mkdir -p "$dest"

	# --- télécharger, vérifier, mettre en place — trois tours au plus pour ce qui a manqué
	local round failed=0 kept got seen sha size url rel target part
	for round in 1 2 3; do
		failed=0; kept=0; got=0; seen=0
		while IFS= read -r sha && IFS= read -r size && IFS= read -r url && IFS= read -r rel; do
			case "$size" in ''|*[!0-9]*) size=0 ;; esac
			seen=$((seen + size))
			# LE CHEMIN VIENT DU RÉSEAU : rien ne sort du dossier d'installation, même si le service le
			# filtre déjà au dépôt.
			case "$rel" in ''|/*|*..*|*:*|*\\*) failed=$((failed + 1)); continue ;; esac
			case "$sha" in *[!0-9a-f]*|'') failed=$((failed + 1)); continue ;; esac
			target="$dest/$rel"
			if [ -f "$target" ] && [ "$(sha256sum "$target" | cut -d' ' -f1)" = "$sha" ]; then kept=$((kept + 1)); continue; fi
			# l'empreinte est DANS le nom du fichier partiel : une reprise ne recolle jamais deux versions
			part="$target.${sha:0:8}.part"
			mkdir -p "$(dirname "$target")"
			progress "$((seen * 99 / total))" "$rel"
			if curl -fsSL --retry 3 --connect-timeout 30 -C - -A "EvoForge-Setup/sh" "$url" -o "$part" \
				&& [ "$(sha256sum "$part" | cut -d' ' -f1)" = "$sha" ]; then
				mv -f "$part" "$target"; got=$((got + 1))
			else
				rm -f "$part"; failed=$((failed + 1))
			fi
		done < <(tail -n +5 "$tmp/manifest")
		endline
		say "  $got téléchargé(s), $kept déjà à jour, $failed en échec" "  $got downloaded, $kept already up to date, $failed failed"
		[ "$failed" = 0 ] && break
	done
	[ "$failed" = 0 ] || die "$failed fichier(s) n'ont pas pu être installés. Relancez l'installateur : ce qui est téléchargé est conservé." "$failed file(s) could not be installed. Run the installer again: what is downloaded is kept."

	# --- le manifeste ne porte pas les droits : tout ce qui EST un programme devient exécutable
	while IFS= read -r sha && IFS= read -r size && IFS= read -r url && IFS= read -r rel; do
		case "$(head -c 4 "$dest/$rel" 2>/dev/null | od -An -c | tr -d ' \n')" in
			177ELF|'#!'*) chmod +x "$dest/$rel" ;;
		esac
	done < <(tail -n +5 "$tmp/manifest")
	cp "$tmp/manifest" "$dest/install-manifest.txt"

	# --- L'INVITATION D'UN AMI (Paddock) : téléchargé depuis sa page d'invitation, l'installateur s'appelle
	#     « EvoForge-Setup-EF-7K2Q-H8M3PZ4X.sh » (code de l'ami + jeton secret de son lien) ; EvoForge le lit au
	#     lancement (invite.txt) et en fait une amitié dès que le compte est ouvert. Lu depuis un tuyau
	#     (`curl | bash`), il n'a pas de nom : rien à faire.
	local self; self="$(basename -- "${BASH_SOURCE[0]:-$0}")"
	if [[ "$self" =~ EF-([A-Za-z0-9]{4})(-([A-Za-z0-9]{8}))? ]]; then
		printf '%s\n' "${BASH_REMATCH[1]^^}" > "$dest/invite.txt"
		[[ -n "${BASH_REMATCH[3]}" ]] && printf '%s\n' "${BASH_REMATCH[3]^^}" > "$dest/invite-token.txt"
	fi

	# --- entrée de menu (pour l'utilisateur seul)
	local apps="${XDG_DATA_HOME:-$HOME/.local/share}/applications"
	mkdir -p "$apps"
	cat > "$apps/evoforge.desktop" <<-DESKTOP
		[Desktop Entry]
		Type=Application
		Name=EvoForge
		Comment=Content manager for Assetto Corsa EVO
		Exec="$dest/EvoForge" %u
		Path=$dest
		Icon=$dest/ui/resources/evoforge_96.png
		Terminal=false
		Categories=Game;Utility;
		MimeType=x-scheme-handler/evoforge;
	DESKTOP
	command -v update-desktop-database >/dev/null && update-desktop-database "$apps" >/dev/null 2>&1 || true
	# LES LIENS D'INVITATION `evoforge://` : sous Windows l'application déclare son schéma elle-même ;
	# sous Linux c'est cette entrée de menu qui le porte (le lien arrive en argument, %u).
	# (EVOFORGE_NO_MIME=1 pour un essai : xdg-mime écrit dans ~/.config, quel que soit le dossier visé)
	[ -n "${EVOFORGE_NO_MIME:-}" ] || ! command -v xdg-mime >/dev/null || xdg-mime default evoforge.desktop x-scheme-handler/evoforge >/dev/null 2>&1 || true

	# --- le désinstalleur, posé à côté de l'application : il retire ce que l'installateur a posé
	cat > "$dest/uninstall.sh" <<-'UNINSTALL'
		#!/usr/bin/env bash
		# Désinstalle EvoForge. Par défaut, vos données (réglages, session, atelier) sont CONSERVÉES ;
		# `--all` retire tout le dossier. Vos mods installés dans le jeu ne sont touchés dans aucun cas.
		set -euo pipefail
		here="$(cd "$(dirname "$0")" && pwd)"
		[ -f "$here/install-manifest.txt" ] || { echo "install-manifest.txt introuvable : ce dossier n'a pas été posé par l'installateur." >&2; exit 1; }
		rm -f "${XDG_DATA_HOME:-$HOME/.local/share}/applications/evoforge.desktop"
		if [ "${1:-}" = "--all" ] && [ "$(basename "$here")" = "EvoForge" ]; then rm -rf "$here"; echo "EvoForge et ses données ont été retirés."; exit 0; fi
		while IFS= read -r _ && IFS= read -r _ && IFS= read -r _ && IFS= read -r rel; do
			case "$rel" in ''|/*|*..*) continue ;; esac
			rm -f "$here/$rel"
		done < <(tail -n +5 "$here/install-manifest.txt")
		rm -f "$here/install-manifest.txt" "$here/uninstall.sh"
		find "$here" -depth -type d -empty -delete 2>/dev/null || true
		if [ -d "$here" ]; then echo "EvoForge est retiré. Vos données restent dans $here (« --all » pour tout retirer)."; else echo "EvoForge est retiré."; fi
	UNINSTALL
	chmod +x "$dest/uninstall.sh"

	progress 100 "$(tr_ 'Terminé' 'Done')"
	endline
	box info "$(tr_ "EvoForge $version est installé.

Lancez-le depuis le menu des applications, ou : $dest/EvoForge
Pour le désinstaller : $dest/uninstall.sh" "EvoForge $version is installed.

Start it from the applications menu, or: $dest/EvoForge
To uninstall: $dest/uninstall.sh")"
}

main "$@"
