#!/bin/bash
# Priority Forge Watchdog
# This script monitors both backend and frontend processes and restarts them if they die.
# Run this as a LaunchAgent or manually for belt-and-suspenders reliability.

# Derive the repo root from this script's own location so the watchdog works
# regardless of where the user cloned Priority Forge. The previous hardcoded
# $HOME/Desktop/priority-forge only matched the original author's layout.
PRIORITY_FORGE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PIDS_DIR="$PRIORITY_FORGE_DIR/.pids"
LOG_FILE="$PIDS_DIR/watchdog.log"

# Ensure pids directory exists
mkdir -p "$PIDS_DIR"

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

check_backend() {
    # Use localhost (not 127.0.0.1) so curl tries both IPv4 and IPv6. Express
    # binds dual-stack by default, but the symmetric form here matches
    # check_frontend and avoids subtle differences if PORT changes.
    if curl -s --connect-timeout 2 "http://localhost:3456/version" > /dev/null 2>&1; then
        return 0  # Backend is up
    fi
    return 1  # Backend is down
}

# Resolve the port the frontend is actually bound to.
#
# Vite has no `strictPort` in frontend/vite.config.ts, so when 5173 is taken
# it auto-increments to 5174+. Curling :5173 blindly either talks to the
# wrong server or false-fails while ours is healthy on a higher port.
#
# Strategy: ask launchctl for the launchd job's PID, walk its descendants
# (npm → sh → node), and grab the listening TCP port from lsof.
frontend_port() {
    local pid
    if [ "$(uname)" = "Linux" ]; then
        pid=$(systemctl --user show -p MainPID --value priority-forge-frontend.service 2>/dev/null)
    else
        pid=$(launchctl list 2>/dev/null | awk '$3 == "com.priority-forge.frontend" { print $1 }')
    fi
    [ -n "$pid" ] && [ "$pid" != "0" ] && [ "$pid" != "-" ] || { 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
        if command -v lsof >/dev/null 2>&1; then
            port=$(lsof -nP -p "$p" -iTCP -sTCP:LISTEN 2>/dev/null | awk 'NR>1 {print $9}' | grep -oE '[0-9]+$' | head -1)
        elif command -v ss >/dev/null 2>&1; then
            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)
        fi
        if [ -n "$port" ]; then
            echo "$port"
            return 0
        fi
    done
    echo "5173"
    return 1
}

check_frontend() {
    local port
    port=$(frontend_port)
    # localhost not 127.0.0.1 — Vite's default host on macOS binds IPv6 only
    # ([::1]:PORT), so an IPv4-forced probe fails while the server is healthy
    # and the watchdog thrashes the frontend on a 30s cycle.
    if curl -s --connect-timeout 2 "http://localhost:$port" > /dev/null 2>&1; then
        return 0  # Frontend is up
    fi
    return 1  # Frontend is down
}

restart_backend() {
    log "Backend down - attempting restart"
    if [ "$(uname)" = "Linux" ]; then
        systemctl --user restart priority-forge-backend 2>/dev/null && log "Restarted via systemctl" || log "systemctl restart failed"
    else
        launchctl kickstart -k "gui/$(id -u)/com.priority-forge.backend" 2>/dev/null && log "Restarted via launchctl" || log "launchctl restart failed"
    fi
}

restart_frontend() {
    log "Frontend down - attempting restart"
    if [ "$(uname)" = "Linux" ]; then
        systemctl --user restart priority-forge-frontend 2>/dev/null && log "Restarted via systemctl" || log "systemctl restart failed"
    else
        launchctl kickstart -k "gui/$(id -u)/com.priority-forge.frontend" 2>/dev/null && log "Restarted via launchctl" || log "launchctl restart failed"
    fi
}

# Main watchdog loop
log "=== Priority Forge Watchdog Started ==="

consecutive_backend_failures=0
consecutive_frontend_failures=0
MAX_CONSECUTIVE_FAILURES=3

while true; do
    # Check backend
    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  # Give it time to start
        fi
    fi
    
    # Check frontend
    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  # Give it time to start
        fi
    fi
    
    # Check every 30 seconds
    sleep 30
done
