#!/usr/bin/env bash
# Supervisor del ESCÁNER PIXEL V4 (:8773) — P0 (2026-08-06).
# Reemplaza el nohup suelto de run_pixel.sh, que NO revivía: un SIGSEGV de frida-core dejaba el escáner
# MUERTO hasta reinicio manual (horas de "bloqueo"). Aquí: prep de dispositivos UNA vez + backend con
# RESPAWN (throttle anti-flap) + log con ROTACIÓN (>> en vez de >; no se trunca en cada respawn -> forense).
# NO reinicia el juego si ya está vivo (respeta la sesión). Pensado para lanzarse desde launchd (boot) o a mano.
set -u
HERE="$(cd "$(dirname "$0")" && pwd)"
PY="/Applications/Xcode.app/Contents/Developer/usr/bin/python3"
FS_BIN_NAME="frida-server-17.9.10-android-arm64"
EVONY_ACT="com.topgamesinc.evony/com.topgamesinc.androidplugin.UnityActivity"
PORT=8773
LOG=/tmp/evony_pixel.log
export PATH="/opt/homebrew/bin:/usr/bin:/bin:$PATH"
SCANNER_W="${SCANNER_W:-33201JEHN00951}"; W_WIFI="${W_WIFI:-192.168.0.30:5555}"
SCANNER_E="${SCANNER_E:-32241JEHN24574}"; E_WIFI="${E_WIFI:-192.168.0.31:5555}"
log(){ echo "[supervise $(date '+%F %T')] $*" | tee -a "$LOG"; }

resolve(){   # $1=serial USB preferido  $2=respaldo wifi
  if [ "$(adb -s "$1" get-state 2>/dev/null | tr -d '\r')" = "device" ]; then echo "$1"; return 0; fi
  adb connect "$2" >/dev/null 2>&1; sleep 2
  [ "$(adb -s "$2" get-state 2>/dev/null | tr -d '\r')" = "device" ] && { echo "$2"; return 0; }
  return 1
}

DW="$(resolve "$SCANNER_W" "$W_WIFI")" || { log "ABORT: móvil W no responde (USB ni WiFi)"; exit 1; }
DE="$(resolve "$SCANNER_E" "$E_WIFI")" || { log "ABORT: móvil E no responde (USB ni WiFi)"; exit 1; }
[ -f "$HERE/agent_W.js" ] && [ -f "$HERE/agent_E.js" ] || bash "$HERE/build_agents_v4.sh"

