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

usage() {
  cat <<'USAGE'
Usage: scripts/check-cache-impact.sh [changed-file ...]

Checks that PRs touching cache-sensitive prompt or tool surfaces include an
explicit cache-impact note and guard-test note in the pull request body.

Inputs:
  CACHE_IMPACT_PR_BODY or PR_BODY          Pull request body text.
  CACHE_IMPACT_PR_BODY_FILE               File containing the pull request body.
  CACHE_IMPACT_CHANGED_FILES              Newline-separated changed files.
  CACHE_IMPACT_CHANGED_FILES_FILE         File containing newline-separated changed files.
  CACHE_IMPACT_BASE_SHA / BASE_SHA        Diff base when files are not supplied.
  CACHE_IMPACT_HEAD_SHA / HEAD_SHA        Diff head when files are not supplied.
USAGE
}

if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
  usage
  exit 0
fi

body="${CACHE_IMPACT_PR_BODY:-${PR_BODY:-}}"
if [[ -n "${CACHE_IMPACT_PR_BODY_FILE:-}" ]]; then
  body="$(cat "$CACHE_IMPACT_PR_BODY_FILE")"
fi

changed_input=""
if [[ "$#" -gt 0 ]]; then
  changed_input="$(printf '%s\n' "$@")"
elif [[ -n "${CACHE_IMPACT_CHANGED_FILES_FILE:-}" ]]; then
  changed_input="$(cat "$CACHE_IMPACT_CHANGED_FILES_FILE")"
elif [[ -n "${CACHE_IMPACT_CHANGED_FILES:-}" ]]; then
  changed_input="$CACHE_IMPACT_CHANGED_FILES"
else
  base="${CACHE_IMPACT_BASE_SHA:-${BASE_SHA:-}}"
  head="${CACHE_IMPACT_HEAD_SHA:-${HEAD_SHA:-HEAD}}"
  if [[ -z "$base" ]]; then
    base="$(git merge-base origin/main-v2 "$head" 2>/dev/null || git merge-base main-v2 "$head")"
  fi
  diff_base="$base"
  if merge_base="$(git merge-base "$base" "$head" 2>/dev/null)"; then
    diff_base="$merge_base"
  fi
  changed_input="$(git diff --name-only "$diff_base" "$head")"
fi

changed_files=()
while IFS= read -r file; do
  [[ -z "$file" ]] && continue
  changed_files+=("$file")
done <<< "$changed_input"

cache_sensitive=()
system_prompt_sensitive=()
standing_instruction_changed=()

