#!/bin/bash
# Ralph pentru Claude Code - Loop autonom de agent AI (W3: smart gates + DAG + rate limit)
# Adaptat din Ralph original (snarktank/ralph) pentru Claude Code CLI
# Usage: ./ralph.sh [max_iterations] [project_dir]
#
# Env vars (opționale):
#   RALPH_MAX_TURNS        — --max-turns per iter (default 30)
#   RALPH_RATE_LIMIT_SLEEP — sleep după rate limit detection (default 1800 = 30min)
#   RALPH_DAG_HELPER       — path la tools/ralph_dag.py (auto-detect default)
#   RALPH_PYTHON           — interpreter Python pentru DAG helper (default python3)

set -e

MAX_ITERATIONS=${1:-10}
PROJECT_DIR=${2:-$(pwd)}
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PRD_FILE="$SCRIPT_DIR/prd.json"
PROGRESS_FILE="$SCRIPT_DIR/progress.txt"
ARCHIVE_DIR="$SCRIPT_DIR/archive"
SCREENSHOTS_DIR="$SCRIPT_DIR/screenshots"
LAST_BRANCH_FILE="$SCRIPT_DIR/.last-branch"
PROMPT_FILE="$SCRIPT_DIR/prompt.md"

# W3 config
MAX_TURNS=${RALPH_MAX_TURNS:-30}
RATE_LIMIT_SLEEP=${RALPH_RATE_LIMIT_SLEEP:-1800}
RALPH_PYTHON=${RALPH_PYTHON:-python3}

# DAG helper auto-detect: prefer co-located cu echo-core; fallback la $SCRIPT_DIR
if [ -n "$RALPH_DAG_HELPER" ] && [ -f "$RALPH_DAG_HELPER" ]; then
  DAG_HELPER="$RALPH_DAG_HELPER"
elif [ -f "/home/moltbot/echo-core/tools/ralph_dag.py" ]; then
  DAG_HELPER="/home/moltbot/echo-core/tools/ralph_dag.py"
elif [ -f "/home/moltbot/echo-core-qc/tools/ralph_dag.py" ]; then
  DAG_HELPER="/home/moltbot/echo-core-qc/tools/ralph_dag.py"
elif [ -f "$SCRIPT_DIR/ralph_dag.py" ]; then
  DAG_HELPER="$SCRIPT_DIR/ralph_dag.py"
else
  DAG_HELPER=""
fi

# Usage helper auto-detect (rate limit budget tracking — best effort, niciodată
# blochează rularea Ralph dacă lipsește)
if [ -n "$RALPH_USAGE_HELPER" ] && [ -f "$RALPH_USAGE_HELPER" ]; then
  USAGE_HELPER="$RALPH_USAGE_HELPER"
elif [ -f "/home/moltbot/echo-core/tools/ralph_usage.py" ]; then
  USAGE_HELPER="/home/moltbot/echo-core/tools/ralph_usage.py"
elif [ -f "/home/moltbot/echo-core-instr/tools/ralph_usage.py" ]; then
  USAGE_HELPER="/home/moltbot/echo-core-instr/tools/ralph_usage.py"
elif [ -f "$SCRIPT_DIR/ralph_usage.py" ]; then
  USAGE_HELPER="$SCRIPT_DIR/ralph_usage.py"
else
  USAGE_HELPER=""
fi
USAGE_FILE="$SCRIPT_DIR/usage.jsonl"

# Verifică că jq este instalat
if ! command -v jq &> /dev/null; then
    echo "Eroare: jq nu este instalat. Rulează: apt install jq"
    exit 1
fi

# Verifică că claude este instalat
if ! command -v claude &> /dev/null; then
    echo "Eroare: Claude Code CLI nu este instalat."
    echo "Instalează cu: npm install -g @anthropic-ai/claude-code"
    exit 1
fi

# Verifică agent-browser (opțional, pentru verificări UI)
if ! command -v agent-browser &> /dev/null; then
    echo "Notă: agent-browser nu este instalat."
    echo "Pentru verificări vizuale UI, instalează cu: npm install -g agent-browser && agent-browser install"
    echo "Continuăm fără verificări browser..."
    echo ""
fi

# Verifică existența fișierelor necesare
if [ ! -f "$PRD_FILE" ]; then
    echo "Eroare: prd.json nu există în $SCRIPT_DIR"
    echo "Generează mai întâi un PRD folosind skill-ul /prd și apoi /ralph"
    exit 1
fi

if [ ! -f "$PROMPT_FILE" ]; then
    echo "Eroare: prompt.md nu există în $SCRIPT_DIR"
    exit 1
