#!/bin/bash

# Exit on any error, undefined variables, and pipe failures
set -euo pipefail

# Script configuration
SCRIPT_NAME="$(basename "$0")"
LOGFILE_PREFIX="pkg-ohpc"
readonly SCRIPT_NAME
readonly LOGFILE_PREFIX

# Function to display usage information
usage() {
	cat <<EOF
Usage: ${SCRIPT_NAME} [-n] <version>

Options:
    -n    Use natural sorting for package lists
    -h    Display this help message

Arguments:
    version    OpenHPC version (e.g., 4.0, 3.1, 2.5)

Examples:
    ${SCRIPT_NAME} 4.0
    ${SCRIPT_NAME} -n 3.1
EOF
}

# Function to log messages with timestamp
log() {
	echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >&2
}

# Function to parse version components
parse_version() {
	local version="$1"

	# Validate version format
	if [[ ! "${version}" =~ ^[0-9]+\.[0-9]+(\.[0-9]+)?$ ]]; then
		log "ERROR: Invalid version format: ${version}"
		log "Expected format: X.Y or X.Y.Z (e.g., 4.0, 3.1.2)"
		exit 1
	fi

	major_ver="${version%%.*}"
	minor_ver="${version%.*}"
	micro_ver="${version##*.}"
	minor_dig="${version#*.}"
	minor_dig="${minor_dig%%.*}"

	# Handle cases where micro version is not specified
	if [[ "${micro_ver}" == "${minor_ver}" ]]; then
		micro_ver=0
	fi
}

# Function to detect base OS and architecture from current directory
detect_platform() {
	local pwd_path
	pwd_path="$(pwd)"

	# Detect architecture
	case "${pwd_path}" in
	*aarch64*) arch="aarch64" ;;
	*x86_64*) arch="x86_64" ;;
	*)
		log "ERROR: Unable to detect architecture from path: ${pwd_path}"
		log "Path must contain 'aarch64' or 'x86_64'"
		exit 1
		;;
	esac

	# Detect base OS based on version and path
	case "${major_ver}" in
	1)
		case "${minor_ver}" in
		1.0) [[ "${pwd_path}" == *centos7* ]] && baseos="CentOS_7.1" ;;
		1.1 | 1.2)
			case "${pwd_path}" in
			*sles12*) baseos="SLE_12_SP1" ;;
			*centos7*) baseos="CentOS_7.2" ;;
			esac
			;;
		1.3)
			case "${pwd_path}" in
			*sles12*) baseos="SLE_12" ;;
			*centos7*) baseos="CentOS_7" ;;
			esac
			;;
		esac
		;;
	2)
		case "${pwd_path}" in
		*leap15*) baseos="Leap_15" ;;
		*centos8*) baseos="CentOS_8" ;;
		*rocky8*) baseos="EL_8" ;;
		esac
		;;
	3)
		case "${pwd_path}" in
		*leap15*) baseos="Leap_15" ;;
		*rocky9*) baseos="EL_9" ;;
		*almalinux9*) baseos="EL_9" ;;
		*openeuler22.03*) baseos="openEuler_22.03" ;;
		esac
		;;
	4)
		case "${pwd_path}" in
		*rocky10*) baseos="EL_10" ;;
		*almalinux10*) baseos="EL_10" ;;
		*openeuler24.03*) baseos="openEuler_24.03" ;;
		esac
		;;
	*)
		log "ERROR: Unsupported major version: ${major_ver}"
		exit 1
		;;
	esac

	if [[ -z "${baseos:-}" ]]; then
		log "ERROR: Unable to determine base OS"
		log "Detected major version: ${major_ver}"
		log "Current path: ${pwd_path}"
		log "Supported combinations:"
		log "  v1.x: centos7, sles12"
		log "  v2.x: leap15, centos8, rocky8"
		log "  v3.x: leap15, rocky9, almalinux9, openeuler22.03"
		log "  v4.x: rocky10, almalinux10, openeuler24.03"
		exit 1
	fi
}

# Parse command line options
naturalSort=0

while getopts ":nh" opt; do
	case ${opt} in
	n)
		naturalSort=1
		;;
	h)
		usage
		exit 0
		;;
	\?)
		log "ERROR: Invalid option: -${OPTARG}"
		usage
		exit 1
		;;
	esac
done
shift $((OPTIND - 1))

