#!/bin/bash
# shellcheck disable=SC2317
set -e

CURRENT_INTERVENTION_SEQUENCE=3
INTERVENTION_SEQUENCE_FILE="/etc/garuda/garuda-update/intervention_sequence"

# Hacky way to check if the package is installed
package-exists-fast() {
    [[ -n "$1" ]] || return 1
    local p
    for p in "/var/lib/pacman/local/$1-"*/; do
        if [[ $p =~ /$1-[^-]+-[0-9.]+/$ ]]; then
            return 0
        fi
    done
    return 1
}

get_intervention_sequence() {
	if [[ -f "$INTERVENTION_SEQUENCE_FILE" ]]; then
		while IFS= read -r line; do
			if [[ $line =~ ^INTERVENTION_SEQUENCE=([0-9]+)$ ]]; then
				echo "${BASH_REMATCH[1]}"
				return
			fi
		done < "$INTERVENTION_SEQUENCE_FILE"

		echo "$CURRENT_INTERVENTION_SEQUENCE"
	else
		echo "0"
	fi
}

update_keyring_packages() {
	local packages=("archlinux-keyring" "chaotic-keyring")
	if package-exists-fast blackarch-keyring; then
		packages+=("blackarch-keyring")
	fi
	if [ -n "$($PACMAN -Qu "${packages[@]}" 2>&1)" ]; then
		echo -e "\n\033[1;33m-->\033[1;34m Applying keyring updates before starting full system update..\033[0m"
		# shellcheck disable=1007
		SNAP_PAC_SKIP=y SKIP_AUTOSNAP= $PACMAN -S --needed --noconfirm "${packages[@]}" || return 0
		return 1
	fi
	return 0
}

# Various hotfixes for the update process that do not involve modifying the pacman command itself (use package-replaces for that)
update_hotfixes() {
	set -euo pipefail
	local packages sequence
	sequence="$(get_intervention_sequence)"

	if (( sequence < 1 )); then
		packages="$($PACMAN -Qq "linux-firmware<20250613.12fe085f-5" 2>/dev/null | xargs || true)"
		# https://archlinux.org/news/linux-firmware-2025061312fe085f-5-upgrade-requires-manual-intervention/
		# linux-firmware<20250613.12fe085f-5
		if [[ "$packages" =~ (^| )linux-firmware($| ) ]]; then
			# It doesn't handle symlinks very well, it seems
			# We replace the symlinks with the ones that are expected by linux-firmware-nvidia to allow the update to go through smoothly
			# This is of course very hacky and not how archlinux wants us to do it, but this is more correct than force removing the entire linux-firmware package in my opinion >.<
			# Reason: If the update process does not succeed for another reason, the user is left without any linux-firmware files AT ALL. This way, we have all the linux-firmware files still in place
			# Any potential damage caused to the local pacman database will be reverted upon the next update of linux-firmware, which is scheduled to happen only moments after this code is run.

			# Find linux-firmware database path
			local db_path=""
			for p in /var/lib/pacman/local/linux-firmware-*/; do
				# Match path ending in /linux-firmware-<version>-<pkgrel>
				[[ $p =~ /linux-firmware-[^-]+-[0-9]+/$ ]] && { db_path="$p"; break; }
			done

			if [ -d "$db_path" ]; then
				# Remove any lines that match /usr/lib/firmware/nvidia/ad*/gsp from the files list that are not ad102
				gawk -i inplace '!/usr\/lib\/firmware\/nvidia\/ad.*/ || /usr\/lib\/firmware\/nvidia\/ad102\/.*$/' "$db_path/files"
				# Delete faulty symlinks
				rm -r /usr/lib/firmware/nvidia/ad103 /usr/lib/firmware/nvidia/ad104 /usr/lib/firmware/nvidia/ad106 /usr/lib/firmware/nvidia/ad107 || true
				# Copy the ad102 gsp files to the correct locations
				pushd /usr/lib/firmware/nvidia/ >/dev/null
				for dest in /usr/lib/firmware/nvidia/ad10{3,4,6,7}; do ln -s ./ad102 "$dest"; done
				popd >/dev/null
				# Write to "$db_path"/files
				sed -i -e '$ { /^$/d; }' "$db_path/files"
				printf "%s\n" usr/lib/firmware/nvidia/ad10{3,4,6,7} >> "$db_path/files"
			fi
		fi
	fi
}