# --- prep de dispositivos (una vez): frida-server + anti-Doze/anti-sleep + wake; Evony SOLO si no corre ---
for serial in "$DW" "$DE"; do
  adb -s "$serial" shell "su -c 'ls /data/local/tmp/frida-server'" >/dev/null 2>&1 || \
    adb -s "$serial" push "$HERE/$FS_BIN_NAME" /data/local/tmp/frida-server >/dev/null 2>&1 || true
  adb -s "$serial" shell "su -c 'chmod 755 /data/local/tmp/frida-server'" >/dev/null 2>&1 || true
  adb -s "$serial" shell "su -c 'netstat -anl 2>/dev/null | grep \":27042 \" | grep LISTEN'" >/dev/null 2>&1 || \
    adb -s "$serial" shell "su -c 'setsid /data/local/tmp/frida-server </dev/null >/dev/null 2>&1 &'" >/dev/null 2>&1 || true
  adb -s "$serial" shell settings put global stay_on_while_plugged_in 3 >/dev/null 2>&1 || true
  adb -s "$serial" shell settings put system screen_off_timeout 1800000 >/dev/null 2>&1 || true
  adb -s "$serial" shell "su -c 'dumpsys deviceidle disable'" >/dev/null 2>&1 || true   # anti-Doze (Android no mata frida/juego en reposo)
  adb -s "$serial" shell "su -c 'settings put global window_animation_scale 0'" >/dev/null 2>&1 || true
  adb -s "$serial" shell input keyevent KEYCODE_WAKEUP >/dev/null 2>&1 || true
  if adb -s "$serial" shell pidof com.topgamesinc.evony >/dev/null 2>&1; then
    :   # ya viva: no la tocamos (relanzarla provocaría cold-load y popups innecesarios)
  else
    adb -s "$serial" shell am start -n "$EVONY_ACT" >/dev/null 2>&1 || true
    # Evony recién arrancada abre MODALES (Daily Rewards / regalos de login) que tapan el world
    # map. Si no se pelan, el agente ni siquiera llega a reportar nav_stuck: la inyección agota
    # el tiempo -> HARD-RESET -> el reinicio reabre el popup -> bucle (visto 2026-08-10 en E).
    # BACK x2 espaciados, igual que _nav_unstick. Ojo: un BACK de más abre "exit the game?", pero
    # otro BACK lo CANCELA; lo que nunca hay que pulsar es Quit.
    sleep 45
    adb -s "$serial" shell am start -n "$EVONY_ACT" >/dev/null 2>&1 || true
    for _b in 1 2; do
      adb -s "$serial" shell input keyevent 4 >/dev/null 2>&1 || true
      sleep 2
    done
    adb -s "$serial" shell am start -n "$EVONY_ACT" >/dev/null 2>&1 || true

    # ── Esperar a que el juego RENDERICE antes de que el backend attache (2026-08-10) ──
    # MEDIDO: si frida inyecta mientras el juego sigue cargando, la pantalla se queda EN
    # BLANCO — el juego vive y el agente llega a decir "sweep activo", pero no renderiza y
    # devuelve 0 objetos — y el attach se va en `create_script timeout`. Attachando a un
    # juego YA CARGADO ambas mitades entran a la 1ª. Regla: captura ~15KB = pantalla
    # blanca/negra, >1MB = renderiza (el mismo criterio del shortcut de reinicio).
    _renders(){ local f="/tmp/sv_render_$$.png" sz
      adb -s "$1" exec-out screencap -p > "$f" 2>/dev/null
      sz=$(stat -f%z "$f" 2>/dev/null || echo 0); rm -f "$f"; [ "$sz" -gt 1000000 ]; }
    _ok=0
    for _i in 1 2 3 4 5 6 7 8 9; do            # hasta ~90s
      if _renders "$serial"; then _ok=$((_ok+1)); else _ok=0; fi
      [ "$_ok" -ge 2 ] && break                # 2 lecturas buenas seguidas
      sleep 10
    done
    if [ "$_ok" -lt 2 ]; then
      log "$serial: pantalla EN BLANCO tras el arranque -> relanzando el juego una vez"
      adb -s "$serial" shell am force-stop com.topgamesinc.evony >/dev/null 2>&1 || true
      sleep 3
      adb -s "$serial" shell input keyevent KEYCODE_WAKEUP >/dev/null 2>&1 || true
      adb -s "$serial" shell am start -n "$EVONY_ACT" >/dev/null 2>&1 || true
      sleep 50
      for _b in 1 2; do adb -s "$serial" shell input keyevent 4 >/dev/null 2>&1 || true; sleep 2; done
      adb -s "$serial" shell am start -n "$EVONY_ACT" >/dev/null 2>&1 || true
    fi
    if _renders "$serial"; then log "$serial: juego RENDERIZANDO (listo para attach)"
    else log "$serial: ⚠ sigue EN BLANCO; el escáner podría no ver nada en esta mitad"; fi
  fi
done

BIND="$(tailscale ip -4 2>/dev/null | head -1)"; [ -z "$BIND" ] && BIND=127.0.0.1
log "prep OK (W=$DW E=$DE bind=$BIND:$PORT). Supervisando backend con respawn…"

# --- SUPERVISOR: mantiene el backend vivo; un crash (SIGSEGV frida) -> respawn en 8s (throttle anti-flap) ---
FAILS=0; LAST_START=0
while true; do
  # rotación del log si pasa de 25MB (archiva a .1) -> no crece sin límite, conserva forense
  if [ -f "$LOG" ] && [ "$(stat -f%z "$LOG" 2>/dev/null || echo 0)" -gt 26214400 ]; then mv "$LOG" "$LOG.1" 2>/dev/null || true; fi
  NOW=$(date +%s)
  if [ $((NOW - LAST_START)) -lt 20 ]; then FAILS=$((FAILS+1)); else FAILS=0; fi
  LAST_START=$NOW
  if [ "$FAILS" -ge 5 ]; then log "5 crashes rápidos seguidos -> pausa 120s (anti-flap)"; sleep 120; FAILS=0; fi
  log "(re)lanzando backend en :$PORT"
  SCANNER_W="$DW" SCANNER_E="$DE" ISCOUT_BIND="$BIND" "$PY" "$HERE/iscout_web.py" >> "$LOG" 2>&1
  CODE=$?
  log "backend salió (code=$CODE) -> respawn en 8s"
  lsof -ti :$PORT 2>/dev/null | xargs kill -9 2>/dev/null || true   # libera el puerto por si quedó ocupado
  sleep 8
done