# Validate arguments
if [[ $# -ne 1 ]]; then
	log "ERROR: Exactly one version argument required"
	usage
	exit 1
fi

version="$1"

# Parse version and detect platform
parse_version "${version}"
detect_platform

# Clean up old files
rm -f "${LOGFILE_PREFIX}".*

# Set up architecture-specific skip patterns
skip=""
if [[ "${arch}" == "aarch64" ]]; then
	log "Detected aarch64 architecture - excluding incompatible packages"
	skip="mvapich2|impi-ohpc|lmod-defaults-intel"
fi

# Configure repository URLs
readonly REPO_BASE_URL="http://repos.openhpc.community/.staging/OpenHPC"
repobase="${REPO_BASE_URL}/${major_ver}/${baseos}"
repoupdate=""

if [[ ${minor_dig} -gt 0 ]]; then
	repoupdate="${REPO_BASE_URL}/${major_ver}/update.${version}/${baseos}"
fi

# Include noarch packages in query
arch_query="${arch},noarch"

# Set locale for consistent sorting
export LC_COLLATE=C

log "Configuration:"
log "  Version: ${version} (major=${major_ver}, minor=${minor_ver}, micro=${micro_ver})"
log "  Base OS: ${baseos}"
log "  Architecture: ${arch}"
log "  Repository Base: ${repobase}"
if [[ -n "${repoupdate}" ]]; then
	log "  Repository Updates: ${repoupdate}"
fi
if [[ -n "${skip}" ]]; then
	log "  Excluded packages: ${skip}"
fi

# Function to execute DNF repoquery with common parameters
dnf_repoquery() {
	local query_format="$1"
	local pattern="$2"
	local use_updates="${3:-false}"

	local cmd_args=(
		"dnf" "repoquery"
		"--arch=${arch_query}"
		"--repofrompath=ohpc-base,${repobase}"
		"--repoid=ohpc-base"
	)

	if [[ "${use_updates}" == "true" && -n "${repoupdate}" ]]; then
		cmd_args+=(
			"--latest-limit" "1"
			"--repofrompath=ohpc-update,${repoupdate}"
			"--repoid=ohpc-update"
		)
	fi

	cmd_args+=(
		"--queryformat=${query_format}"
	)

	# Add pattern if specified, otherwise query all packages
	if [[ -n "${pattern}" && "${pattern}" != "*" ]]; then
		cmd_args+=("${pattern}")
	fi

	"${cmd_args[@]}"
}

# Function to filter packages based on skip patterns
filter_packages() {
	if [[ -n "${skip}" ]]; then
		grep -E -v "${skip}"
	else
		cat
	fi
}

# Query repository for OpenHPC packages
log "Querying OpenHPC packages..."

use_updates="false"
if [[ ${minor_dig} -gt 0 ]]; then
	use_updates="true"
fi

# Query all OpenHPC packages
dnf_repoquery '%{Name} %{Version} %{URL} %{Group} %{Summary}\n' '*' "${use_updates}" |
	grep -e "-ohpc\b" |
	filter_packages |
	sort >>"${LOGFILE_PREFIX}.all"

# Query meta-packages
log "Querying meta-packages..."
dnf_repoquery '%{Name} %{Group} %{Description}\n' 'ohpc*' "${use_updates}" |
	grep 'ohpc/meta-package' |
	cut -d' ' -f1,3- |
	grep -e "^ohpc-" |
	sort >pattern-ohpc.all

# Include ohpc-release package
log "Including ohpc-release package..."
ohpc_release_info=$(dnf_repoquery '%{Name} %{Version} %{URL} %{Group} %{Summary}\n' 'ohpc-release' "${use_updates}")
echo "${ohpc_release_info}" >>"${LOGFILE_PREFIX}.all"

# Process package groups
log "Processing package groups..."

# Extract unique package groups
groups=$(grep -o '\sohpc/[^ ]*' "${LOGFILE_PREFIX}.all" | sort -u | cut -d '/' -f 2)

if [[ -z "${groups}" ]]; then
	log "WARNING: No OpenHPC package groups found"
	exit 0
fi

echo
echo "++++ List of OpenHPC package groups"
echo "${groups}"
echo

# Generate group-specific package lists
for grp in ${groups}; do
	log "Processing group: ${grp}"

	if [[ ${naturalSort} -eq 1 ]]; then
		# Natural sorting (alphabetical)
		grep "ohpc/${grp}" "${LOGFILE_PREFIX}.all" | sort >"${LOGFILE_PREFIX}.${grp}"
	else
		# Version-aware sorting
		grep "ohpc/${grp}" "${LOGFILE_PREFIX}.all" | sort -k 1.1,1.2 -k2V >"${LOGFILE_PREFIX}.${grp}"
	fi

	# Log the number of packages in each group
	package_count=$(wc -l <"${LOGFILE_PREFIX}.${grp}")
	log "  Generated ${LOGFILE_PREFIX}.${grp} with ${package_count} packages"
done

log "Package listing completed successfully"
log "Generated files:"
log "  ${LOGFILE_PREFIX}.all - All OpenHPC packages"
log "  pattern-ohpc.all - Meta-packages"
for grp in ${groups}; do
	log "  ${LOGFILE_PREFIX}.${grp} - ${grp} packages"
done
