#!/usr/bin/env bash
set -euo pipefail

# Runs clang-tidy and cppcheck for the initial scope (lib/ and include/)
# Expectation: compile_commands.json exists in repo root (generated by generate-compile-commands.sh)

CLANG_TIDY_BIN=${CLANG_TIDY_BIN:-clang-tidy}
CPPCHECK_BIN=${CPPCHECK_BIN:-cppcheck}

if [ ! -f compile_commands.json ]; then
  echo "compile_commands.json not found. Run scripts/lint/generate-compile-commands.sh first." >&2
  exit 1
fi

mkdir -p lint
echo "Running clang-tidy per-file (this runs each source file one at a time)..."

## Accept optional file arguments. If files are provided as arguments, lint only those.
if [ "$#" -gt 0 ]; then
  FILES=("$@")
else
  # Build list of C/C++ source files under lib tracked by git
  FILES=()
  while IFS= read -r f; do
    case "$f" in
      *.cpp|*.cc|*.cxx|*.c)
        FILES+=("$f") ;;
    esac
  done < <(git ls-files lib)
fi

if [ ${#FILES[@]} -eq 0 ]; then
  echo "No source files found to lint under lib/" >&2
else
  TMPD=$(mktemp -d)
  # Compute an anchored header-filter that limits diagnostics to files inside
  # this repository's include/ and lib/ directories. Use an absolute path so
  # headers in deps/ that happen to contain '/include/' don't match.
  REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || echo "$(pwd)")
  HEADER_FILTER="^${REPO_ROOT}/(include|lib)/"
  echo "Using clang-tidy header-filter: ${HEADER_FILTER}"
  n=0
  total=${#FILES[@]}
  for f in "${FILES[@]}"; do
      n=$((n+1))
      echo "[$n/$total] clang-tidy $f"
      # Capture textual diagnostics per-file; still write export-fixes when available
    ${CLANG_TIDY_BIN} -p . -checks='clang-analyzer-*,bugprone-*,performance-*,modernize-*,readability-*' --header-filter="${HEADER_FILTER}" --export-fixes="${TMPD}/ct-${n}.yaml" "$f" 2> "${TMPD}/ct-${n}.stderr" || true
  done

  echo "Merging per-file clang-tidy fixes into lint/clang-tidy-fixes.yaml"
  # Merge all per-file YAMLs into a single Diagnostics list. Missing or empty files are ignored.
  python3 - "${TMPD}" <<'PY'
import sys, yaml, glob
tmp = sys.argv[1]
out = []
for p in sorted(glob.glob(tmp + '/*.yaml')):
    try:
        data = yaml.safe_load(open(p))
    except Exception:
        continue
    if not data:
        continue
    out.extend(data.get('Diagnostics', []))
yaml.safe_dump({'Diagnostics': out}, open('lint/clang-tidy-fixes.yaml', 'w'))
PY

  # Keep per-file stderr outputs around until after normalization so we can
  # fall back to parsing textual diagnostics when export-fixes is empty.
  # We'll clean up TMPD after normalization below.
fi

echo "Running cppcheck..."
${CPPCHECK_BIN} --enable=warning,performance,portability,style --std=c++17 --project=compile_commands.json --inline-suppr 2> lint/cppcheck-output.txt || true

echo "Normalizing outputs..."
# If export-fixes YAML produced diagnostics use that; otherwise merge textual stderr outputs
if [ -f lint/clang-tidy-fixes.yaml ]; then
  python3 scripts/lint/normalize-clang-tidy.py lint/clang-tidy-fixes.yaml > lint/clang-tidy.txt || true
else
  # Merge all per-file stderr clang-tidy outputs if present
  if ls ${TMPD:-/dev/null}/ct-*.stderr >/dev/null 2>&1; then
    cat ${TMPD}/ct-*.stderr > lint/clang-tidy-raw.txt || true
    python3 scripts/lint/normalize-clang-tidy.py lint/clang-tidy-raw.txt > lint/clang-tidy.txt || true
  else
    # Nothing found
    :
  fi
fi

# Clean up temporary directory created for per-file clang-tidy runs
if [ -n "${TMPD:-}" ] && [ -d "${TMPD}" ]; then
  rm -rf "${TMPD}"
fi
python3 scripts/lint/normalize-cppcheck.py lint/cppcheck-output.txt > lint/cppcheck.txt || true

echo "Local lint run complete. Reports written to lint/clang-tidy.txt and lint/cppcheck.txt"
