#!/bin/bash
# CLX launcher — auto-rebuild + watchdog + library path setup.
DIR="$(dirname "$(readlink -f "$0" 2>/dev/null || realpath "$0" 2>/dev/null || echo "$0")")/.."
export DYLD_LIBRARY_PATH="${DIR}/rs/target/release:${DYLD_LIBRARY_PATH}"
export PATH="${DIR}:${PATH}"
# ort (ten-vad) dlopens ONNX Runtime from ORT_DYLIB_PATH (load-dynamic). Point at
# the dev copy that build.sh drops in target/release; the .app sets this itself.
export ORT_DYLIB_PATH="${ORT_DYLIB_PATH:-${DIR}/rs/target/release/libonnxruntime.1.23.2.dylib}"

# ── Auto-rebuild if source changed (like otoji) ──────────────────────────
# Compare binary mtime against newest source file. If source is newer,
# rebuild before launching. Skips if not in a git repo or build.sh missing.
if [ -f "${DIR}/build.sh" ] && [ -d "${DIR}/rs/core/src" ] && [ -z "$CLX_REBUILDING" ]; then
    BIN="${DIR}/clx"
    if [ -f "$BIN" ]; then
        # Find newest .rs file in src dirs (excluding target/)
        NEWEST_SRC=$(find "${DIR}/rs/core/src" "${DIR}/rs/adapters" -name '*.rs' -newer "$BIN" 2>/dev/null | head -1)
        if [ -n "$NEWEST_SRC" ]; then
            echo "clx: source changed, rebuilding…" >&2
            if CLX_REBUILDING=1 "${DIR}/build.sh" >/dev/null 2>&1; then
                echo "clx: rebuild ok" >&2
            else
                echo "clx: rebuild failed, using existing binary" >&2
            fi
        fi
    fi
fi

# If args are passed (agent, dino, --help, -f, etc.), run directly — no watchdog.
if [ $# -gt 0 ] && [ "$1" != "--watchdog" ]; then
    exec "${DIR}/clx" "$@"
fi

# Fork watchdog to background unless already running as watchdog.
if [ "$1" != "--watchdog" ]; then
    mkdir -p "${DIR}/tmp"
    # Rotate watchdog log if it exceeds 5 MB so it can't grow unbounded
    # (previously hit 56 MB / 583k lines from CGEventTap-disabled spam).
    LOG="${DIR}/tmp/clx-watchdog.log"
    if [ -f "$LOG" ] && [ "$(stat -f%z "$LOG" 2>/dev/null || stat -c%s "$LOG" 2>/dev/null || echo 0)" -gt 5242880 ]; then
        mv -f "$LOG" "${LOG}.1"
    fi
    nohup "$0" --watchdog </dev/null >>"$LOG" 2>&1 &
    echo "[CLX] started (pid $!)" >&2
    exit 0
fi

# Daemon mode: watchdog with exponential backoff.
# Starts at 10ms, doubles each crash, resets on clean run (>10s uptime).
MAX_RESTARTS=20
backoff_ms=10
restarts=0

while [ $restarts -lt $MAX_RESTARTS ]; do
    start_time=$(date +%s)
    "${DIR}/clx" -f
    exit_code=$?
    end_time=$(date +%s)
    uptime=$((end_time - start_time))

    if [ $exit_code -eq 0 ] || [ $exit_code -eq 130 ] || [ $exit_code -eq 137 ]; then
        # Clean exit (0), Ctrl+C (130), or killed by new instance dedup (137) — don't restart.
        break
    fi

    # If it ran for >10s, it was a normal session — reset backoff.
    if [ $uptime -gt 10 ]; then
        backoff_ms=10
        restarts=0
    fi

    restarts=$((restarts + 1))
    backoff_sec=$(echo "scale=3; $backoff_ms / 1000" | bc)
    echo "[CLX] crashed (exit $exit_code, uptime ${uptime}s), restart #$restarts in ${backoff_ms}ms..." >&2

    # macOS notification so the user knows it crashed.
    osascript -e "display notification \"Crashed (exit $exit_code). Restarting #$restarts...\" with title \"CapsLockX\"" 2>/dev/null &

    sleep "$backoff_sec"
    backoff_ms=$((backoff_ms * 2))
done

if [ $restarts -ge $MAX_RESTARTS ]; then
    echo "[CLX] too many crashes ($MAX_RESTARTS), giving up." >&2
    osascript -e "display notification \"Too many crashes ($MAX_RESTARTS). Stopped.\" with title \"CapsLockX\" sound name \"Basso\"" 2>/dev/null
fi
