#!/bin/bash

# afterFileEdit/stop hook: remind agents to review React UI source changes

set -u

input="$(cat)"

skill_dir=""
scope_prefixes=()

while [ "$#" -gt 0 ]; do
  case "$1" in
    --skill-dir)
      skill_dir="${2:-}"
      shift 2
      ;;
    --scope-prefix)
      scope_prefixes+=("${2:-}")
      shift 2
      ;;
    *)
      shift
      ;;
  esac
done

repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
cd "$repo_root" || exit 0

extract_file_path() {
  if command -v jq >/dev/null 2>&1; then
    printf '%s' "$input" | jq -r '.file_path // empty' 2>/dev/null
    return
  fi

  echo "$input" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*:.*"\([^"]*\)"/\1/'
}

is_source_file() {
  case "$1" in
    *.js|*.jsx|*.ts|*.tsx|*.mjs|*.cjs) return 0 ;;
    *) return 1 ;;
  esac
}

matches_scope() {
  local candidate="$1"

  if [ "${#scope_prefixes[@]}" -eq 0 ]; then
    return 0
  fi

  local prefix
  for prefix in "${scope_prefixes[@]}"; do
    case "$candidate" in
      "$prefix"*) return 0 ;;
    esac
  done

  return 1
}

is_react_ui_source_file() {
  case "$1" in
    src/components/*|src/views/*|src/hooks/*|src/stores/*) return 0 ;;
    *) return 1 ;;
  esac
}

parse_matches_from_diff() {
  awk '
    /^\+\+\+ b\// {
      file = substr($0, 7)
      next
    }

    /^\+[^+]/ {
      line = substr($0, 2)
      if (line ~ /(^|[^[:alnum:]_])(useEffect|useLayoutEffect|useInsertionEffect|useMemo|useCallback)[[:space:]]*[(<]/ || line ~ /(^|[^[:alnum:]_])React\.(useEffect|useLayoutEffect|useInsertionEffect|useMemo|useCallback|memo)[[:space:]]*[(<]/ || line ~ /(^|[^[:alnum:]_])memo[[:space:]]*[(<]/) {
        print file ": " line
      }
    }
  '
}

scan_untracked_file() {
  local file_path="$1"

  [ -f "$file_path" ] || return 0

  awk -v file="$file_path" '
    {
      line = $0
      if (line ~ /(^|[^[:alnum:]_])(useEffect|useLayoutEffect|useInsertionEffect|useMemo|useCallback)[[:space:]]*[(<]/ || line ~ /(^|[^[:alnum:]_])React\.(useEffect|useLayoutEffect|useInsertionEffect|useMemo|useCallback|memo)[[:space:]]*[(<]/ || line ~ /(^|[^[:alnum:]_])memo[[:space:]]*[(<]/) {
        print file ": " line
      }
    }
  ' "$file_path"
}

append_results() {
  local existing="$1"
  local incoming="$2"

  if [ -z "$incoming" ]; then
    printf '%s' "$existing"
    return
  fi

  if [ -z "$existing" ]; then
    printf '%s' "$incoming"
    return
  fi

  printf '%s\n%s' "$existing" "$incoming"
}

append_file_if_react_ui_source() {
  local existing="$1"
  local file_path="$2"

  if is_source_file "$file_path" && matches_scope "$file_path" && is_react_ui_source_file "$file_path"; then
    append_results "$existing" "$file_path"
    return
  fi

  printf '%s' "$existing"
}

results=""
react_source_files=""
file_path="$(extract_file_path)"

if [ -n "$file_path" ]; then
  if is_source_file "$file_path" && matches_scope "$file_path"; then
    react_source_files="$(append_file_if_react_ui_source "$react_source_files" "$file_path")"
    if git ls-files --others --exclude-standard -- "$file_path" | grep -q '.'; then
      results="$(scan_untracked_file "$file_path")"
    else
      diff_output="$(git diff --no-ext-diff --unified=0 --no-color HEAD -- "$file_path" 2>/dev/null || true)"
      results="$(printf '%s\n' "$diff_output" | parse_matches_from_diff)"
    fi
  fi
else
  diff_output="$(git diff --no-ext-diff --unified=0 --no-color HEAD -- '*.js' '*.jsx' '*.ts' '*.tsx' '*.mjs' '*.cjs' 2>/dev/null || true)"
  results="$(printf '%s\n' "$diff_output" | parse_matches_from_diff)"

  while IFS= read -r changed_file; do
    [ -z "$changed_file" ] && continue
    react_source_files="$(append_file_if_react_ui_source "$react_source_files" "$changed_file")"
  done < <(git diff --name-only --diff-filter=ACMRT HEAD -- 'src/components' 'src/views' 'src/hooks' 'src/stores' 2>/dev/null || true)

  while IFS= read -r untracked_file; do
    [ -z "$untracked_file" ] && continue
    is_source_file "$untracked_file" || continue
    matches_scope "$untracked_file" || continue
    react_source_files="$(append_file_if_react_ui_source "$react_source_files" "$untracked_file")"
    file_results="$(scan_untracked_file "$untracked_file")"
    results="$(append_results "$results" "$file_results")"
  done < <(git ls-files --others --exclude-standard -- '*.js' '*.jsx' '*.ts' '*.tsx' '*.mjs' '*.cjs')
fi

results="$(printf '%s\n' "$results" | sed '/^$/d' | awk '!seen[$0]++')"
react_source_files="$(printf '%s\n' "$react_source_files" | sed '/^$/d' | awk '!seen[$0]++')"

if [ -z "$results" ] && [ -z "$react_source_files" ]; then
  exit 0
fi

effect_skill="you-might-not-need-an-effect"
if [ -n "$skill_dir" ] && [ -f "$repo_root/$skill_dir/you-might-not-need-an-effect/SKILL.md" ]; then
  effect_skill="$repo_root/$skill_dir/you-might-not-need-an-effect/SKILL.md"
fi

vercel_skill="vercel-react-best-practices"
if [ -n "$skill_dir" ] && [ -f "$repo_root/$skill_dir/vercel-react-best-practices/SKILL.md" ]; then
  vercel_skill="$repo_root/$skill_dir/vercel-react-best-practices/SKILL.md"
fi

echo "=== React Best Practices Review Reminder ==="

if [ -n "$react_source_files" ]; then
  echo "React UI source changed in the current diff:"

  file_count=0
  while IFS= read -r changed_file; do
    [ -z "$changed_file" ] && continue
    file_count=$((file_count + 1))
    if [ "$file_count" -le 10 ]; then
      echo "- $changed_file"
    fi
  done <<< "$react_source_files"

  if [ "$file_count" -gt 10 ]; then
    echo "- ... and $((file_count - 10)) more"
  fi

  echo "Before finishing, review the changed diff with:"
  echo "- $vercel_skill"
  echo "- vercel:react-best-practices, when available in the current harness"
fi

if [ -n "$results" ]; then
  echo "New React effect or memo primitives were also added in the current diff:"

  match_count=0
  while IFS= read -r match_line; do
    [ -z "$match_line" ] && continue
    match_count=$((match_count + 1))
    if [ "$match_count" -le 10 ]; then
      echo "- $match_line"
    fi
  done <<< "$results"

  if [ "$match_count" -gt 10 ]; then
    echo "- ... and $((match_count - 10)) more"
  fi

  echo "Also reconsider effect/memo usage with:"
  echo "- $effect_skill"
fi

echo "Questions to resolve before finishing:"
echo "- Does the TSX avoid inline object/array prop churn and unnecessary component work?"
echo "- Can this be derived during render instead of synchronized with an effect?"
echo "- Can interaction logic move to an event handler or a key-based reset?"
echo "- Is the memoization actually needed, or is simpler render-time code better?"

exit 0
