#!/bin/bash
# Priority Forge Watchdog (Linux/systemd)
# Monitors backend and frontend health; restarts via systemctl --user if unresponsive.

LOG_DIR="$HOME/.local/share/priority-forge"
LOG_FILE="$LOG_DIR/watchdog.log"
MAX_CONSECUTIVE_FAILURES=3

mkdir -p "$LOG_DIR"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

check_backend() {
    curl -s --connect-timeout 2 "http://127.0.0.1:3456/health" > /dev/null 2>&1
}

# Resolve the port the frontend service is actually bound to.
#
# Vite has no `strictPort` in frontend/vite.config.ts, so when 5173 is taken
# (e.g. another dev server, a stale instance) it auto-increments to 5174+.
# A naïve curl to :5173 would either succeed against the wrong server or fail
# while our service is fine, both of which mislead the watchdog.
#
# Strategy: read the systemd service's MainPID, walk its descendant tree
# (npm → sh → node), and grab the listening TCP port from `ss`.
frontend_port() {
    local pid
    pid=$(systemctl --user show -p MainPID --value priority-forge-frontend.service 2>/dev/null)
    [ -n "$pid" ] && [ "$pid" != "0" ] || { echo "5173"; return 1; }

    local pids="$pid"
    for child in $(pgrep -P "$pid" 2>/dev/null); do
        pids="$pids $child"
        for grand in $(pgrep -P "$child" 2>/dev/null); do
            pids="$pids $grand"
        done
    done
    for p in $pids; do
        local port
        port=$(ss -tlnp 2>/dev/null | grep "pid=$p," | grep -oE '127\.0\.0\.1:[0-9]+|0\.0\.0\.0:[0-9]+|\[::\]:[0-9]+|\[::1\]:[0-9]+' | grep -oE '[0-9]+$' | head -1)
        if [ -n "$port" ]; then
            echo "$port"
            return 0
        fi
    done
    echo "5173"
    return 1
}

check_frontend() {
    local port
    port=$(frontend_port)
    curl -s --connect-timeout 2 "http://127.0.0.1:$port" > /dev/null 2>&1
}

restart_backend() {
    log "Backend unresponsive — restarting via systemctl"
    systemctl --user restart priority-forge-backend.service
}

restart_frontend() {
    log "Frontend unresponsive — restarting via systemctl"
    systemctl --user restart priority-forge-frontend.service
}

# Reap mcp-proxy.js processes that have been re-parented to init (ppid=1),
# meaning their originating Claude Code session died without cleaning them up.
# Safe because a live proxy always has its Claude Code parent; ppid=1 is a
# smoking gun for orphans. Only kills proxies older than 60 minutes to avoid
# racing against legitimately starting ones.
reap_orphan_proxies() {
    local reaped=0
    for pid in $(pgrep -f 'mcp-proxy.js' 2>/dev/null); do
        local ppid
        ppid=$(awk '{print $4}' /proc/$pid/stat 2>/dev/null)
        [ "$ppid" = "1" ] || continue
        local etime
        etime=$(ps -o etimes= -p $pid 2>/dev/null | tr -d ' ')
        [ "${etime:-0}" -gt 3600 ] || continue
        log "Reaping orphan mcp-proxy pid=$pid etime=${etime}s (ppid=1)"
        kill -TERM $pid 2>/dev/null
        reaped=$((reaped + 1))
    done
    if [ $reaped -gt 0 ]; then
        # Give them 10s to handle SIGTERM gracefully, then SIGKILL stragglers.
        sleep 10
        for pid in $(pgrep -f 'mcp-proxy.js' 2>/dev/null); do
            local ppid
            ppid=$(awk '{print $4}' /proc/$pid/stat 2>/dev/null)
            [ "$ppid" = "1" ] || continue
            if kill -0 $pid 2>/dev/null; then
                log "Force-killing unresponsive orphan mcp-proxy pid=$pid"
                kill -KILL $pid 2>/dev/null
            fi
        done
    fi
}

log "=== Priority Forge Watchdog Started ==="

consecutive_backend_failures=0
consecutive_frontend_failures=0
iteration=0

while true; do
    if check_backend; then
        consecutive_backend_failures=0
    else
        consecutive_backend_failures=$((consecutive_backend_failures + 1))
        log "Backend check failed ($consecutive_backend_failures/$MAX_CONSECUTIVE_FAILURES)"
        if [ $consecutive_backend_failures -ge $MAX_CONSECUTIVE_FAILURES ]; then
            restart_backend
            consecutive_backend_failures=0
            sleep 10
        fi
    fi

    if check_frontend; then
        consecutive_frontend_failures=0
    else
        consecutive_frontend_failures=$((consecutive_frontend_failures + 1))
        log "Frontend check failed on port $(frontend_port) ($consecutive_frontend_failures/$MAX_CONSECUTIVE_FAILURES)"
        if [ $consecutive_frontend_failures -ge $MAX_CONSECUTIVE_FAILURES ]; then
            restart_frontend
            consecutive_frontend_failures=0
            sleep 10
        fi
    fi

    # Reap orphan mcp-proxy.js processes every 10th iteration (~5 min at 30s cadence).
    iteration=$((iteration + 1))
    if [ $((iteration % 10)) -eq 0 ]; then
        reap_orphan_proxies
    fi

    sleep 30
done
