#!/bin/bash
# This script runs herb-lint only on modified ERB files in git diff

set -e

# Display help message and exit
show_help() {
  echo "Usage: herb-lint-git [--local|--uncommitted|--branch|--against=REFSPEC] [--fix] [--modified-only]"
  echo ""
  echo "Options:"
  echo "  --local          Check changes to be pushed (diff against upstream)"
  echo "  --uncommitted    Check uncommitted changes (diff against HEAD)"
  echo "  --branch         Check changes in current branch (diff against master)"
  echo "  --against=REF    Check changes against specific git reference"
  echo "  --fix            Automatically fix auto-correctable offenses"
  echo "  --modified-only  Check only modified lines (filters JSON output)"
  echo "  --help, -h       Show this help message"
  echo ""
  echo "Default behavior: Check entire modified files"
  echo ""
  echo "GitLab CI example:"
  echo "  bin/herb-lint-git --against=origin/\$CI_MERGE_REQUEST_TARGET_BRANCH_NAME --modified-only"
  exit 0
}

# Parse command line arguments
AGAINST=""
LOCAL=false
UNCOMMITTED=false
BRANCH=false
FIX_FLAG=""
MODIFIED_ONLY=false

for arg in "$@"; do
  case $arg in
    --help|-h)
      show_help
      ;;
    --local)
      LOCAL=true
      ;;
    --uncommitted)
      UNCOMMITTED=true
      ;;
    --branch)
      BRANCH=true
      ;;
    --fix)
      FIX_FLAG="--fix"
      ;;
    --modified-only)
      MODIFIED_ONLY=true
      ;;
    --against=*)
      AGAINST="${arg#*=}"
      ;;
    *)
      echo "Unknown option: $arg"
      exit 1
      ;;
  esac
done

# Determine git diff command based on options
if [ "$LOCAL" = true ]; then
  DIFF_COMMAND="git diff @{upstream}...HEAD"
elif [ "$UNCOMMITTED" = true ]; then
  DIFF_COMMAND="git diff HEAD"
elif [ "$BRANCH" = true ]; then
  DIFF_COMMAND="git diff master...HEAD"
elif [ -n "$AGAINST" ]; then
  DIFF_COMMAND="git diff ${AGAINST}...HEAD"
else
  echo "Usage: herb-lint-git [--local|--uncommitted|--branch|--against=REFSPEC] [--fix] [--modified-only]"
  echo ""
  echo "Options:"
  echo "  --local          Check changes to be pushed (diff against upstream)"
  echo "  --uncommitted    Check uncommitted changes (diff against HEAD)"
  echo "  --branch         Check changes in current branch (diff against master)"
  echo "  --against=REF    Check changes against specific git reference"
  echo "  --fix            Automatically fix auto-correctable offenses"
  echo "  --modified-only  Check only modified lines (filters JSON output)"
  echo ""
  echo "Default behavior: Check entire modified files"
  echo ""
  echo "GitLab CI example:"
  echo "  bin/herb-lint-git --against=origin/\$CI_MERGE_REQUEST_TARGET_BRANCH_NAME"
  exit 1
fi

# Get list of modified ERB files
ERB_FILES=$($DIFF_COMMAND --diff-filter=AMT --name-only | grep -E '\.(erb|html\.erb)$' || true)

if [ -z "$ERB_FILES" ]; then
  echo "No ERB files changed"
  exit 0
fi

# Convert to array for proper handling of filenames with spaces
mapfile -t ERB_FILE_ARRAY <<< "$ERB_FILES"

if [ "$MODIFIED_ONLY" = true ]; then
  # Build hash of files and their changed line numbers
  declare -A files_and_lines

  # Parse git diff output
  current_file=""
  current_line_number=0

  while IFS= read -r line; do
    if [[ $line =~ ^\+\+\+\ b/(.+)$ ]]; then
      # New file in diff
      current_file="${BASH_REMATCH[1]}"
      current_line_number=0
      files_and_lines[$current_file]=""
    elif [[ $line =~ ^@@\ -[0-9]+(,[0-9]+)?\ \+([0-9]+)(,[0-9]+)?\ @@.* ]]; then
      # Hunk header - extract starting line number in new file
      current_line_number="${BASH_REMATCH[2]}"
    elif [[ $line =~ ^\+[^+] ]]; then
      # Line addition (starts with + but not +++ which is file marker)
      if [ -n "$current_file" ]; then
        files_and_lines[$current_file]+="$current_line_number "
      fi
      ((current_line_number++))
    elif [[ $line =~ ^\ .* ]]; then
      # Context line (unchanged)
      ((current_line_number++))
    fi
  done < <($DIFF_COMMAND --diff-filter=AMT)

  # Run herb-lint with JSON output
  # Filter to only the JSON portion (herb-lint may output warnings before the JSON)
  RAW_OUTPUT=$(pnpm exec herb-lint --json --no-color "${ERB_FILE_ARRAY[@]}" 2>&1 || true)
  JSON_OUTPUT=$(echo "$RAW_OUTPUT" | sed -n '/^{/,/^}$/p')

  # Parse JSON and filter offenses to only changed lines
  # Using jq to filter offenses
  if command -v jq &> /dev/null; then
    # Process each file's offenses
    found_offenses=false
    for file in "${ERB_FILE_ARRAY[@]}"; do
      if [ ! -f "$file" ]; then
        continue
      fi

      changed_lines="${files_and_lines[$file]}"
      if [ -z "$changed_lines" ]; then
        continue
      fi

      # Convert space-separated string to array
      IFS=' ' read -ra changed_lines_array <<< "$changed_lines"

      # Get just the filename for matching (herb-lint uses basename in JSON)
      filename=$(basename "$file")

      # Filter offenses for this file to only changed lines
      filtered=$(echo "$JSON_OUTPUT" | jq -r --arg filename "$filename" --arg filepath "$file" --argjson lines "$(printf '%s\n' "${changed_lines_array[@]}" | jq -R . | jq -s .)" '
        .offenses[] |
        select(.filename == $filename) |
        select(.location.start.line as $line | $lines | map(tonumber) | index($line)) |
        "\($filepath):\(.location.start.line):\(.location.start.column): \(.severity[0:1] | ascii_upcase): \(.code): \(.message)"
      ')

      if [ -n "$filtered" ]; then
        found_offenses=true
        echo "$filtered"
      fi
    done

    if [ "$found_offenses" = false ]; then
      echo "No offenses found on changed lines"
      exit 0
    fi
    exit 1
  else
    # Fallback: if jq not available, show raw JSON
    echo "Warning: jq not found, showing raw JSON output"
    echo "$JSON_OUTPUT"
    # Check if there were any offenses in the JSON
    if echo "$JSON_OUTPUT" | grep -q '"offenses":\s*\['; then
      exit 1
    fi
    exit 0
  fi

else
  # Default: Check entire modified files with --fix support
  echo "Linting entire modified ERB files:"
  for file in "${ERB_FILE_ARRAY[@]}"; do
    if [ -f "$file" ]; then
      echo "  $file"
    fi
  done

  # Execute herb-lint via pnpm
  pnpm exec herb-lint ${FIX_FLAG} "${ERB_FILE_ARRAY[@]}"
fi
