# Initialize color variables early
_color_reset=""
_color_green=""
_color_red=""
_color_yellow=""

colors="$(tput colors 2>/dev/null || echo 0)"
if ((colors >= 8)); then
	_color_reset="\033[0m"
	_color_green="\033[1;32m"
	_color_yellow="\033[1;33m"
	_color_red="\033[1;31m"
fi

readonly LIMINE_LOCK_FILE="/tmp/limine-global.lock"

info_msg() {
	echo "$1"
}

success_msg() {
	echo -e "${_color_green}SUCCESS: $1${_color_reset} ${2:-}"
}

warning_msg() {
	echo -e "${_color_yellow}WARNING: $1${_color_reset} ${2:-}" >&2
}

error_msg() {
	echo -e "${_color_red}ERROR: $1${_color_reset} ${2:-}" >&2
}

mutex_lock() {
	local name=$1
	exec 200>${LIMINE_LOCK_FILE} || {
		rm -f ${LIMINE_LOCK_FILE}
		exec 200>${LIMINE_LOCK_FILE}
	}
	flock --timeout=10 200 || {
		warning_msg "Mutex lock timeout on ${name}."
		return 1
	}
}

mutex_unlock() {
	# Release the lock
	flock --unlock 200
}

### Check if the system is using UEFI
check_uefi() {
	if [[ ! -d /sys/firmware/efi ]]; then
		return 1
	fi
	return 0
}

is_x64() {
	[[ "$(uname -m)" == "x86_64" ]]
}

