#!/usr/bin/env bash
# ── Consolidated Cycle v1 ──────────────────────────────────────────────
# Single-execution cycle. Cron fires this. No while-true. No sleep.
#
# 3 phases, always in order:
#   1. Watchdog     — bash only, zero tokens, always runs
#   2. Dev cycle    — sonnet, skips if no work or plateau detected
#   3. Philosophy   — opus, skips if ran in last 24h
#
# PLATEAU DETECTION:
#   Tracks cycle outcomes in .loop/cycle-metrics.jsonl
#   If last N cycles produced zero merged PRs and zero new issues filed,
#   the loop is plateauing — it skips the LLM call and logs why.
#   Plateau resets when: new issues appear, PRs merge, or owner comments.
# ───────────────────────────────────────────────────────────────────────

set -uo pipefail
export PATH="$HOME/.local/bin:$HOME/.hermes/bin:/usr/local/bin:$PATH"

REPO="$HOME/Timmy-Time-dashboard"
STATE="$REPO/.loop/state.json"
CLAIMS="$REPO/.loop/claims.json"
PROMPT_FILE="$HOME/.hermes/bin/timmy-loop-prompt.md"
LOG_DIR="$REPO/.loop/logs"
METRICS="$REPO/.loop/cycle-metrics.jsonl"
QUEUE_FILE="$REPO/.loop/queue.json"
TRIAGE_SCRIPT="$REPO/scripts/triage_score.py"
RETRO_SCRIPT="$REPO/scripts/cycle_retro.py"
GITEA_URL="http://143.198.27.163:3000"
GITEA_API="$GITEA_URL/api/v1"
GITEA_TOKEN=$(cat ~/.hermes/gitea_token_vps 2>/dev/null || cat ~/.hermes/gitea_token 2>/dev/null)
REPO_API="$GITEA_API/repos/rockachopa/Timmy-time-dashboard"
MAX_CYCLE_TIME=1200
PHILOSOPHY_MARKER="/tmp/philosophy-last-run"
PLATEAU_THRESHOLD=3  # skip after N consecutive zero-output cycles

DEV_MODEL="claude-sonnet-4-20250514"
PHILOSOPHY_MODEL="claude-opus-4-6"

# macOS timeout fallback
if ! command -v timeout &>/dev/null; then
    timeout() { local d="$1"; shift; perl -e "alarm $d; exec @ARGV" -- "$@"; }
fi

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

# ── Plateau Detection ─────────────────────────────────────────────────
check_plateau() {
    # Returns 0 (true/plateau) or 1 (false/work to do)
    [ ! -f "$METRICS" ] && return 1  # no history = no plateau
    
    local recent
    recent=$(tail -n "$PLATEAU_THRESHOLD" "$METRICS" 2>/dev/null)
    local zero_count=0
    local total=0
    
    while IFS= read -r line; do
        total=$((total + 1))
        local prs_merged issues_filed
        prs_merged=$(echo "$line" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('prs_merged',0))" 2>/dev/null || echo 0)
        issues_filed=$(echo "$line" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('issues_filed',0))" 2>/dev/null || echo 0)
        if [ "$prs_merged" -eq 0 ] && [ "$issues_filed" -eq 0 ]; then
            zero_count=$((zero_count + 1))
        fi
    done <<< "$recent"
    
    if [ "$total" -ge "$PLATEAU_THRESHOLD" ] && [ "$zero_count" -ge "$PLATEAU_THRESHOLD" ]; then
        return 0  # plateau detected
    fi
    return 1  # not plateauing
}

log_metric() {
    # $1=prs_merged $2=issues_filed $3=outcome
    local ts
    ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
    echo "{\"ts\":\"$ts\",\"prs_merged\":${1:-0},\"issues_filed\":${2:-0},\"outcome\":\"${3:-unknown}\"}" >> "$METRICS"
}

# ── Phase 1: Watchdog (bash only, zero tokens) ───────────────────────
phase_watchdog() {
    log "── WATCHDOG ──"
    
    # Kill orphaned pytest processes (> 20 min)
    ps aux | grep "pytest tests/" | grep -v grep | while read -r line; do
        local pid etime
        pid=$(echo "$line" | awk '{print $2}')
        etime=$(ps -o etime= -p "$pid" 2>/dev/null | tr -d ' ')
        if [[ "$etime" == *:*:* ]]; then
            log "  Killing stale pytest PID $pid ($etime)"
            kill "$pid" 2>/dev/null
        fi
    done
    
    # Kill stuck git pushes (> 10 min)
    ps aux | grep "git.*push\|git-remote-http" | grep -v grep | while read -r line; do
        local pid etime
        pid=$(echo "$line" | awk '{print $2}')
        etime=$(ps -o etime= -p "$pid" 2>/dev/null | tr -d ' ')
        if [[ "$etime" == *:*:* ]]; then
            log "  Killing stuck git PID $pid ($etime)"
            kill "$pid" 2>/dev/null
        fi
    done
    
    # Kill orphaned vi/vim editors
    ps aux | grep "vi.*COMMIT_EDITMSG" | grep -v grep | awk '{print $2}' | xargs kill 2>/dev/null
    
    # Expire stale claims
    if [ -f "$CLAIMS" ]; then
        python3 -c "
import json, time
try:
    claims = json.load(open('$CLAIMS'))
    now = time.time()
    expired = [k for k,v in claims.items() if isinstance(v, dict) and now - v.get('ts', now) > 3600]
    for k in expired:
        del claims[k]
        print(f'  Expired claim: {k}')
    if expired:
        json.dump(claims, open('$CLAIMS', 'w'), indent=2)
except: pass
" 2>/dev/null
    fi
    
    # Gitea health
    if curl -s --max-time 5 "$GITEA_URL/api/v1/version" >/dev/null 2>&1; then
        log "  Gitea: OK"
    else
        log "  WARNING: Gitea unreachable"
    fi
}