for file in "${changed_files[@]:-}"; do
  case "$file" in
    REASONIX.md|AGENTS.md|CLAUDE.md|\
    REASONIX.local.md|AGENTS.local.md|CLAUDE.local.md|\
    */REASONIX.md|*/AGENTS.md|*/CLAUDE.md|\
    */REASONIX.local.md|*/AGENTS.local.md|*/CLAUDE.local.md)
      standing_instruction_changed+=("$file")
      cache_sensitive+=("$file")
      system_prompt_sensitive+=("$file")
      ;;
    desktop/session_prompt.go|\
    internal/agent/agent.go|\
    internal/agent/ask.go|\
    internal/agent/cache*|\
    internal/agent/compact*|\
    internal/agent/goal_display.go|\
    internal/agent/parallel_tasks.go|\
    internal/agent/planner_registry.go|\
    internal/agent/prune*|\
    internal/agent/subagent_registry*|\
    internal/agent/subagent_identity.go|\
    internal/agent/task.go|\
    internal/boot/*|\
    internal/command/slashtool.go|\
    internal/config/config.go|\
    internal/config/system_prompt*|\
    internal/control/goal.go|\
    internal/control/input.go|\
    internal/control/turn_orchestrator.go|\
    internal/environment/*|\
    internal/goaleval/*|\
    internal/history/tool.go|\
    internal/installsource/*|\
    internal/lsp/tool.go|\
    internal/memory/*|\
    internal/outputstyle/*|\
    internal/plugin/*|\
    internal/provider/*|\
    internal/skill/*|\
    internal/taskintent/*|\
    internal/tool/*|\
    scripts/cache-guard.sh|\
    scripts/check-cache-impact.sh)
      cache_sensitive+=("$file")
      ;;
  esac

  case "$file" in
    desktop/session_prompt.go|\
    internal/agent/task.go|\
    internal/boot/*|\
    internal/config/config.go|\
    internal/config/system_prompt*|\
    internal/environment/*|\
    internal/memory/*|\
    internal/outputstyle/*|\
    internal/skill/*)
      system_prompt_sensitive+=("$file")
      ;;
  esac
done

if [[ "${#cache_sensitive[@]}" -eq 0 ]]; then
  echo "No cache-sensitive prompt/tool files changed."
  exit 0
fi

failures=()

trim() {
  local s="$1"
  s="${s#"${s%%[![:space:]]*}"}"
  s="${s%"${s##*[![:space:]]}"}"
  printf '%s' "$s"
}

field_value() {
  local label="$1"
  local line
  line="$(printf '%s\n' "$body" | grep -Eim1 "^[[:space:]>#*_-]*${label}[[:space:]]*:" || true)"
  [[ -z "$line" ]] && return 1
  trim "${line#*:}"
}

require_field() {
  local label="$1"
  local value
  if ! value="$(field_value "$label")"; then
    failures+=("missing ${label}: line")
    return
  fi
  if [[ -z "$value" || "$value" =~ ^[Tt][Oo][Dd][Oo]($|[[:space:]:-]) || "$value" =~ ^[Tt][Bb][Dd]($|[[:space:]:-]) ]]; then
    failures+=("${label}: must be filled out")
  fi
}

require_review_field() {
  local label="$1"
  local value
  if ! value="$(field_value "$label")"; then
    failures+=("missing ${label}: line")
    return
  fi
  local lower
  lower="$(printf '%s' "$value" | tr '[:upper:]' '[:lower:]')"
  if [[ -z "$value" || "$lower" =~ ^todo($|[[:space:]:-]) || "$lower" =~ ^tbd($|[[:space:]:-]) || "$lower" =~ ^n/?a($|[[:space:]:-]) || "$lower" =~ ^none($|[[:space:]:-]) ]]; then
    failures+=("${label}: must name the explicit system-prompt review/approval")
  fi
}

require_field "Cache-impact"
require_field "Cache-guard"

if [[ "${#standing_instruction_changed[@]}" -gt 0 ]]; then
  cache_impact="$(field_value "Cache-impact" || true)"
  cache_impact_lower="$(printf '%s' "$cache_impact" | tr '[:upper:]' '[:lower:]')"
  if [[ "$cache_impact_lower" =~ ^none($|[[:space:]:-]) ]]; then
    failures+=("Cache-impact: cannot be none when standing instruction files change")
  fi
fi

if [[ "${#system_prompt_sensitive[@]}" -gt 0 ]]; then
  require_review_field "System-prompt-review"
fi

if [[ "${#failures[@]}" -gt 0 ]]; then
  {
    echo "Cache impact check failed."
    echo
    echo "Cache-sensitive files changed:"
    printf '  - %s\n' "${cache_sensitive[@]}"
    if [[ "${#system_prompt_sensitive[@]}" -gt 0 ]]; then
      echo
      echo "System-prompt-sensitive files changed:"
      printf '  - %s\n' "${system_prompt_sensitive[@]}"
    fi
    echo
    echo "Required PR body lines:"
    echo "  Cache-impact: <none|low|medium|high> - <reason>"
    echo "  Cache-guard: <focused guard test/command or existing guard rationale>"
    if [[ "${#system_prompt_sensitive[@]}" -gt 0 ]]; then
      echo "  System-prompt-review: <reviewer/approval note>"
    fi
    echo
    echo "Failures:"
    printf '  - %s\n' "${failures[@]}"
  } >&2
  exit 1
fi

echo "Cache impact check passed."
echo "Cache-sensitive files:"
printf '  - %s\n' "${cache_sensitive[@]}"
