#!/usr/bin/env bash
set -uo pipefail

# shellcheck disable=SC1091
source /usr/lib/biglinux-livecd/kernel-options
# shellcheck disable=SC1091
source /usr/lib/biglinux-livecd/storage-probe

# Every user-visible string below uses bash's own translated-string form rather
# than a call to gettext. Two reasons: bash translates those itself, against
# the domain declared here, with no fork per string; and the translation
# pipeline extracts shell strings with `bash --dump-po-strings`, which sees
# only that form. Written the other way, these messages were dropped from the
# catalogs on the next pipeline run and every dialog came back in English.
export TEXTDOMAINDIR=/usr/share/locale
export TEXTDOMAIN=biglinux-livecd

user=${BIGLINUX_LIVE_USER:-}
if [[ ! $user =~ ^[a-z_][a-z0-9_-]*[$]?$ ]] || ! user_id=$(id -u -- "$user") || ((user_id == 0)); then
	printf '%s\n' 'calamares-biglinux: invalid live-session user' >&2
	exit 1
fi
user_home=$(getent passwd "$user" | awk -F: 'NR == 1 { print $6 }')
if [[ $user_home != /home/* || ! -d $user_home || -L $user_home ]] || [[ $(stat -c '%u' -- "$user_home") != "$user_id" ]]; then
	printf '%s\n' 'calamares-biglinux: unsafe live-session home directory' >&2
	exit 1
fi

calamares_state_directory=/run/biglinux-live/calamares
integrity_state_directory=/run/biglinux-live/integrity
if [[ ! -d $calamares_state_directory || -L $calamares_state_directory ]] ||
	[[ ! -d $integrity_state_directory || -L $integrity_state_directory ]]; then
	printf '%s\n' 'calamares-biglinux: required runtime state directories are unavailable' >&2
	exit 1
fi

gtk_dialog=(/usr/bin/python3 /usr/share/biglinux/calamares/gtk_dialog.py)

wait_for_verification() {
	local verification_state="" attempt
	for ((attempt = 0; attempt < 18000; attempt++)); do
		if [[ -f $integrity_state_directory/verified && ! -L $integrity_state_directory/verified ]]; then
			IFS= read -r verification_state <"$integrity_state_directory/verified" || true
			break
		fi
		if [[ -f $integrity_state_directory/failed && ! -L $integrity_state_directory/failed ]]; then
			verification_state=failed
			break
		fi
		sleep 0.2
	done
	[[ $verification_state == verified ]] && printf '%s\n' verified || printf '%s\n' failed
}

if [[ ! -f $integrity_state_directory/verified && ! -f $integrity_state_directory/failed ]]; then
	if ! /usr/bin/systemctl start --no-block biglinux-integrity-check.service; then
		printf '%s\n' 'calamares-biglinux: failed to start the integrity verification service' >&2
		exit 1
	fi
	# The dialog reads the outcome from the pipe and reports it as its exit
	# status. Reading it from stdout, which is what this used to do, compared
	# an always-empty string against "verified" and so refused to install
	# whenever the check had not already finished at boot.
	if ! wait_for_verification | "${gtk_dialog[@]}" integrity-wait \
		--title=$"Please wait..." \
		--text=$"Checking the integrity of the download and storage device..." \
		--success-title=$"Verification complete" \
		--success-text=$"The files are intact."; then
		exit 1
	fi
fi

calamares_profile_resolver=/usr/lib/biglinux-livecd/resolve-profile
calamares_profile_directory=$($calamares_profile_resolver --path) || {
	printf '%s\n' 'calamares-biglinux: failed to resolve the live Calamares profile' >&2
	exit 1
}
calamares_profile_id=${calamares_profile_directory##*/}

profile_warn_old_release() {
	/usr/bin/python3 - "$1/profile.json" <<'PY'
import json
import sys

try:
    with open(sys.argv[1], encoding="utf-8") as profile_file:
        profile = json.load(profile_file)
except (OSError, ValueError):
    raise SystemExit(2)

if not isinstance(profile, dict) or not isinstance(
    profile.get("warn_old_release", True), bool
):
    raise SystemExit(2)

raise SystemExit(0 if profile.get("warn_old_release", True) else 1)
PY
}

release_day=$(awk -F= '$1 == "UNIX_TIMESTAMP" { print $2; exit }' /etc/big-release 2>/dev/null || true)
current_day=$(($(date +%s) / 86400))
profile_warning_status=1
profile_warn_old_release "$calamares_profile_directory" || profile_warning_status=$?
if ((profile_warning_status == 2)); then
	printf '%s\n' 'calamares-biglinux: invalid profile metadata' >&2
	exit 1
fi
if [[ $release_day =~ ^[0-9]+$ ]] && ((current_day > release_day + 30)) && ((profile_warning_status == 0)); then
	if ! "${gtk_dialog[@]}" question \
		--icon-name=emblem-warning --width=500 \
		--title=$"Outdated Version" \
		--text=$"Do you want to proceed with the installation of this old version anyway? BigLinux is a system with weekly updates and this version was released more than a month ago. We recommend downloading a more recent version." \
		--ok-label=$"Continue" --cancel-label=$"Close"; then
		exit 1
	fi
fi

while IFS= read -r swap_device; do
	[[ -n $swap_device && $swap_device != /dev/zram* && -b $swap_device ]] || continue
	swapoff -- "$swap_device"
done < <(swapon --show=NAME --noheadings --raw)

# The kernel keeps unmounted Btrfs members registered, holding the disk open.
# Partitioning over an existing Btrfs install fails while they are registered.
btrfs device scan --forget || true

calamares_config_directory=/etc/calamares
calamares_lock_file=$calamares_state_directory/lock

exec {calamares_lock_fd}>"$calamares_lock_file" || {
	printf '%s\n' 'calamares-biglinux: could not open the installer lock' >&2
	exit 1
}
if ! flock -n "$calamares_lock_fd"; then
	printf '%s\n' 'calamares-biglinux: another installer instance is already running' >&2
	exit 1
fi

export CALAMARES_BRANDING=$calamares_profile_id
export CALAMARES_PROFILE_ID=$calamares_profile_id
export CALAMARES_PROFILE_DIRECTORY=$calamares_config_directory

prepare_calamares_runtime() {
	local temporary_directory keyfile keyring_name driver
	cleanup_profile_runtime() {
		[[ -n ${temporary_directory:-} ]] && rm -rf -- "$temporary_directory"
	}

	[[ -f $calamares_profile_directory/settings.conf ]] || return 1
	[[ -f $calamares_profile_directory/profile.json ]] || return 1
	[[ -d $calamares_profile_directory/modules ]] || return 1
	[[ -d $calamares_profile_directory/branding ]] || return 1

	temporary_directory=$(mktemp -d -- "$calamares_state_directory/profile.XXXXXX") || return 1
	if ! cp -a -- "$calamares_profile_directory"/. "$temporary_directory"/; then
		cleanup_profile_runtime
		return 1
	fi

	if find "$temporary_directory" -type l -print -quit | grep -q .; then
		printf '%s\n' 'calamares-biglinux: profile contains an unsupported symbolic link' >&2
		cleanup_profile_runtime
		return 1
	fi

	driver=$(kernel_driver)
	if ! grep -q '^driver:' "$temporary_directory/modules/mhwdcfg.conf" ||
		! sed -i "s/^driver:.*/driver: $driver/" "$temporary_directory/modules/mhwdcfg.conf"; then
		cleanup_profile_runtime
		return 1
	fi
	# Calamares asks geoip where the machine is and falls back to the region
	# and zone in this file when it cannot ask. That fallback was a fixed
	# value, so an install with no network landed in the wrong timezone.
	#
	# The setup wizard already knows better: every language it offers carries
	# a timezone, and picking one runs timedatectl. Reading the live session's
	# timezone back turns the user's own choice into the fallback.
	live_timezone=$(readlink -f /etc/localtime 2>/dev/null)
	live_timezone=${live_timezone#/usr/share/zoneinfo/}
	# Region is the first component and zone is the rest, which keeps the
	# three-part names such as America/Argentina/Buenos_Aires intact.
	if [[ $live_timezone == */* && $live_timezone != /* && $live_timezone != *[[:space:]]* ]]; then
		sed -i -e "s|^region:.*|region: \"${live_timezone%%/*}\"|" \
			-e "s|^zone:.*|zone: \"${live_timezone#*/}\"|" \
			"$temporary_directory/modules/locale.conf" ||
			printf '%s\n' 'calamares-biglinux: could not apply the live timezone' >&2
	fi

	if [[ $driver == free ]] &&
		! sed -i '/^[[:space:]]*- mhwdcfg[[:space:]]*$/d' "$temporary_directory/settings.conf"; then
		cleanup_profile_runtime
		return 1
	fi
	if ! printf '%s\n' '---' 'keyrings:' >"$temporary_directory/modules/postcfg.conf"; then
		cleanup_profile_runtime
		return 1
	fi
	for keyfile in /usr/share/pacman/keyrings/*.gpg; do
		[[ -f $keyfile ]] || continue
		keyring_name=${keyfile##*/}
		case "$keyring_name" in
		pubring.gpg | trustdb.gpg) continue ;;
		esac
		if ! printf '    - %s\n' "${keyring_name%.gpg}" >>"$temporary_directory/modules/postcfg.conf"; then
			cleanup_profile_runtime
			return 1
		fi
	done

	if [[ -e $calamares_config_directory || -L $calamares_config_directory ]]; then
		if [[ -L $calamares_config_directory || ! -d $calamares_config_directory ]]; then
			printf '%s\n' 'calamares-biglinux: unsafe /etc/calamares path' >&2
			cleanup_profile_runtime
			return 1
		fi
		if ! rm -rf -- "$calamares_config_directory"; then
			cleanup_profile_runtime
			return 1
		fi
	fi
	if ! mv -- "$temporary_directory" "$calamares_config_directory"; then
		cleanup_profile_runtime
		return 1
	fi
	temporary_directory=
}

if ! prepare_calamares_runtime; then
	printf '%s\n' 'calamares-biglinux: failed to prepare the selected Calamares profile' >&2
	exit 1
fi

if ! efi_partition_count=$(count_non_live_efi_partitions /run/miso/bootmnt); then
	"${gtk_dialog[@]}" info \
		--title=$"Could not inspect EFI partitions" \
		--text=$"The installer could not safely inspect the disk layout. Close the installer, reconnect the storage device, and try again." \
		--width=480
	exit 1
fi

if [[ -d /sys/firmware/efi ]] && ((efi_partition_count == 0)); then
	if ! "${gtk_dialog[@]}" question --icon-name=drive-harddisk --width=500 \
		--title=$"Install the system" \
		--text=$"Do you want to proceed with the installation in EFI mode? The current boot is using EFI mode, but I did not find an EFI partition on this computer." \
		--ok-label=$"Continue" --cancel-label=$"Close"; then
		exit 1
	fi
fi
if [[ ! -d /sys/firmware/efi ]] && ((efi_partition_count > 0)); then
	if ! "${gtk_dialog[@]}" question --icon-name=drive-harddisk --width=500 \
		--title=$"Install the system" \
		--text=$"Do you want to proceed with the installation in Legacy/BIOS mode? I found an EFI partition on this computer." \
		--ok-label=$"Continue" --cancel-label=$"Close"; then
		exit 1
	fi
fi

rm -f -- "$calamares_state_directory/start_calamares"
python3 /usr/share/biglinux/calamares/main.py

if [[ -f $calamares_state_directory/start_calamares && ! -L $calamares_state_directory/start_calamares ]]; then
	# Calamares runs as root and reads its theme from root's config. On an
	# image where root has never had one, both copies below failed and the
	# installer came up unthemed - silently, since this script does not run
	# under set -e.
	mkdir -p /root/.config
	if [[ -e /usr/share/biglinux/themes/biglinux/.config/kdeglobals ]]; then
		cp -f /usr/share/biglinux/themes/biglinux/.config/kdeglobals /root/.config/kdeglobals
		cp -Rf /usr/share/biglinux/themes/biglinux/.config/Kvantum /root/.config/Kvantum
	else
		cp -f /etc/skel/.config/kdeglobals /root/.config/kdeglobals
		cp -Rf /etc/skel/.config/Kvantum /root/.config/Kvantum
	fi

	QT_QPA_PLATFORMTHEME=kde QT_QUICK_BACKEND=software dbus-launch \
		/usr/bin/calamares

	session_log=/root/.cache/calamares/session.log
	if [[ ! -f $session_log || -L $session_log ]]; then
		"${gtk_dialog[@]}" info --title=$"Installation log unavailable" \
			--text=$"Calamares did not create a valid installation log. Close the installer and try again." --width=480
		exit 1
	fi

	copy_installation_log() {
		local destination_name=$1 destination log_size
		log_size=$(stat -c '%s' -- "$session_log") || return 1
		((log_size <= 32 * 1024 * 1024)) || return 1
		destination=$user_home/$destination_name
		# The inner shell expands $1 after runuser sets its argument vector.
		# shellcheck disable=SC2016
		/usr/bin/cat -- "$session_log" | /usr/bin/runuser -u "$user" -- \
			/usr/bin/bash -c 'umask 077; cat >"$1"' _ "$destination"
	}

	copy_installation_log calamares_complete.log || true
	# Calamares writes this line only when the installation actually ran, with
	# succeeded or failed. Closing the installer before starting leaves no line
	# at all, and that is not a failure to report.
	if grep -w completion "$session_log" | grep -v bash-completion | grep -qE ':[[:space:]]*failed[[:space:]]*$'; then
		copy_installation_log calamares_error.log || true
		"${gtk_dialog[@]}" info --title=$"Installation failed" \
			--text=$"The installation failed. The local error log was saved in your home folder." --width=480
	fi
fi