fi

# Arhivare rulare anterioară dacă branch-ul s-a schimbat
if [ -f "$PRD_FILE" ] && [ -f "$LAST_BRANCH_FILE" ]; then
  CURRENT_BRANCH=$(jq -r '.branchName // empty' "$PRD_FILE" 2>/dev/null || echo "")
  LAST_BRANCH=$(cat "$LAST_BRANCH_FILE" 2>/dev/null || echo "")

  if [ -n "$CURRENT_BRANCH" ] && [ -n "$LAST_BRANCH" ] && [ "$CURRENT_BRANCH" != "$LAST_BRANCH" ]; then
    DATE=$(date +%Y-%m-%d)
    FOLDER_NAME=$(echo "$LAST_BRANCH" | sed 's|^ralph/||')
    ARCHIVE_FOLDER="$ARCHIVE_DIR/$DATE-$FOLDER_NAME"

    echo "Arhivare rulare anterioară: $LAST_BRANCH"
    mkdir -p "$ARCHIVE_FOLDER"
    [ -f "$PRD_FILE" ] && cp "$PRD_FILE" "$ARCHIVE_FOLDER/"
    [ -f "$PROGRESS_FILE" ] && cp "$PROGRESS_FILE" "$ARCHIVE_FOLDER/"
    echo "   Arhivat în: $ARCHIVE_FOLDER"

    # Reset progress file
    echo "# Ralph Progress Log" > "$PROGRESS_FILE"
    echo "Started: $(date)" >> "$PROGRESS_FILE"
    echo "Branch: $CURRENT_BRANCH" >> "$PROGRESS_FILE"
    echo "---" >> "$PROGRESS_FILE"
  fi
fi

# Salvează branch-ul curent
if [ -f "$PRD_FILE" ]; then
  CURRENT_BRANCH=$(jq -r '.branchName // empty' "$PRD_FILE" 2>/dev/null || echo "")
  if [ -n "$CURRENT_BRANCH" ]; then
    echo "$CURRENT_BRANCH" > "$LAST_BRANCH_FILE"
  fi
fi

# Creează directoare necesare
mkdir -p "$SCRIPT_DIR/logs" "$SCRIPT_DIR/archive" "$SCRIPT_DIR/screenshots"

# Creează .gitignore dacă nu există
if [ ! -f "$PROJECT_DIR/.gitignore" ]; then
  cat > "$PROJECT_DIR/.gitignore" << 'GITIGNORE'
# Python
__pycache__/
*.py[cod]
*.pyo
*.egg-info/
dist/
build/
.coverage
htmlcov/
.pytest_cache/

# Virtual environment
venv/
.venv/

# Ralph runtime
scripts/ralph/.ralph.pid
scripts/ralph/.last-branch
scripts/ralph/logs/
scripts/ralph/screenshots/
scripts/ralph/archive/

# IDE
.vscode/
.idea/

# OS
.DS_Store
Thumbs.db
GITIGNORE
  echo "Created .gitignore"
fi

# Inițializare progress file dacă nu există
if [ ! -f "$PROGRESS_FILE" ]; then
  echo "# Ralph Progress Log" > "$PROGRESS_FILE"
  echo "Started: $(date)" >> "$PROGRESS_FILE"
  echo "---" >> "$PROGRESS_FILE"
fi

# Funcție pentru a verifica dacă toate story-urile sunt complete
check_all_complete() {
  local incomplete=$(jq '[.userStories[] | select(.passes != true)] | length' "$PRD_FILE" 2>/dev/null || echo "999")
  [ "$incomplete" -eq 0 ]
}

# W3: nimic eligibil = toate sunt fie passes, fie failed, fie blocked
check_no_eligible() {
  local n=$(jq '[.userStories[] | select(.passes != true and .failed != true and .blocked != true)] | length' "$PRD_FILE" 2>/dev/null || echo "999")
  [ "$n" -eq 0 ]
}

# W3: alege next eligible story via DAG helper. Print story ID sau "" dacă nimic.
dag_next_story() {
  if [ -n "$DAG_HELPER" ]; then
    "$RALPH_PYTHON" "$DAG_HELPER" next-story "$PRD_FILE" 2>/dev/null || echo ""
  else
    # Fallback simplu (fără DAG): primul story cu passes!=true && failed!=true && blocked!=true, priority asc
    jq -r '[.userStories[] | select(.passes != true and .failed != true and .blocked != true)] | sort_by(.priority) | .[0].id // ""' "$PRD_FILE"
  fi
}

