#!/usr/bin/env bash
# quality.sh — Local quality gate for GDScript projects.
# Usage: ./tools/quality.sh [--changed]
#   --changed  Only check changed .gd files (gdformat/gdlint only)
set -uo pipefail
IFS=$'\n\t'

ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT" || exit 1
LOG="logs/quality/latest"
mkdir -p "$LOG"

CHANGED=false
[[ "${1:-}" == "--changed" ]] && CHANGED=true
OVERALL=false
ERRORS=()
FIXES=()

# -- tool detection -----------------------------------------------------------
find_godot() {
  if [[ -n "${GODOT_BIN:-}" ]]; then echo "$GODOT_BIN"; return 0; fi
  for c in godot godot4; do command -v "$c" &>/dev/null && echo "$c" && return 0; done
  return 1
}

find_gdformat() {
  if command -v gdformat &>/dev/null; then echo "gdformat"; return 0; fi
  if python -m gdtoolkit.formatter --help &>/dev/null 2>&1; then echo "python -m gdtoolkit.formatter"; return 0; fi
  return 1
}

find_gdlint() {
  if command -v gdlint &>/dev/null; then echo "gdlint"; return 0; fi
  if python -m gdtoolkit.linter --help &>/dev/null 2>&1; then echo "python -m gdtoolkit.linter"; return 0; fi
  return 1
}

changed_gd() {
  git diff --name-only HEAD -- '*.gd' 2>/dev/null || true
}

res_path() {
  local p="$1"
  p="${p//\\//}"
  if [[ "$p" == "$ROOT"* ]]; then p="${p#"$ROOT"/}"; fi
  p="${p#./}"
  echo "res://$p"
}

contains_element() {
  local e; for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done; return 1
}

need_timeout() {
  command -v timeout &>/dev/null
}

# -- godot --------------------------------------------------------------------
GODOT="$(find_godot)" || true
if [[ -z "$GODOT" ]]; then
  echo "ERROR: Godot binary not found. Set GODOT_BIN or add godot/godot4 to PATH."
  exit 1
fi

# -- 1. gdformat --------------------------------------------------------------
fmt_tool=$(find_gdformat) || true
fmt_result="PASS"
: > "$LOG/gdformat.log"
if [[ -z "$fmt_tool" ]]; then
  fmt_result="SKIPPED"
  echo "gdformat not found — install: pip install gdtoolkit" > "$LOG/gdformat.log"
else
  if $CHANGED; then
    files=$(changed_gd)
    if [[ -z "$files" ]]; then
      echo "No changed .gd files to check." > "$LOG/gdformat.log"
    else
      total=0; bad=0
      while IFS= read -r f; do
        [[ -f "$f" ]] || continue
        total=$((total+1))
        if ! $fmt_tool --check "$f" >> "$LOG/gdformat.log" 2>&1; then
          bad=$((bad+1))
        fi
      done <<< "$files"
      echo "($bad/$total files need formatting)" >> "$LOG/gdformat.log"
    fi
  else
    $fmt_tool --check . >> "$LOG/gdformat.log" 2>&1 || true
  fi
fi

if grep -q "would reformat" "$LOG/gdformat.log" 2>/dev/null; then
  fmt_result="FAIL"
  while IFS= read -r line; do
    line="${line#would reformat }"
    rp=$(res_path "$line")
    ERRORS+=("$rp needs formatting")
  done < <(grep "would reformat" "$LOG/gdformat.log")
  FIXES+=("Run gdformat to auto-format files")
fi

# -- 2. gdlint ----------------------------------------------------------------
lint_tool=$(find_gdlint) || true
lint_result="PASS"
: > "$LOG/gdlint.log"
if [[ -z "$lint_tool" ]]; then
  lint_result="SKIPPED"
  echo "gdlint not found — install: pip install gdtoolkit" > "$LOG/gdlint.log"
else
  if $CHANGED; then
    files=$(changed_gd)
    if [[ -z "$files" ]]; then
      echo "No changed .gd files to lint." > "$LOG/gdlint.log"
    else
      while IFS= read -r f; do
        [[ -f "$f" ]] || continue
        $lint_tool "$f" >> "$LOG/gdlint.log" 2>&1 || true
      done <<< "$files"
    fi
  else
    $lint_tool . >> "$LOG/gdlint.log" 2>&1 || true
  fi
fi

