#!/usr/bin/env bash
#
# LOC Budget Checker — enforces the 500 LOC hard cap.
#
# Usage:
#   bash scripts/check-loc.sh --changed   # only git-changed files (pre-commit)
#   bash scripts/check-loc.sh --all       # all source files in repo
#   bash scripts/check-loc.sh --staged    # only staged files (for pre-commit hook)
#
# Exit codes:
#   0 — all files within budget
#   1 — one or more violations found

set -uo pipefail

MAX_LOC="${MAX_LOC:-500}"
ROOT_DIR="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
EXCEPTIONS_FILE="${ROOT_DIR}/.claude/rules/loc-exceptions.txt"

red()   { printf '\033[31m%s\033[0m\n' "$*"; }
green() { printf '\033[32m%s\033[0m\n' "$*"; }
yellow(){ printf '\033[33m%s\033[0m\n' "$*"; }

# Check if a file matches any exception pattern in loc-exceptions.txt
is_exempt() {
  local file="$1"
  [[ -f "$EXCEPTIONS_FILE" ]] || return 1

  while IFS= read -r line; do
    # Skip empty lines and comments
    [[ -z "$line" ]] && continue
    [[ "$line" =~ ^[[:space:]]*# ]] && continue

    # Extract glob pattern (everything before first |)
    local pattern="${line%%|*}"
    pattern="$(echo "$pattern" | xargs)"  # trim whitespace
    [[ -z "$pattern" ]] && continue

    # Match against the file path (supports ** globs)
    # shellcheck disable=SC2254
    case "$file" in
      $pattern) return 0 ;;
    esac
  done < "$EXCEPTIONS_FILE"
  return 1
}

# Check if file is a source file we care about
is_source_file() {
  local file="$1"
  case "$file" in
    *.py|*.go|*.ts|*.tsx) return 0 ;;
    *) return 1 ;;
  esac
}

# Skip non-source directories
should_skip_path() {
  local file="$1"
  case "$file" in
    */node_modules/*|*/.next/*|*/__pycache__/*|*/venv/*|*/.git/*) return 0 ;;
    *) return 1 ;;
  esac
}

main() {
  local mode="changed"
  case "${1:-}" in
    --changed) mode="changed" ;;
    --staged)  mode="staged" ;;
    --all)     mode="all" ;;
  esac

  cd "$ROOT_DIR"

  # Collect files based on mode
  local tmpfile
  tmpfile=$(mktemp)
  trap "rm -f '$tmpfile'" EXIT

  case "$mode" in
    staged)
      git diff --name-only --cached > "$tmpfile"
      ;;
    changed)
      { git diff --name-only --cached 2>/dev/null; git diff --name-only 2>/dev/null; git ls-files --others --exclude-standard 2>/dev/null; } | sort -u > "$tmpfile"
      ;;
    all)
      # Use find + xargs wc for speed, then filter >500 LOC only
      find . \( -name '*.py' -o -name '*.go' -o -name '*.ts' -o -name '*.tsx' \) \
        -not -path '*/node_modules/*' \
        -not -path '*/.next/*' \
        -not -path '*/__pycache__/*' \
        -not -path '*/venv/*' \
        -not -path '*/.git/*' \
        -exec wc -l {} + 2>/dev/null \
        | awk -v max="$MAX_LOC" '$1 > max && !/total$/ { sub(/^[[:space:]]*[0-9]+[[:space:]]*\.\//, ""); print }' > "$tmpfile"
      # For --all mode, we already have only the violating files
      # Override the check loop to just check exemptions
      local fast_violations=0
      local fast_checked=0
      local fast_exempted=0
      while IFS= read -r file; do
        [[ -z "$file" ]] && continue
        [[ -f "$file" ]] || continue
        if is_exempt "$file"; then
          fast_exempted=$((fast_exempted + 1))
          continue
        fi
        fast_checked=$((fast_checked + 1))
        local loc
        loc=$(wc -l < "$file" 2>/dev/null || echo 0)
        red "VIOLATION: $file ($loc LOC > $MAX_LOC)"
        fast_violations=$((fast_violations + 1))
      done < "$tmpfile"
      echo ""
      if (( fast_violations > 0 )); then
        red "$fast_violations file(s) exceed ${MAX_LOC} LOC budget. (violations: $fast_violations, exempted: $fast_exempted)"
        exit 1
      fi
      green "LOC budget check passed. (exempted: $fast_exempted)"
      exit 0
      ;;
  esac

  local violations=0
  local checked=0
  local exempted=0

  while IFS= read -r file; do
    [[ -z "$file" ]] && continue
    [[ -f "$file" ]] || continue
    is_source_file "$file" || continue
    should_skip_path "$file" && continue

    if is_exempt "$file"; then
      exempted=$((exempted + 1))
      continue
    fi

    checked=$((checked + 1))
    local loc
    loc=$(wc -l < "$file" 2>/dev/null || echo 0)

    if (( loc > MAX_LOC )); then
      red "VIOLATION: $file ($loc LOC > $MAX_LOC)"
      violations=$((violations + 1))
    fi
  done < "$tmpfile"

  echo ""
  if (( violations > 0 )); then
    red "$violations file(s) exceed ${MAX_LOC} LOC budget. (checked: $checked, exempted: $exempted)"
    exit 1
  fi

  green "LOC budget check passed. (checked: $checked, exempted: $exempted)"
  exit 0
}

main "$@"