### Load key=value config
load_key_value_config() {
	local file="$1"
	while IFS= read -r line || [[ -n "$line" ]]; do
		# Skip blank lines and comments
		[[ "$line" =~ ^[[:space:]]*(#.*)?$ ]] && continue

		# Match VAR = value
		if [[ "$line" =~ ^[[:space:]]*([A-Za-z_][A-Za-z0-9_]*)[[:space:]]*=[[:space:]]*(.*)$ ]]; then
			local var="${BASH_REMATCH[1]}"
			local val="${BASH_REMATCH[2]}"
			val="${val%\"}"
			val="${val#\"}"
			export "$var"="$val"
		fi
	done <"$file"
}

### Load configuration
load_config() {
	local usr_d_dir="/usr/share/limine-entry-tool.d"
	local etc_conf="/etc/limine-entry-tool.conf"
	local etc_d_dir="/etc/limine-entry-tool.d"
	# /etc/default/limine is the central config read by all Limine-based packages
	local default_conf="/etc/default/limine"

	# 1. Load all /usr/share/limine-entry-tool.d/*.conf (lowest priority)
	if [[ -d "$usr_d_dir" ]]; then
		for file in "$usr_d_dir"/*.conf; do
			[[ -f "$file" ]] || continue
			load_key_value_config "$file"
		done
	fi

	# 2. Load /etc/limine-entry-tool.conf
	if [[ -f "$etc_conf" ]]; then
		load_key_value_config "$etc_conf"
	fi

	# 3. Load all /etc/limine-entry-tool.d/*.conf
	if [[ -d "$etc_d_dir" ]]; then
		for file in "$etc_d_dir"/*.conf; do
			[[ -f "$file" ]] || continue
			load_key_value_config "$file"
		done
	fi

	# Do not use ENABLE_ENROLL_LIMINE_CONFIG in any random configs
	ENABLE_ENROLL_LIMINE_CONFIG=""

	# 4. Load /etc/default/limine (highest priority)
	if [[ -f "$default_conf" ]]; then
		load_key_value_config "$default_conf"
	fi

	# Detect ESP_PATH if not set
	if [[ -z "${ESP_PATH:-}" || -z "${ESP_PATH// /}" ]]; then
		if check_uefi && command -v bootctl &>/dev/null; then
			ESP_PATH=$(bootctl --print-esp-path 2>/dev/null)
			if [[ -z "${ESP_PATH}" ]]; then
				error_msg "Cannot detect an ESP path. Please set ESP_PATH in '$default_conf'."
				return 1
			fi
		else
			error_msg "Please set ESP_PATH in '$default_conf'."
			return 1
		fi
	fi
	return 0
}

### Check if a given path is a valid EFI System Partition (ESP)
check_esp() {
	local path="$1"

	### Check if the path exists and is a directory
	if [[ ! -d "$path" ]]; then
		error_msg "The specified ESP path '$path' does not exist or is not a directory."
		return 1
	fi

	### Check if the path is mounted
	if ! mountpoint -q "$path"; then
		error_msg "FAT32 boot partition is not mounted at '$path'."
		return 1
	fi

	### Check if the filesystem type is FAT (EFI System Partitions use FAT)
	local fsType
	read -r fsType < <(findmnt -n -o FSTYPE "$path")
	if [[ "$fsType" != "vfat" ]]; then
		error_msg "The mounted ESP '$path' is not of type 'FAT32'."
		return 1
	fi

	return 0
}

### Get machine-id
get_machine_id() {
	local machine_id_path="/etc/machine-id"

	# Generate if missing or empty
	if [[ ! -s "$machine_id_path" ]]; then
		info_msg "Generating machine-id at $machine_id_path..."
		uuidgen | tr -d '-' | head -c 32 >"$machine_id_path" || return 1
		# Set ownership and permissions
		chmod 444 "$machine_id_path"
		chown root:root "$machine_id_path"
	fi

	# Read and export
	# shellcheck disable=SC2034
	MACHINE_ID=$(<"$machine_id_path")
	return 0
}

### Get a version of Limine binary
get_limine_version() {
	local file=$1

	# Helper function:
	# Runs the given command with the file as argument,
	# checks if output contains "limine",
	# and extracts the version number if present.
	try_cmd() {
		local output version
		if output=$("$@" 2>/dev/null); then
			# Check if output contains 'limine'
			echo "$output" | grep -qi 'limine' || return 1

			# Extract version number like 9.5 or 9.5.0
			version=$(echo "$output" | grep -E '^[0-9]+\.[0-9]+(\.[0-9]+)?$' | head -n1 | tr -d '\r[:space:]')
			[[ -n $version ]] && {
				echo "$version"
				return 0
			}
		fi
		return 1
	}

	# Try with strings if available
	if command -v strings &>/dev/null && try_cmd strings "$file"; then
		return 0
	fi

	# Final fallback: grep -a to simulate strings
	if try_cmd grep -a -o '[[:print:]]\{4,\}' "$file"; then
		return 0
	fi

	return 1
}

get_limine_version_as_int() {
	local file=$1
	local version major minor patch

	version=$(get_limine_version "$file") || return 1

	# Split into major, minor, patch
	major=$(cut -d. -f1 <<<"$version")
	minor=$(cut -d. -f2 <<<"$version")
	patch=$(cut -d. -f3 <<<"$version")

	# Default fallback if parts are missing
	major=${major:-0}
	minor=${minor:-0}
	patch=${patch:-0}

	printf "%03d%03d%03d\n" "$major" "$minor" "$patch"
}

### Compare two files based on their embedded Limine version
### Output:
###   0 → source == target
###   1 → source > target
###   2 → source < target
###   3 → source invalid
compare_limine_versions() {
	local src="$1"
	local tgt="$2"

	local src_ver tgt_ver

	src_ver=$(get_limine_version_as_int "$src") || return 3
	tgt_ver=$(get_limine_version_as_int "$tgt") || tgt_ver=0

	# Strip leading zeros
	src_ver=$((10#$src_ver))
	tgt_ver=$((10#$tgt_ver))

	if ((src_ver == tgt_ver)); then
		return 0
	elif ((src_ver > tgt_ver)); then
		return 1
	else
		return 2
	fi
}

### Sync cached writes to FAT32 to prevent data loss in case of power failure or forced shutdown.
sync_file() {
	if [[ -f "${BINARY_TARGET_PATH}" ]]; then
		# Do not use the -d option, as it applies to Linux file systems but not to FAT32. Use -f instead.
		sync -f "${BINARY_TARGET_PATH}"
	fi
}

restore_limine_binary() {
	# Restore the binary file from backup
	if [[ ! -f "$BINARY_BACKUP_PATH" ]]; then
		#warning_msg "No backup of the Limine binary."
		return 1
	fi

	local backup_hash=""
	local target_hash=""

	# Check if b2sum is present
	if command -v b2sum &>/dev/null; then
		backup_hash=$(tar --to-command="b2sum" -xf "$BINARY_BACKUP_PATH" 2>/dev/null | awk '{print $1}')
		if [[ -f "$BINARY_TARGET_PATH" ]]; then
			target_hash=$(b2sum "$BINARY_TARGET_PATH" | awk '{print $1}')
		fi
	fi

	if [[ -z "$backup_hash" || "$backup_hash" != "$target_hash" ]]; then
		if ! tar -xf "$BINARY_BACKUP_PATH" --directory="${LIMINE_DIR_PATH}" "${LIMINE_EFI_FILE}"; then
			error_msg "Failed to restore '$BINARY_BACKUP_PATH' to '$BINARY_TARGET_PATH'."
		fi
	fi
}

reset_enroll_config() {
	is_x64 || return 0
	check_uefi || return 0
	if restore_limine_binary; then
		return 0
	fi
	if [[ -f "$BINARY_TARGET_PATH" ]]; then
		if ! limine enroll-config --reset "$BINARY_TARGET_PATH" --quiet; then
			error_msg "Failed to reset the enrolled config for '$BINARY_TARGET_PATH'."
			return 1
		fi
	fi
}

### Enroll Limine config into Limine binary file.
enroll_config() {
	is_x64 || return 0
	check_uefi || return 0
	restore_limine_binary
	if [[ -f "$LIMINE_CONFIG_PATH" && "$ENABLE_ENROLL_LIMINE_CONFIG" == "yes" ]]; then
		# Attempt to enroll the config file
		if ! limine enroll-config "$BINARY_TARGET_PATH" "$(b2sum "$LIMINE_CONFIG_PATH" | awk '{print $1}')"; then
			error_msg "Failed to enroll the config '$LIMINE_CONFIG_PATH' into '$BINARY_TARGET_PATH'."
			restore_limine_binary
		fi
	fi
	sync_file
}

### Main Header Logic
initialize_header() {
	load_config || return 1
	check_esp "$ESP_PATH" || return 1
	get_machine_id || return 1

	# Source and target file paths
	if [[ -n "${LIMINE_BINARY_PATH:-}" ]]; then
		export BINARY_SOURCE_PATH="$LIMINE_BINARY_PATH"
	else
		export BINARY_SOURCE_PATH="/usr/share/limine/BOOTX64.EFI"
	fi

	export LIMINE_DIR_PATH="${ESP_PATH}/EFI/limine/"
	export LIMINE_EFI_FILE="limine_x64.efi"
	export LIMINE_BACKUP_FILE="limine_x64.bak"

	export BINARY_TARGET_PATH="${LIMINE_DIR_PATH}${LIMINE_EFI_FILE}"
	export BINARY_BACKUP_PATH="${LIMINE_DIR_PATH}${LIMINE_BACKUP_FILE}"
	export BINARY_FALLBACK_PATH="${ESP_PATH}/EFI/BOOT/BOOTX64.EFI"

	export LIMINE_EFI_PATH="\\EFI\\limine\\${LIMINE_EFI_FILE}"
	export LIMINE_CONFIG_PATH="${ESP_PATH}/limine.conf"
	return 0
}