dag_incr_retry() {
  local sid="$1"
  if [ -n "$DAG_HELPER" ]; then
    "$RALPH_PYTHON" "$DAG_HELPER" incr-retry "$PRD_FILE" "$sid" 2>/dev/null || echo "0"
  else
    echo "0"
  fi
}

dag_mark_failed() {
  local sid="$1" reason="$2"
  if [ -n "$DAG_HELPER" ]; then
    "$RALPH_PYTHON" "$DAG_HELPER" mark-failed "$PRD_FILE" "$sid" "$reason" 2>/dev/null || true
  fi
}

dag_force_tags() {
  local sid="$1"
  if [ -n "$DAG_HELPER" ]; then
    "$RALPH_PYTHON" "$DAG_HELPER" force-tags "$PRD_FILE" "$sid" "$PROJECT_DIR" 2>/dev/null || true
  fi
}

# W3: detectează rate limit în output Claude (heuristic — Anthropic nu are exit code dedicat)
is_rate_limited() {
  local output="$1"
  echo "$output" | grep -qiE "rate limit|rate_limit_exceeded|429|too many requests"
}

# Afișare status inițial
echo ""
echo "======================================================================="
echo "       RALPH pentru Claude Code - Agent Autonom                        "
echo "======================================================================="
PROJECT_NAME=$(jq -r '.projectName // "Unknown"' "$PRD_FILE")
BRANCH_NAME=$(jq -r '.branchName // "N/A"' "$PRD_FILE")
TOTAL_STORIES=$(jq '.userStories | length' "$PRD_FILE")
COMPLETE_STORIES=$(jq '[.userStories[] | select(.passes == true)] | length' "$PRD_FILE")
echo "  Proiect: $PROJECT_NAME"
echo "  Branch: $BRANCH_NAME"
echo "  Stories: $COMPLETE_STORIES / $TOTAL_STORIES complete"
echo "  Max iterații: $MAX_ITERATIONS"
echo "  Screenshots: $SCREENSHOTS_DIR"
echo "======================================================================="
echo ""

# Verificare rapidă - poate toate sunt deja complete?
if check_all_complete; then
  echo "Toate story-urile sunt deja complete!"
  exit 0
fi

# Tracker pentru rate limit retry (max 1 retry de iterație-rate-limit per rulare)
RATE_LIMIT_RETRY_USED=0