# Anything printed by this script into stdout will be added as a pacman parameter to pacman -Syu
package-replaces() {
	local sequence
	local packages
	sequence="$(get_intervention_sequence)"

	if (( sequence < 1 )); then
		packages="$($PACMAN -Qq python-xdg garuda-dr460nized garuda-xfce-kwin-settings garuda-lxqt-kwin-settings garuda-wayfire-settings sweet-kde-git sweet-cursor-theme-git pipewire-media-session pipewire-support qemu-base virt-manager-meta libretro-meta libretro-mame-git jack2 pipewire-jack libpipewire-0.3.so=0-64 jre-openjdk-headless jre-openjdk jdk-openjdk pinta dotnet-runtime-7.0 kwin-x11 plasma-desktop plasma-x11-session "linux-firmware<20250613.12fe085f-5" garuda-rani-git garuda-settings-manager gnu-netcat 2>/dev/null | xargs || true)"
		# We replace python-xdg with python-pyxdg from extra
		# This is not done automatically for some reason
		if [[ "$packages" =~ (^| )python-xdg($| ) ]]; then
			echo python-pyxdg
		fi
		if [[ "$packages" =~ (^| )(garuda-dr460nized|garuda-xfce-kwin-settings|garuda-lxqt-kwin-settings|garuda-wayfire-settings)($| ) ]] && [[ "$packages" =~ (^| )(sweet-cursor-theme-git|sweet-kde-git)($| ) ]]; then
			echo --ignore
			echo sweet-kde-git,sweet-cursor-theme-git,kvantum-theme-sweet-git,sweet-gtk-theme-dark,plasma5-theme-sweet-git
		fi
		if [[ "$packages" =~ (^| )pipewire-media-session($| ) ]] && [[ "$packages" =~ (^| )pipewire-support($| ) ]]; then
			echo --ignore
			echo pipewire-media-session
		fi
		if [[ "$packages" =~ (^| )libretro-meta($| ) ]] && [[ "$packages" =~ (^| )libretro-mame-git($| ) ]]; then
			echo --ignore
			echo libretro-mame-git
		fi
		if [[ "$packages" =~ (^| )jack2($| ) ]] && [[ "$packages" =~ (^| )pipewire-jack($| ) ]]; then
			echo --ignore
			echo jack2,lib32-jack2,jack2-dbus
		fi
		if [[ "$packages" =~ (^| )pipewire($| ) ]]; then
			echo --ignore
			echo pipewire-common-git
		fi
		if [[ "$packages" =~ (^| )jdk-openjdk($| ) ]] && [[ "$packages" =~ (^| )jre-openjdk(-headless)?($| ) ]]; then
			echo --ignore
			echo jre-openjdk,jre-openjdk-headless
		elif [[ "$packages" =~ (^| )jre-openjdk($| ) ]] && [[ "$packages" =~ (^| )jre-openjdk-headless($| ) ]]; then
			echo --ignore
			echo jre-openjdk-headless
		fi
		# TODO: Remove this when pinta is updated to .NET 8 or pacman's dependency resolver is fixed lmao
		if [[ "$packages" =~ (^| )pinta($| ) ]] && ! [[ "$packages" =~ (^| )dotnet-runtime-7.0($| ) ]]; then
			echo dotnet-runtime-7.0
		fi
		# Detect if kwin-x11 is installed, we need to install plasma-x11-session to ensure
		# that the user can log into their desktop environment.
		# https://archlinux.org/news/plasma-640-will-need-manual-intervention-if-you-are-on-x11/
		if [[ "$packages" =~ (^| )kwin-x11($| ) ]] && [[ "$packages" =~ (^| )plasma-desktop($| ) ]] && ! [[ "$packages" =~ (^| )plasma-x11-session($| ) ]]; then
			echo plasma-x11-session
		fi
		# garuda-rani has replaces=(garuda-settings-manager). If garuda-rani-git is installed and has an update pending, the conflict prompt can not be shown
		if [[ "$packages" =~ (^| )garuda-rani-git($| ) ]] && [[ "$packages" =~ (^| )garuda-settings-manager($| ) ]]; then
			echo --ignore
			echo garuda-rani-git
		fi
		# Replace gnu-netcat with openbsd-netcat
		if [[ "$packages" =~ (^| )gnu-netcat($| ) ]]; then
			echo openbsd-netcat
		fi
	fi
	if (( sequence < 3 )); then
		packages="$($PACMAN -Qq "proton-ge-custom" 2>/dev/null | xargs || true)"
		if [[ "$packages" =~ (^| )proton-ge-custom($| ) ]]; then
			echo proton-ge-custom-bin
		fi
	fi
}

