#!/usr/bin/env bash
# timmy-orchestrator.sh — Timmy's orchestration loop
# Uses Hermes CLI plus workforce-manager to triage and review.
# Timmy is the brain. Other agents are the hands.

set -uo pipefail\n\nSCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

LOG_DIR="$HOME/.hermes/logs"
LOG="$LOG_DIR/timmy-orchestrator.log"
PIDFILE="$LOG_DIR/timmy-orchestrator.pid"
GITEA_URL="${GITEA_URL:-https://forge.alexanderwhitestone.com}"
GITEA_TOKEN=$(cat "$HOME/.hermes/gitea_token_vps" 2>/dev/null)  # Timmy token, NOT rockachopa
CYCLE_INTERVAL=300
HERMES_TIMEOUT=180
AUTO_ASSIGN_UNASSIGNED="${AUTO_ASSIGN_UNASSIGNED:-0}"   # 0 = report only, 1 = mutate Gitea assignments

mkdir -p "$LOG_DIR"

# Single instance guard
if [ -f "$PIDFILE" ]; then
  old_pid=$(cat "$PIDFILE")
  if kill -0 "$old_pid" 2>/dev/null; then
    echo "Timmy already running (PID $old_pid)" >&2
    exit 0
  fi
fi
echo $$ > "$PIDFILE"
trap 'rm -f "$PIDFILE"' EXIT

log() {
  echo "[$(date '+%Y-%m-%d %H:%M:%S')] TIMMY: $*" >> "$LOG"
}

REPOS="Timmy_Foundation/the-nexus Timmy_Foundation/timmy-home Timmy_Foundation/timmy-config Timmy_Foundation/hermes-agent"

