#!/bin/bash
# HARD RESET de un nodo del mesh: reinicio COMPLETO del dispositivo + relanzamiento de cero. NO toca datos/caché
# (login intacto, no re-login). Ramifica según el tipo de nodo:
#   · FÍSICO  (Rex/Valk/2XS, avd vacío):  stop_node -> adb reboot -> esperar boot -> frida -> descartar keyguard
#                                          -> lanzar Evony -> start_node -> verificar SCAN.
#   · EMULADOR (Lume/DaSea/NiteMan, avd):  stop_node -> adb emu kill -> full_restart.sh (cold-boot del AVD: resetea
#                                          el contexto EGL colgado + frida + juego) -> start_node -> verificar SCAN.
# Uso:  hard_reset.sh <node_id>
set -u
HERE="$(cd "$(dirname "$0")" && pwd)"
source "$HERE/_common.sh"
[ -f "$HERE/.hub_auth" ] && source "$HERE/.hub_auth" 2>/dev/null   # BOT_USER/BOT_PASS para nodos publicados (2xs)

id="${1:-}"
[ -z "$id" ] && { echo "uso: hard_reset.sh <node_id>"; exit 1; }
port=$(node_field "$id" 2); dev=$(node_field "$id" 3); avd=$(node_field "$id" 4); name=$(node_field "$id" 5)
[ -z "$port" ] && { echo "[hard_reset] nodo desconocido: $id"; exit 1; }
PKG="com.topgamesinc.evony"
L(){ echo "[hard_reset:$id $(date +%H:%M:%S)] $*"; }

# LOCK por-nodo (cinturón y tirantes sobre el guard del hub): mkdir es atómico en POSIX.
# Si ya hay un hard reset en curso para este nodo, salimos (evita reboots solapados por doble disparo).
LOCK="/tmp/mesh_hardreset_${id}.lock"
if [ -d "$LOCK" ]; then
  age=$(( $(date +%s) - $(stat -f %m "$LOCK" 2>/dev/null || echo 0) ))   # BSD stat (macOS)
  [ "$age" -gt 900 ] && { echo "[hard_reset:$id] lock viejo (${age}s) -> lo robo"; rmdir "$LOCK" 2>/dev/null; }
fi
if ! mkdir "$LOCK" 2>/dev/null; then
  echo "[hard_reset:$id] YA hay un hard reset en curso para $id -> salgo (exit 5)"; exit 5
fi
trap 'rmdir "$LOCK" 2>/dev/null' EXIT

RUN_START=$(date +%s)
L "INICIO hard reset de $name ($dev)"

# 1) parar el bot_web del nodo (no debe thrashear durante el reset)
stop_node "$id" >/dev/null 2>&1; sleep 2

