#!/usr/bin/env bash
#
# SPDX-License-Identifier: GPL-2.0
#
# Copyright (c) 2013-2026 Igor Pecovnik, igor@armbian.com
#
# This file is a part of the Armbian Build Framework
# https://github.com/armbian/build/

SHELLCHECK_VERSION=${SHELLCHECK_VERSION:-0.11.0} # https://github.com/koalaman/shellcheck/releases

SRC="$(
	cd "$(dirname "$0")/../.."
	pwd -P
)"
echo "SRC: ${SRC}"

DIR_SHELLCHECK="${SRC}/cache/tools/shellcheck"
mkdir -p "${DIR_SHELLCHECK}"

MACHINE="${BASH_VERSINFO[5]}"
case "$MACHINE" in
	*darwin*) SHELLCHECK_OS="darwin" ;;
	*linux*) SHELLCHECK_OS="linux" ;;
	*)
		echo "unknown os: $MACHINE"
		exit 3
		;;
esac

case "$MACHINE" in
	*aarch64*) SHELLCHECK_ARCH="aarch64" ;;
	*x86_64*) SHELLCHECK_ARCH="x86_64" ;;
	*)
		echo "unknown arch: $MACHINE"
		exit 2
		;;
esac

# https://github.com/koalaman/shellcheck/releases/download/v0.8.0/shellcheck-v0.8.0.darwin.x86_64.tar.xz
# https://github.com/koalaman/shellcheck/releases/download/v0.8.0/shellcheck-v0.8.0.linux.aarch64.tar.xz
# https://github.com/koalaman/shellcheck/releases/download/v0.8.0/shellcheck-v0.8.0.linux.x86_64.tar.xz

SHELLCHECK_FN="shellcheck-v${SHELLCHECK_VERSION}.${SHELLCHECK_OS}.${SHELLCHECK_ARCH}"
SHELLCHECK_FN_TARXZ="${SHELLCHECK_FN}.tar.xz"
DOWN_URL="${GITHUB_SOURCE:-"https://github.com"}/koalaman/shellcheck/releases/download/v${SHELLCHECK_VERSION}/${SHELLCHECK_FN_TARXZ}"
SHELLCHECK_BIN="${DIR_SHELLCHECK}/${SHELLCHECK_FN}"

if [[ ! -f "${SHELLCHECK_BIN}" ]]; then
	echo "Cache miss, downloading..."
	echo "MACHINE: ${MACHINE}"
	echo "Down URL: ${DOWN_URL}"
	echo "SHELLCHECK_BIN: ${SHELLCHECK_BIN}"
	curl -fLo "${SHELLCHECK_BIN}.tar.xz" "${DOWN_URL}" || {
		echo "download of shellcheck failed from ${DOWN_URL}"
		rm -f "${SHELLCHECK_BIN}.tar.xz"
		exit 1
	}
	tar -xf "${SHELLCHECK_BIN}.tar.xz" -C "${DIR_SHELLCHECK}" "shellcheck-v${SHELLCHECK_VERSION}/shellcheck"
	mv -v "${DIR_SHELLCHECK}/shellcheck-v${SHELLCHECK_VERSION}/shellcheck" "${SHELLCHECK_BIN}"
	rm -rf "${DIR_SHELLCHECK}/shellcheck-v${SHELLCHECK_VERSION}" "${SHELLCHECK_BIN}.tar.xz"
	chmod +x "${SHELLCHECK_BIN}"
fi
ACTUAL_VERSION="$("${SHELLCHECK_BIN}" --version | grep "^version")"

# Install-only mode: callers that just need the binary (CI workflows
# composing reviewdog / action-suggester on top of the framework's
# platform-aware download cache) can short-circuit the linting passes
# once SHELLCHECK_BIN is populated.
if [[ -n "${SHELLCHECK_INSTALL_ONLY}" ]]; then
	echo "SHELLCHECK_INSTALL_ONLY set, exiting after install. SHELLCHECK_BIN=${SHELLCHECK_BIN}"
	exit 0
fi

function calculate_params_for_severity() {
	declare SEVERITY="${SEVERITY:-"critical"}"

	params+=(--check-sourced --color=always --external-sources --format=tty --shell=bash)
	case "${SEVERITY}" in
		important)
			params+=("--severity=warning")
			excludes+=(
				"SC2034" # "appears unused" -- bad, but no-one will die of this
			)
			;;

		critical)
			params+=("--severity=warning")
			excludes+=(
				"SC2034" # "appears unused" -- bad, but no-one will die of this
				"SC2207" # "prefer mapfile" -- bad expansion, can lead to trouble; a lot of legacy pre-next code hits this
				"SC2046" # "quote this to prevent word splitting" -- bad expansion, variant 2, a lot of legacy pre-next code hits this
				"SC2086" # "quote this to prevent word splitting" -- bad expansion, variant 3, a lot of legacy pre-next code hits this
				"SC2206" # (warning): Quote to prevent word splitting/globbing, or split robustly with mapfile or read -a.
			)
			;;

		*)
			params+=("--severity=${SEVERITY}")
			;;
	esac

	for exclude in "${excludes[@]}"; do
		params+=(--exclude="${exclude}")
	done
}

declare -a problems=()

# formats:
# checkstyle -- some XML format
# gcc - one per line, compact references; does not show the source
# tty - default for checkstyle

cd "${SRC}" || exit 3

# config/sources errors
SEVERITY="error"
declare -a params=()
calculate_params_for_severity
declare -a config_files=()
declare -a config_source_files=()
declare -a config_board_files=()
mapfile -t config_source_files < <(find "${SRC}/config/sources" -type f -name "*.inc" -o -name "*.conf")
mapfile -t config_board_files < <(find "${SRC}/config/boards" -type f -name "*.wip" -o -name "*.tvb" -o -name "*.conf" -o -name "*.csc" -o -name "*.eos")
# add all elements of config_source_files and config_board_files to config_files
config_files=("${config_source_files[@]}" "${config_board_files[@]}")

echo "Running shellcheck ${ACTUAL_VERSION} against ${#config_files[@]} config files, severity 'SEVERITY=${SEVERITY}', please wait..."
#echo "All params: " "${params[@]}"
#echo "Checked files: " "${config_files[@]}"

if "${SHELLCHECK_BIN}" "${params[@]}" "${config_files[@]}"; then
	echo "Congrats, no ${SEVERITY}'s detected in config/sources."
else
	echo "Ooops, ${SEVERITY}'s detected in config/sources."
	problems+=("Ooops, ${SEVERITY}'s detected in config/sources.")
fi

# Lib/ code checks
SEVERITY="critical"
declare -a params=()
calculate_params_for_severity
echo "Running shellcheck ${ACTUAL_VERSION} against 'compile.sh' -- lib/ checks, severity 'SEVERITY=${SEVERITY}', please wait..."
echo "All params: " "${params[@]}"

if "${SHELLCHECK_BIN}" "${params[@]}" compile.sh; then
	echo "Congrats, no ${SEVERITY}'s detected in lib/ code."
else
	problems+=("Ooops, ${SEVERITY}'s detected in lib/ code.")
fi

# show the problems, if any
if [[ ${#problems[@]} -gt 0 ]]; then
	echo "Problems detected:"
	for problem in "${problems[@]}"; do
		echo " - ${problem}"
	done
	exit 1
fi