if grep -qE "^[^ ]+:[0-9]+:" "$LOG/gdlint.log" 2>/dev/null; then
  lint_result="FAIL"
  while IFS= read -r line; do
    [[ "$line" =~ ^([^:]+):([0-9]+):([0-9]+):[^:]+:(.*) ]] || continue
    file="${BASH_REMATCH[1]}"
    lineno="${BASH_REMATCH[2]}"
    msg="${BASH_REMATCH[4]}"
    msg="${msg## }"
    rp=$(res_path "$file")
    ERRORS+=("$rp:$lineno $msg")
  done < <(grep -E "^[^ ]+:[0-9]+:" "$LOG/gdlint.log")

  while IFS= read -r line; do
    [[ "$line" =~ ^([^:]+):([0-9]+):([0-9]+):([^:]+):(.*) ]] || continue
    rule="${BASH_REMATCH[4]}"
    fname=$(basename "${BASH_REMATCH[1]}")
    FIXES+=("Fix $rule in $fname")
  done < <(grep -E "^[^ ]+:[0-9]+:" "$LOG/gdlint.log")
fi

# -- 3. godot headless check --------------------------------------------------
godot_result="PASS"
: > "$LOG/godot-check.log"
GODOT_CMD="$GODOT --headless --path \"$ROOT\" --check-only --quit"
if need_timeout; then
  if timeout 30 $GODOT_CMD >> "$LOG/godot-check.log" 2>&1; then
    godot_result="PASS"
  else
    ec=$?
    if [[ $ec -eq 124 ]]; then
      godot_result="PASS"
      echo "[timed out after 30s — project runs continuously]" >> "$LOG/godot-check.log"
    else
      godot_result="FAIL"
    fi
  fi
else
  if $GODOT --headless --path "$ROOT" --quit >> "$LOG/godot-check.log" 2>&1; then
    godot_result="PASS"
  else
    godot_result="FAIL"
  fi
fi
if [[ "$godot_result" == "FAIL" ]]; then
  grep -iE "error|warning|parse|syntax" "$LOG/godot-check.log" 2>/dev/null | head -20 | while IFS= read -r errline; do
    ERRORS+=("godot: $errline")
  done
  FIXES+=("Fix Godot parser errors")
fi

# -- 4. GUT (optional) --------------------------------------------------------
gut_result="SKIPPED"
: > "$LOG/gut.log"
if [[ -f "addons/gut/gut_cmdln.gd" ]]; then
  if $GODOT --headless --path "$ROOT" -s addons/gut/gut_cmdln.gd >> "$LOG/gut.log" 2>&1; then
    gut_result="PASS"
  else
    gut_result="FAIL"
    grep -iE "fail|error|assert" "$LOG/gut.log" 2>/dev/null | head -20 | while IFS= read -r errline; do
      ERRORS+=("gut: $errline")
    done
    FIXES+=("Fix failing GUT tests")
  fi
fi

# -- summary ------------------------------------------------------------------
if [[ "$fmt_result" == "FAIL" || "$lint_result" == "FAIL" || "$godot_result" == "FAIL" || "$gut_result" == "FAIL" ]]; then
  OVERALL=true
fi

echo ""
if $OVERALL; then
  echo "QUALITY RESULT: FAIL"
else
  echo "QUALITY RESULT: PASS"
fi
echo ""
echo "  gdformat     $fmt_result"
echo "  gdlint       $lint_result"
echo "  godot-check  $godot_result"
echo "  gut          $gut_result"

seen_err=()
for e in "${ERRORS[@]}"; do
  contains_element "$e" "${seen_err[@]}" && continue
  seen_err+=("$e")
  echo "    $e"
done

if $OVERALL && [[ ${#FIXES[@]} -gt 0 ]]; then
  echo ""
  echo "  NEXT FIX:"
  seen_fix=()
  idx=1
  for fix in "${FIXES[@]}"; do
    contains_element "$fix" "${seen_fix[@]}" && continue
    seen_fix+=("$fix")
    echo "    $idx. $fix"
    idx=$((idx+1))
  done
fi

echo ""
echo "Full logs:"
echo "  $LOG/"
echo ""

{
  echo "QUALITY RESULT: $($OVERALL && echo FAIL || echo PASS)"
  echo ""
  echo "  gdformat     $fmt_result"
  echo "  gdlint       $lint_result"
  echo "  godot-check  $godot_result"
  echo "  gut          $gut_result"
} > "$LOG/summary.txt"

$OVERALL && exit 1 || exit 0
