#!/usr/bin/bash
#
# tests/shellcheck.sh — enforce shellcheck (via Docker) on all repo shell scripts
#
# Usage:
#   bash tests/shellcheck.sh                # lint every .sh in the repo
#   bash tests/shellcheck.sh path/a.sh path/b.sh   # lint specific files
#
# Uses the koalaman/shellcheck:stable image (no native binary required).
# Exits non-zero if ANY script emits a diagnostic. Skips non-bash scripts
# (e.g. PHP files with a .sh extension) and the vendored/ trees.
#
set -uo pipefail

IMAGE="koalaman/shellcheck:stable"
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"

# --- Gather target files -----------------------------------------------------
mapfile -t FILES < <(
  if [ "$#" -gt 0 ]; then
    # Explicit args: resolve to repo-root-relative paths, keep only .sh
    for f in "$@"; do
      f_abs="$(cd "$(dirname "$f")" && pwd)/$(basename "$f")"
      case "$f_abs" in
        "$ROOT"/vendor/*) ;;                              # skip vendored
        "$ROOT"/archive/provisioning/Agents/librenms/*) ;; # upstream librenms-agent scripts
        "$ROOT"/archive/KNELServerBuild/*) ;;              # archived legacy code, not maintained [#474]
        *) case "$f_abs" in *.sh) echo "$f_abs";; esac ;;
      esac
    done
  else
    # Default: every .sh under the repo, minus vendor/
    while IFS= read -r f_abs; do
      case "$f_abs" in
        "$ROOT"/vendor/*) ;;                              # skip vendored
        "$ROOT"/archive/provisioning/Agents/librenms/*) ;; # upstream librenms-agent scripts
        "$ROOT"/archive/KNELServerBuild/*) ;;              # archived legacy code, not maintained [#474]
        *) echo "$f_abs" ;;
      esac
    done < <(find "$ROOT" -type f -name '*.sh' \
               -not -path '*/.git/*' -not -path "$ROOT/vendor/*" \
               -not -path "$ROOT/.crush/*")
  fi
)

if [ "${#FILES[@]}" -eq 0 ]; then
  echo "shellcheck.sh: no .sh files to check." >&2
  exit 0
fi

# --- Filter out non-bash scripts (e.g. PHP with a .sh extension) --------------
BASH_FILES=()
for f in "${FILES[@]}"; do
  shebang=$(head -c 64 "$f" 2>/dev/null | head -n1)
  case "$shebang" in
    \#!/usr/bin/php*|\#!/usr/bin/env\ php*) ;;    # PHP, skip
    \#!*) BASH_FILES+=("$f") ;;                   # any other shebang → check
    *)    BASH_FILES+=("$f") ;;                   # no shebang → check anyway
  esac
done

if [ "${#BASH_FILES[@]}" -eq 0 ]; then
  echo "shellcheck.sh: no bash scripts among targets." >&2
  exit 0
fi

# --- Run shellcheck in Docker (mount repo root, pass root-relative paths) -----
# shellcheck disable=SC2012 # basename loop is intentional
REL=()
for f in "${BASH_FILES[@]}"; do REL+=("${f#"$ROOT"/}"); done

# Disable checks that are intentional conventions of this codebase (not bugs):
#   SC1090/SC1091 — cannot follow dynamically-computed `source` paths (KNEL framework)
#   SC2029        — ssh orchestration deliberately expands the command client-side
DISABLES=(
  -e SC1090
  -e SC1091
  -e SC2029
)

echo "Checking ${#BASH_FILES[@]} script(s) with $IMAGE:"
printf '  %s\n' "${REL[@]}"

docker run --rm \
  -v "$ROOT:/mnt:ro" \
  -w /mnt \
  "$IMAGE" -x "${DISABLES[@]}" "${REL[@]}"
rc=$?

if [ "$rc" -eq 0 ]; then
  echo "shellcheck: PASS (${#BASH_FILES[@]} scripts clean)"
else
  echo "shellcheck: FAIL (fix the findings above or add targeted disable directives)" >&2
fi
exit "$rc"
