#!/bin/bash
# vnc-keep.sh — 長開 keep（VNC 出口 → websockify → ssh -R tunnel）＋ watcher ＋ RAM 監察。
# VNC_MODE=xvnc（Xvnc）或 xvfb_x11vnc（Xvfb＋x11vnc）；斷任何一環 10 秒內重開（主持 8578）。
# 檢查方法：Xvnc/websockify 用 ss -ltn 查 listening socket；tunnel 用 PID file（+ 可選遠端 ss）；
# watcher 用 PID file。全部唔用 process-name 掃描（易 self-match → 假 ALIVE）。
# 用法：bash vnc-keep.sh [--once]   （正常由 start-all.sh 拉起，每 ${KEEP_INTERVAL:-5} 秒一輪；主持 8578 要求 10 秒內恢復）
set -uo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
. "$HERE/env.sh"
mkdir -p "$ARENA_HOME/logs" "$ARENA_HOME/run"
PIDF="$ARENA_HOME/run/keep.pid"; echo $$ > "$PIDF"
log(){ echo "[$(date '+%F %T')][keep] $*"; }

listening(){ ss -ltn 2>/dev/null | awk '{print $4}' | grep -qE "[:.]$1\$"; }

ensure_vnc(){
  if listening "$RFB_PORT"; then return; fi
  log "VNC :$RFB_PORT（mode=${VNC_MODE:-xvnc}）唔喺度 → 重開"
  if [ "${VNC_MODE:-xvnc}" = "xvfb_x11vnc" ]; then
    local xf="$ARENA_HOME/run/xvfb.pid"
    if ! { [ -f "$xf" ] && kill -0 "$(cat "$xf")" 2>/dev/null; }; then
      nohup "${XVFB_BIN:-Xvfb}" "$DISPLAY" -screen 0 "${SCREEN_W}x${SCREEN_H}x24" > "$ARENA_HOME/logs/xvfb.log" 2>&1 &
      echo $! > "$xf"; sleep 2
    fi
    local vf="$ARENA_HOME/run/x11vnc.pid"
    if [ -f "$vf" ] && kill -0 "$(cat "$vf")" 2>/dev/null; then kill "$(cat "$vf")" 2>/dev/null || true; fi; sleep 1
    nohup "${X11VNC_BIN:-x11vnc}" -display "$DISPLAY" -rfbport "$RFB_PORT" -localhost -forever -shared -nopw \
          ${X11VNC_EXTRA:-} > "$ARENA_HOME/logs/x11vnc.log" 2>&1 &
    echo $! > "$vf"; sleep 2
  else
    pkill -f "Xvnc $DISPLAY" 2>/dev/null || true; sleep 1
    nohup Xvnc "$DISPLAY" -geometry "${SCREEN_W}x${SCREEN_H}" -depth 24 -rfbport "$RFB_PORT" \
          -SecurityTypes None -localhost > "$ARENA_HOME/logs/xvnc.log" 2>&1 &
    sleep 2
  fi
}
ensure_ws(){
  if listening "$WS_PORT"; then return; fi
  log "websockify :$WS_PORT 唔喺度 → 重開"
  nohup "$VENV/bin/python" -m websockify "127.0.0.1:$WS_PORT" "127.0.0.1:$RFB_PORT" \
        > "$ARENA_HOME/logs/websockify.log" 2>&1 &
  sleep 1
}
ensure_tunnel(){
  local pidf="$ARENA_HOME/run/tunnel.pid"
  if [ -f "$pidf" ] && kill -0 "$(cat "$pidf")" 2>/dev/null; then
    # 可選：確認遠端真係 listening（wrapper 用 command mode，同 watcher 寫 bus 一樣叫法；失敗唔算錯）
    "$SSH_WRAPPER" "ss -ltn 2>/dev/null | grep -q ':$TUNNEL_PORT'" >/dev/null 2>&1 || true
    return
  fi
  log "tunnel PID 死 → 重開（ExitOnForwardFailure=yes）"
  nohup "$SSH_WRAPPER" -o ExitOnForwardFailure=yes -o ServerAliveInterval=15 -o ServerAliveCountMax=3 \
        -N -R "127.0.0.1:$TUNNEL_PORT:localhost:$WS_PORT" > "$ARENA_HOME/logs/tunnel.log" 2>&1 &
  echo $! > "$pidf"
  sleep 2
}
ensure_watchers(){
  local need=0
  for s in $SEATS; do
    local pidf="$ARENA_HOME/run/watcher-$s.pid"
    if [ -f "$pidf" ] && kill -0 "$(cat "$pidf")" 2>/dev/null; then continue; fi
    need=1
  done
  [ "$need" = 0 ] || bash "$HERE/start-watcher.sh"
}
check_ram(){
  local avail
  avail=$(free -m | awk '/^Mem:/{print $7}')
  [ -n "$avail" ] || return
  if [ "$avail" -lt "$RAM_MIN_MB" ]; then
    local oldest="" opid=0
    for pidf in "$ARENA_HOME"/run/watcher-*.pid; do
      [ -f "$pidf" ] || continue
      local p; p=$(cat "$pidf")
      kill -0 "$p" 2>/dev/null || continue
      if [ "$opid" = 0 ] || [ "$p" -lt "$opid" ]; then opid=$p; oldest=$pidf; fi
    done
    if [ "$opid" != 0 ]; then
      log "🚨 可用 RAM ${avail}MB < ${RAM_MIN_MB}MB → 重啟最舊 browser（PID $opid，$(basename "$oldest")）"
      kill "$opid" 2>/dev/null || true; rm -f "$oldest"; sleep 5; ensure_watchers
    fi
  fi
}
if [ "${1:-}" = "--once" ]; then
  ensure_vnc; ensure_ws; ensure_tunnel; ensure_watchers; check_ram; bash "$HERE/place-windows.sh" || true; exit 0
fi
while :; do
  ensure_vnc; ensure_ws; ensure_tunnel; ensure_watchers; check_ram
  bash "$HERE/place-windows.sh" >/dev/null 2>&1 || true
  sleep "${KEEP_INTERVAL:-5}"
done