# ── Phase 2: Dev Cycle (sonnet) ───────────────────────────────────────
phase_dev() {
    log "── DEV CYCLE (model: $DEV_MODEL) ──"
    
    # Plateau check
    if check_plateau; then
        log "  PLATEAU: Last $PLATEAU_THRESHOLD cycles produced no output. Skipping LLM call."
        log "  (Will resume when new issues appear or PRs need review)"
        
        # But still check if there's new external activity that breaks plateau
        local open_prs
        open_prs=$(curl -s -H "Authorization: token $GITEA_TOKEN" \
            "$REPO_API/pulls?state=open&limit=5" 2>/dev/null | \
            python3 -c "import sys,json; print(len(json.loads(sys.stdin.read())))" 2>/dev/null || echo 0)
        
        if [ "$open_prs" -gt 0 ]; then
            log "  But $open_prs open PRs found — breaking plateau for review."
        else
            log_metric 0 0 "plateau_skip"
            return
        fi
    fi
    
    # Fast triage
    if [ -f "$TRIAGE_SCRIPT" ]; then
        GITEA_API="$GITEA_API" GITEA_TOKEN="$GITEA_TOKEN" \
            python3 "$TRIAGE_SCRIPT" > "$QUEUE_FILE" 2>/dev/null || true
    fi
    
    local queue_size=0
    if [ -f "$QUEUE_FILE" ]; then
        queue_size=$(python3 -c "import json; print(len(json.load(open('$QUEUE_FILE'))))" 2>/dev/null || echo 0)
    fi
    
    if [ "$queue_size" -eq 0 ]; then
        log "  No work in queue. Skipping."
        log_metric 0 0 "empty_queue"
        return
    fi
    
    log "  Queue: $queue_size items"
    
    local PROMPT
    PROMPT=$(cat "$PROMPT_FILE" 2>/dev/null)
    [ -z "$PROMPT" ] && { log "ERROR: No prompt file"; return; }
    
    local QUEUE_SUMMARY
    QUEUE_SUMMARY=$(python3 -c "
import json
q = json.load(open('$QUEUE_FILE'))
lines = ['PRIORITIZED QUEUE ({} ready issues):'.format(len(q))]
for i, item in enumerate(q[:8]):
    score = item.get('score', 0)
    title = item.get('title', '?')[:70]
    num = item.get('number', '?')
    labels = ','.join(item.get('labels', []))
    files = ', '.join(item.get('files', [])[:3])
    lines.append(f'  {i+1}. #{num} [{labels}] score={score} — {title}')
    if files: lines.append(f'     files: {files}')
if len(q) > 8: lines.append(f'  ... +{len(q)-8} more')
print('\n'.join(lines))
" 2>/dev/null || echo "Queue: error")

    local FULL_PROMPT="TIME BUDGET: 20 minutes. Be efficient — sonnet, not opus.

$QUEUE_SUMMARY

Pick from the TOP of this queue.

$PROMPT"
    
    local CYCLE_LOG="$LOG_DIR/cycle-$(date +%Y%m%d_%H%M%S).log"
    mkdir -p "$LOG_DIR"
    
    if timeout "$MAX_CYCLE_TIME" hermes chat --yolo \
        --provider anthropic \
        --model "$DEV_MODEL" \
        -q "$FULL_PROMPT" 2>&1 | tee "$CYCLE_LOG"; then
        log "  Cycle completed OK"
        # TODO: parse cycle log for prs_merged / issues_filed counts
        log_metric 0 0 "completed"
    else
        log "  Cycle failed (exit $?)"
        log_metric 0 0 "failed"
    fi
}

# ── Phase 3: Philosophy (opus, daily) ─────────────────────────────────
phase_philosophy() {
    if [ -f "$PHILOSOPHY_MARKER" ]; then
        local last_run now elapsed
        last_run=$(cat "$PHILOSOPHY_MARKER" 2>/dev/null || echo 0)
        now=$(date +%s)
        elapsed=$((now - last_run))
        if [ "$elapsed" -lt 86400 ]; then
            return  # ran today already
        fi
    fi
    
    log "── PHILOSOPHY (daily, model: $PHILOSOPHY_MODEL) ──"
    
    timeout 600 hermes chat --yolo \
        --provider anthropic \
        --model "$PHILOSOPHY_MODEL" \
        -q "You are Hermes Agent on a philosophy loop. Study the next influence from ~/philosophy-journal.md. Search web for a real primary source. Write 300-500 word reflection on agentic architecture. File Gitea issue at $REPO_API/issues (token from ~/.hermes/gitea_token_vps). Append to ~/philosophy-journal.md. Tag: [philosophy]." \
        2>&1 | tee "$LOG_DIR/philosophy-$(date +%Y%m%d).log" || true
    
    date +%s > "$PHILOSOPHY_MARKER"
    log "  Philosophy complete. Next: ~24h."
}

# ── Main (single execution) ───────────────────────────────────────────
log "=== CONSOLIDATED CYCLE START ==="
phase_watchdog
phase_dev
phase_philosophy
log "=== CONSOLIDATED CYCLE END ==="