# Exit codes:
# 0: Everything is fine
# 1: Something went wrong
# 2: Keyring updated
pre-update-routines() {
	set -euo pipefail

	local exit_code=0
	update_keyring_packages || exit_code=2
	update_hotfixes || exit_code=1

	return $exit_code
}

post-update-routines() {
	set -euo pipefail
	local sequence
	sequence="$(get_intervention_sequence)"

	if (( sequence < 2 )); then
		if $PACMAN -Qq garuda-hotfixes >/dev/null 2>&1; then
			SNAP_PAC_SKIP=y SKIP_AUTOSNAP='' $PACMAN -R --noconfirm garuda-hotfixes
		fi
	fi

	update-intervention-sequence
}

end-routines() {
	set -euo pipefail
	# Update plocate index in the background
	if [ -x /usr/bin/locate ]; then
		systemctl start plocate-updatedb.service --no-block
	fi

	local TARGET_UID=""
	if [ -v SUDO_UID ]; then
		TARGET_UID="$SUDO_UID"
	elif [ -v PKEXEC_UID ]; then
		TARGET_UID="$PKEXEC_UID"
	fi

	if [ -n "$TARGET_UID" ]; then
		local TARGET_USER
		TARGET_USER="$(id -nu "$TARGET_UID")"
		# Update fish completions
		if [ -f /usr/lib/systemd/user/garuda-fish-completions-update.service ]; then
			systemctl --user -M "$TARGET_USER@.host" start garuda-fish-completions-update.service --no-block 2>&1 | awk '/Transport endpoint is not connected$/ { exit 0; } 1'
		fi

		# Rebuild bat cache
		if [ -f /usr/lib/systemd/user/garuda-bat-cache-rebuild.service ]; then
			systemctl --user -M "$TARGET_USER@.host" start garuda-bat-cache-rebuild.service --no-block 2>&1 | awk '/Transport endpoint is not connected$/ { exit 0; } 1'
		fi

		# Update micro plugins
		if [ -x /usr/bin/micro ]; then
			systemd-run --user -M "$TARGET_USER@.host" --no-block -G -q micro -plugin update
		fi
	fi
}

migrate-garuda-repo() {
	# Add garuda repo if it doesn't exist
	gawk -i inplace 'BEGIN {
        err=1
    }
    {
    if (rm)
    {
        if ($0 ~ /^ *(Include|Server) *=/)
        {
            next
        }
        # Check for empty line
        else if ($0 ~ /^ *$/)
        {
            next
        }
        else
        {
            rm=0
        }
    }
    if ($0 == "[options]")
    {
        print
        next
    }
    else if ($0 == "[garuda]")
    {
      if (set) {
        rm=1
        next
      }
      set=1
    }
    else if ($0 == "[testing]")
    {
        print "[core-testing]"
        err=0
        next
    }
    else if ($0 == "[community-testing]")
    {
        print "[extra-testing]"
        err=0
        next
    }
    else if ($0 == "[community]")
    {
        rm=1
        err=0
        next
    }
  }
  /^\[[^ \[\]]+\]/ {
    if (!set) {
        print "[garuda]"
        print "Include = /etc/pacman.d/chaotic-mirrorlist"
        print ""
        set=1
        err=0
    }
  }
  END {exit err}
  1' /etc/pacman.conf
}

update-intervention-sequence() {
	echo "# DO NOT MODIFY THIS FILE
# This file is used by garuda-update to determine which intervention steps (fixes) have already been completed.
# You have nothing to gain by modifying this file
INTERVENTION_SEQUENCE=${CURRENT_INTERVENTION_SEQUENCE}" > "$INTERVENTION_SEQUENCE_FILE"
}

"$@"
exit "$?"