gather_state() {
  local state_dir="/tmp/timmy-state-$$"
  mkdir -p "$state_dir"

  > "$state_dir/unassigned.txt"
  > "$state_dir/open_prs.txt"
  > "$state_dir/agent_status.txt"
  > "$state_dir/uncommitted_work.txt"

  for repo in $REPOS; do
    local short=$(echo "$repo" | cut -d/ -f2)

    # Unassigned issues
    curl -sf -H "Authorization: token $GITEA_TOKEN" \
      "$GITEA_URL/api/v1/repos/$repo/issues?state=open&type=issues&limit=50" 2>/dev/null | \
    python3 -c "
import sys,json
for i in json.load(sys.stdin):
    if not i.get('assignees'):
        print(f'REPO={\"$repo\"} NUM={i[\"number\"]} TITLE={i[\"title\"]}')" >> "$state_dir/unassigned.txt" 2>/dev/null

    # Open PRs
    curl -sf -H "Authorization: token $GITEA_TOKEN" \
      "$GITEA_URL/api/v1/repos/$repo/pulls?state=open&limit=30" 2>/dev/null | \
    python3 -c "
import sys,json
for p in json.load(sys.stdin):
    print(f'REPO={\"$repo\"} PR={p[\"number\"]} BY={p[\"user\"][\"login\"]} TITLE={p[\"title\"]}')" >> "$state_dir/open_prs.txt" 2>/dev/null
  done

  echo "Claude workers: $(pgrep -f 'claude.*--print.*--dangerously' 2>/dev/null | wc -l | tr -d ' ')" >> "$state_dir/agent_status.txt"
  echo "Claude loop: $(pgrep -f 'claude-loop.sh' 2>/dev/null | wc -l | tr -d ' ') procs" >> "$state_dir/agent_status.txt"
  tail -50 "$LOG_DIR/claude-loop.log" 2>/dev/null | grep -c "SUCCESS" | xargs -I{} echo "Claude recent successes: {}" >> "$state_dir/agent_status.txt"
  tail -50 "$LOG_DIR/claude-loop.log" 2>/dev/null | grep -c "FAILED" | xargs -I{} echo "Claude recent failures: {}" >> "$state_dir/agent_status.txt"
  echo "Kimi heartbeat launchd: $(launchctl list 2>/dev/null | grep -c 'ai.timmy.kimi-heartbeat' | tr -d ' ') job" >> "$state_dir/agent_status.txt"
  tail -50 "/tmp/kimi-heartbeat.log" 2>/dev/null | grep -c "DISPATCHED:" | xargs -I{} echo "Kimi recent dispatches: {}" >> "$state_dir/agent_status.txt"
  tail -50 "/tmp/kimi-heartbeat.log" 2>/dev/null | grep -c "FAILED:" | xargs -I{} echo "Kimi recent failures: {}" >> "$state_dir/agent_status.txt"
  tail -1 "/tmp/kimi-heartbeat.log" 2>/dev/null | xargs -I{} echo "Kimi last event: {}" >> "$state_dir/agent_status.txt"

  # Scan worktrees for uncommitted work
  for wt_dir in "$HOME/worktrees"/*/; do
    [ -d "$wt_dir" ] || continue
    [ -d "$wt_dir/.git" ] || continue
    local dirty
    dirty=$(cd "$wt_dir" && git status --porcelain 2>/dev/null | wc -l | tr -d " ")
    if [ "${dirty:-0}" -gt 0 ]; then
      local branch
      branch=$(cd "$wt_dir" && git branch --show-current 2>/dev/null || echo "?")
      local age=""
      local last_commit
      last_commit=$(cd "$wt_dir" && git log -1 --format=%ct 2>/dev/null || echo 0)
      local now=$(date +%s)
      local stale_mins=$(( (now - last_commit) / 60 ))
      echo "DIR=$wt_dir BRANCH=$branch DIRTY=$dirty STALE=${stale_mins}m" >> "$state_dir/uncommitted_work.txt"
    fi
  done

  echo "$state_dir"
}

run_triage() {
  local state_dir="$1"
  local unassigned_count=$(wc -l < "$state_dir/unassigned.txt" | tr -d ' ')
  local pr_count=$(wc -l < "$state_dir/open_prs.txt" | tr -d ' ')

  log "Cycle: $unassigned_count unassigned, $pr_count open PRs"

  # Check for uncommitted work — nag if stale
  local uncommitted_count
  uncommitted_count=$(wc -l < "$state_dir/uncommitted_work.txt" 2>/dev/null | tr -d " " || echo 0)
  if [ "${uncommitted_count:-0}" -gt 0 ]; then
    log "WARNING: $uncommitted_count worktree(s) with uncommitted work"
    while IFS= read -r line; do
      log "  UNCOMMITTED: $line"
      # Auto-commit stale work (>60 min without commit)
      local stale=$(echo "$line" | sed 's/.*STALE=\([0-9]*\)m.*/\1/')
      local wt_dir=$(echo "$line" | sed 's/.*DIR=\([^ ]*\) .*/\1/')
      if [ "${stale:-0}" -gt 60 ]; then
        log "  AUTO-COMMITTING stale work in $wt_dir (${stale}m stale)"
        (cd "$wt_dir" && git add -A && git commit -m "WIP: orchestrator auto-commit — ${stale}m stale work

Preserved by timmy-orchestrator to prevent loss." 2>/dev/null &&           git push 2>/dev/null) && log "  COMMITTED: $wt_dir" || log "  COMMIT FAILED: $wt_dir"
      fi
    done < "$state_dir/uncommitted_work.txt"
  fi

  # If nothing to do, skip the LLM call
  if [ "$unassigned_count" -eq 0 ] && [ "$pr_count" -eq 0 ]; then
    log "Nothing to triage"
    return
  fi

  # Phase 1: Report unassigned issues by default.
  # Auto-assignment is opt-in because silent queue mutation resurrects old state.
  if [ "$unassigned_count" -gt 0 ]; then
    if [ "$AUTO_ASSIGN_UNASSIGNED" = "1" ]; then
      log "Assigning $unassigned_count issues to claude..."
      while IFS= read -r line; do
        local repo=$(echo "$line" | sed 's/.*REPO=\([^ ]*\).*/\1/')
        local num=$(echo "$line" | sed 's/.*NUM=\([^ ]*\).*/\1/')
        curl -sf -X PATCH "$GITEA_URL/api/v1/repos/$repo/issues/$num" \
          -H "Authorization: token $GITEA_TOKEN" \
          -H "Content-Type: application/json" \
          -d '{"assignees":["claude"]}' >/dev/null 2>&1 && \
          log "  Assigned #$num ($repo) to claude"
      done < "$state_dir/unassigned.txt"
    else
      log "Auto-assign disabled: leaving $unassigned_count unassigned issues untouched"
    fi
  fi

  # Phase 2: PR review via Timmy (LLM)
  if [ "$pr_count" -gt 0 ]; then
    run_pr_review "$state_dir"
  fi
}

run_pr_review() {
  local state_dir="$1"
  local prompt_file="/tmp/timmy-prompt-$$.txt"

  # Build a review prompt listing all open PRs
  cat > "$prompt_file" <<'HEADER'
You are Timmy, the orchestrator. Review these open PRs from AI agents.

For each PR, you will see the diff. Your job:
- MERGE if changes look reasonable (most agent PRs are good, merge aggressively)
- COMMENT if there is a clear problem
- CLOSE if it is a duplicate or garbage

Use these exact curl patterns (replace REPO, NUM):
  Merge:   curl -sf -X POST "GITEA/api/v1/repos/REPO/pulls/NUM/merge" -H "Authorization: token TOKEN" -H "Content-Type: application/json" -d '{"Do":"squash"}'
  Comment: curl -sf -X POST "GITEA/api/v1/repos/REPO/pulls/NUM/comments" -H "Authorization: token TOKEN" -H "Content-Type: application/json" -d '{"body":"feedback"}'
  Close:   curl -sf -X PATCH "GITEA/api/v1/repos/REPO/pulls/NUM" -H "Authorization: token TOKEN" -H "Content-Type: application/json" -d '{"state":"closed"}'

HEADER

  # Replace placeholders
  sed -i '' "s|GITEA|$GITEA_URL|g; s|TOKEN|$GITEA_TOKEN|g" "$prompt_file"

  # Add each PR with its diff (up to 10 PRs per cycle)
  local count=0
  while IFS= read -r line && [ "$count" -lt 10 ]; do
    local repo=$(echo "$line" | sed 's/.*REPO=\([^ ]*\).*/\1/')
    local pr_num=$(echo "$line" | sed 's/.*PR=\([^ ]*\).*/\1/')
    local by=$(echo "$line" | sed 's/.*BY=\([^ ]*\).*/\1/')
    local title=$(echo "$line" | sed 's/.*TITLE=//')

    [ -z "$pr_num" ] && continue

    local diff
    diff=$(curl -sf -H "Authorization: token $GITEA_TOKEN" \
      -H "Accept: application/diff" \
      "$GITEA_URL/api/v1/repos/$repo/pulls/$pr_num" 2>/dev/null | head -150)

    [ -z "$diff" ] && continue

    echo "" >> "$prompt_file"
    echo "=== PR #$pr_num in $repo by $by ===" >> "$prompt_file"
    echo "Title: $title" >> "$prompt_file"
    echo "Diff (first 150 lines):" >> "$prompt_file"
    echo "$diff" >> "$prompt_file"
    echo "=== END PR #$pr_num ===" >> "$prompt_file"

    count=$((count + 1))
  done < "$state_dir/open_prs.txt"

  if [ "$count" -eq 0 ]; then
    rm -f "$prompt_file"
    return
  fi

  echo "" >> "$prompt_file"
  cat >> "$prompt_file" <<'FOOTER'
INSTRUCTIONS: For EACH PR above, do ONE of the following RIGHT NOW using your terminal tool:
- Run the merge curl command if the diff looks good
- Run the close curl command if it is a duplicate or garbage
- Run the comment curl command only if there is a clear bug

IMPORTANT: Actually run the curl commands. Do not just describe what you would do. Finish means the PR world-state changed.
FOOTER

  local prompt_text
  prompt_text=$(cat "$prompt_file")
  rm -f "$prompt_file"

  log "Reviewing $count PRs..."
  local result
  result=$(timeout "$HERMES_TIMEOUT" hermes chat -q "$prompt_text" -Q --yolo 2>&1)
  local exit_code=$?

  if [ "$exit_code" -eq 0 ]; then
    log "PR review complete"
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $result" >> "$LOG_DIR/timmy-reviews.log"
  else
    log "PR review failed (exit $exit_code)"
  fi
}

# === MAIN LOOP ===
log "=== Timmy Orchestrator Started (PID $$) ==="
log "Cycle: ${CYCLE_INTERVAL}s | Auto-assign: ${AUTO_ASSIGN_UNASSIGNED} | Inference surface: Hermes CLI"

# Start auto-commit-guard daemon for work preservation
if ! pgrep -f "auto-commit-guard.sh" >/dev/null 2>&1; then
  nohup bash "$SCRIPT_DIR/auto-commit-guard.sh" 120 >> "$LOG_DIR/auto-commit-guard.log" 2>&1 &
  log "Started auto-commit-guard daemon (PID $!)"
fi

WORKFORCE_CYCLE=0

while true; do
  state_dir=$(gather_state)
  run_triage "$state_dir"
  rm -rf "$state_dir"

  # Run workforce manager every 3rd cycle (~15 min)
  WORKFORCE_CYCLE=$((WORKFORCE_CYCLE + 1))
  if [ $((WORKFORCE_CYCLE % 3)) -eq 0 ]; then
    log "Running workforce manager..."
    python3 "$HOME/.hermes/bin/workforce-manager.py" all >> "$LOG_DIR/workforce-manager.log" 2>&1
    log "Workforce manager complete"
  fi

  log "Sleeping ${CYCLE_INTERVAL}s"
  sleep "$CYCLE_INTERVAL"
done