if [ -z "$avd" ]; then
  # ===================== FÍSICO: reboot del dispositivo =====================
  L "nodo parado -> reboot del dispositivo FÍSICO"

  # 2) reboot del dispositivo físico
  adb -s "$dev" reboot 2>&1 | sed 's/^/   /'
  sleep 25

  # 3) esperar a que vuelva (adb de red: reconectar) + boot_completed
  ok=0
  for i in $(seq 1 40); do            # ~40*8s = 5min máx
    adb connect "$dev" >/dev/null 2>&1
    st=$(adb -s "$dev" get-state 2>/dev/null)
    boot=$(adb -s "$dev" shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')
    if [ "$st" = "device" ] && [ "$boot" = "1" ]; then ok=1; L "boot completado (intento $i)"; break; fi
    sleep 8
  done
  [ "$ok" != 1 ] && { L "ERROR: el dispositivo no volvió tras el reboot"; exit 3; }
  sleep 5

  # 4) asegurar frida-server (auto-arranca al boot; si no, lanzarlo con setsid root)
  fp=$(adb -s "$dev" shell "su -c 'pidof frida-server'" 2>/dev/null | tr -d '\r')
  if [ -z "$fp" ]; then
    L "frida-server no vivo -> lanzando (setsid root)"
    adb -s "$dev" shell "su -c 'setsid /data/local/tmp/frida-server >/dev/null 2>&1 < /dev/null &'" 2>/dev/null
    sleep 4
    fp=$(adb -s "$dev" shell "su -c 'pidof frida-server'" 2>/dev/null | tr -d '\r')
  fi
  L "frida-server pid=${fp:-NONE}"

  # 5) despertar pantalla + DESCARTAR KEYGUARD (clave: si no, Evony no renderiza/carga tras el reboot)
  adb -s "$dev" shell input keyevent KEYCODE_WAKEUP 2>/dev/null
  adb -s "$dev" shell wm dismiss-keyguard 2>/dev/null
  adb -s "$dev" shell input keyevent 82 2>/dev/null       # MENU (fallback para descartar)
  sleep 1
  L "keyguard descartado"

  # 6) lanzar Evony
  adb -s "$dev" shell "monkey -p $PKG -c android.intent.category.LAUNCHER 1" >/dev/null 2>&1
  L "Evony lanzado -> esperando carga de la cuenta"
  sleep 45                               # margen para splash -> ciudad (el nodo detecta la carga al attachear)
  # re-descartar keyguard por si volvió a bloquear durante la carga
  adb -s "$dev" shell wm dismiss-keyguard 2>/dev/null
else
  # ===================== EMULADOR: cold-boot del AVD =====================
  # full_restart.sh es IDEMPOTENTE (no reinicia un emulador vivo, solo trae el juego al frente) -> para forzar
  # el arranque en FRÍO (resetea el contexto EGL colgado) hay que matar el emulador ANTES.
  L "nodo parado -> emu kill del emulador $avd ($dev)"
  adb -s "$dev" emu kill 2>/dev/null
  sleep 6

  # cold-boot vía full_restart.sh (honra BOT_EMULATOR/BOT_AVD): arranca el emulador en frío + frida + juego.
  # OJO: SIN pipe ni $(...) — el emulador se lanza como demonio y heredaría el fd -> hang (misma lección del físico).
  L "cold-boot: full_restart.sh (emulador + frida + Evony)"
  BOT_EMULATOR="$dev" BOT_AVD="$avd" bash "$APP/full_restart.sh" </dev/null 2>&1
  # confirmar que el emulador volvió a adb (full_restart.sh espera el boot internamente)
  adb connect "$dev" >/dev/null 2>&1
  st=$(adb -s "$dev" get-state 2>/dev/null)
  [ "$st" != "device" ] && { L "ERROR: el emulador no volvió tras el cold-boot"; exit 3; }
  L "emulador operativo -> esperando carga de la cuenta"
  sleep 40                               # margen para que el juego (am start) cargue tras el cold-boot
fi

# 7) arrancar el bot_web del nodo -> attach + carga cuenta + SCAN
# OJO: NADA de `| sed` ni `$(start_node ...)` aquí. start_node lanza bot_web como demonio y éste hereda
# el fd de escritura del pipe/substitución -> nunca llega EOF -> el script se COLGABA tras start_node
# (sin imprimir OK ni liberar el lock). Salida directa al stdout del script (= log del hub), sin pipe.
start_node "$id" </dev/null 2>&1

# 8) verificar operativo: SCAN NUEVO (no uno viejo). Snapshot del tamaño del log tras start_node
#    (start_node trunca $id.log) y solo aceptamos un 'SCAN ciudad' en el contenido POSTERIOR.
sleep 3
OFF=$(wc -c < "$LOGDIR/$id.log" 2>/dev/null || echo 0)
for i in $(seq 1 18); do               # ~18*10s = 3min
  sleep 10
  if tail -c +$((OFF+1)) "$LOGDIR/$id.log" 2>/dev/null | grep -q 'SCAN ciudad'; then
    L "OK: $name operativo (escaneando con SCAN fresco) tras hard reset"; exit 0
  fi
done
L "AVISO: hard reset completado pero no confirmé SCAN fresco a tiempo (revisar; puede seguir cargando)"
exit 4