# Loop principal
for i in $(seq 1 $MAX_ITERATIONS); do
  echo ""
  echo "==================================================================="
  echo "  Ralph Iterația $i din $MAX_ITERATIONS"
  echo "==================================================================="

  # W3: alege next story via DAG (propagă blocked dacă vreun dep a eșuat)
  CURRENT_STORY=$(dag_next_story)
  if [ -z "$CURRENT_STORY" ]; then
    echo ""
    echo "==================================================================="
    if check_all_complete; then
      echo "  TOATE STORY-URILE DIN PRD SUNT COMPLETE!"
      exit 0
    else
      echo "  NICIUN STORY ELIGIBIL (toate fie complete, fie failed, fie blocked)"
      echo "  Stories incomplete:"
      jq -r '.userStories[] | select(.passes != true) | "  - \(.id): \(.title) [failed=\(.failed // false) blocked=\(.blocked // false) retries=\(.retries // 0)]"' "$PRD_FILE"
      exit 0
    fi
  fi

  # Status curent
  COMPLETE_NOW=$(jq '[.userStories[] | select(.passes == true)] | length' "$PRD_FILE")
  NEXT_TITLE=$(jq -r --arg id "$CURRENT_STORY" '.userStories[] | select(.id == $id) | "\(.id): \(.title)"' "$PRD_FILE")
  STORY_TAGS=$(jq -r --arg id "$CURRENT_STORY" '.userStories[] | select(.id == $id) | (.tags // []) | join(",")' "$PRD_FILE")
  STORY_RETRIES=$(jq -r --arg id "$CURRENT_STORY" '.userStories[] | select(.id == $id) | (.retries // 0)' "$PRD_FILE")
  echo "  Progress: $COMPLETE_NOW / $TOTAL_STORIES stories complete"
  echo "  Next: $NEXT_TITLE  [tags: ${STORY_TAGS:-<none>}, retries: $STORY_RETRIES]"
  echo ""

  # Pregătește prompt-ul cu context
  FULL_PROMPT=$(cat <<EOF
# Context pentru această iterație Ralph

## Story țintă (DAG-eligible):
$CURRENT_STORY  (tags: ${STORY_TAGS:-<none>})

## PRD (prd.json):
$(cat "$PRD_FILE")

## Progress până acum (progress.txt):
$(cat "$PROGRESS_FILE")

## Instrucțiuni pentru această iterație:
$(cat "$PROMPT_FILE")
EOF
)

  # Execută Claude Code în modul non-interactiv
  LOG_FILE="$SCRIPT_DIR/logs/iteration-$i-$(date +%Y%m%d-%H%M%S).log"
  mkdir -p "$SCRIPT_DIR/logs"

  # --output-format json + --max-turns pentru control runtime
  set +e
  echo "$FULL_PROMPT" | claude -p \
    --dangerously-skip-permissions \
    --output-format json \
    --max-turns "$MAX_TURNS" \
    2>&1 | tee "$LOG_FILE"
  CLAUDE_EXIT=${PIPESTATUS[1]}
  set -e
  OUTPUT=$(cat "$LOG_FILE")

  # Rate limit budget tracking (best-effort, never blocks Ralph)
  if [ -n "$USAGE_HELPER" ]; then
    "$RALPH_PYTHON" "$USAGE_HELPER" append \
      "$USAGE_FILE" "$LOG_FILE" \
      --slug "$PROJECT_NAME" \
      --story-id "$CURRENT_STORY" \
      --iter "$i" 2>/dev/null || true
  fi

  # W3: rate limit detection (max 1 retry per rulare)
  if is_rate_limited "$OUTPUT" || [ "$CLAUDE_EXIT" = "29" ]; then
    if [ "$RATE_LIMIT_RETRY_USED" = "0" ]; then
      echo ""
      echo "  ⏸️  Rate limit detectat. Sleep ${RATE_LIMIT_SLEEP}s, apoi retry o dată."
      RATE_LIMIT_RETRY_USED=1
      echo "## Rate limit la iter $i — sleep $RATE_LIMIT_SLEEP" >> "$PROGRESS_FILE"
      sleep "$RATE_LIMIT_SLEEP"
      continue  # retry aceeași iterație
    else
      echo "  ❌ Rate limit din nou — abort run, mark $CURRENT_STORY rate_limited"
      dag_mark_failed "$CURRENT_STORY" "rate_limited"
      echo "## Rate limit final la iter $i — abort" >> "$PROGRESS_FILE"
      exit 2
    fi
  fi

  # Verifică dacă toate task-urile sunt complete
  if echo "$OUTPUT" | grep -q "<promise>COMPLETE</promise>"; then
    echo ""
    echo "==================================================================="
    echo "  RALPH A TERMINAT TOATE TASK-URILE!"
    echo "  Completat la iterația $i din $MAX_ITERATIONS"
    echo "==================================================================="
    exit 0
  fi

  # Verifică și prin prd.json
  if check_all_complete; then
    echo ""
    echo "==================================================================="
    echo "  TOATE STORY-URILE DIN PRD SUNT COMPLETE!"
    echo "==================================================================="
    exit 0
  fi

  # W3: tag validation post-iter — chiar dacă Opus a marcat docs, dacă diff atinge .vue/.tsx, force ui
  dag_force_tags "$CURRENT_STORY" >/dev/null 2>&1 || true

  # W3: dacă story-ul curent ÎNCĂ nu trece (passes==false), incrementăm retries
  STILL_INCOMPLETE=$(jq -r --arg id "$CURRENT_STORY" '.userStories[] | select(.id == $id) | (.passes == true)' "$PRD_FILE")
  if [ "$STILL_INCOMPLETE" != "true" ]; then
    NEW_RETRY=$(dag_incr_retry "$CURRENT_STORY")
    echo "  Story $CURRENT_STORY încă incomplet. Retries: $NEW_RETRY/3"
    if [ "$NEW_RETRY" -ge 3 ] 2>/dev/null; then
      echo "  ❌ $CURRENT_STORY failed: max_retries — sare la următorul"
      # mark-failed e deja făcut de incr-retry când >=3, dar idempotent o re-aplicăm
      dag_mark_failed "$CURRENT_STORY" "max_retries"
    fi
  else
    echo "  ✅ Story $CURRENT_STORY marcat passes=true în iterația asta."
  fi

  echo "  Iterația $i completă. Continuăm..."
  sleep 2
done

echo ""
echo "==================================================================="
echo "  Ralph a atins limita de iterații ($MAX_ITERATIONS)"
echo "  Verifică progress.txt pentru status."
echo "==================================================================="
echo ""

# Afișează stories incomplete
echo "Stories incomplete:"
jq -r '.userStories[] | select(.passes != true) | "  - \(.id): \(.title)"' "$PRD_FILE"

exit 1
