#!/usr/bin/env bash
# Lanzador del JARVIS naranja: el nucleo, fuera del flatpak.
#
#   arranca.sh            levanta ollama si hace falta, abre el panel y escucha
#   arranca.sh --panel    solo el panel, sin microfono
#   arranca.sh --stop     lo para y libera la grafica
#   arranca.sh --estado   dice si esta vivo
#
# Comparado con el lanzador del JARVIS azul, aqui sobra casi todo: no hay que
# adivinar la rama del flatpak, ni leer gsettings con escapado de GVariant, ni
# preguntarle a la aplicacion que modelo tiene puesto. El nucleo lo sabe porque
# es suyo.
#
# Lo que SI se conserva, porque en esta maquina importa: al parar se le dice a
# ollama que suelte el modelo. Son 2,1 GB de una tarjeta de 4, y sin esto whisper
# se queda sin sitio la proxima vez.
set -uo pipefail

RAIZ=$(cd -P "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)
PYTHON=$RAIZ/venv/bin/python
[ -x "$PYTHON" ] || PYTHON=$(command -v python3)
OLLAMA_URL=${JARVIS_OLLAMA:-http://127.0.0.1:11434}
REGISTRO=$RAIZ/jarvis.log
PUERTO=${JARVIS_PUERTO:-8790}

# Fichero de PID, no pgrep.
#
# El primer intento fue marcar el proceso con una variable de entorno y buscarla
# con `pgrep -f`. No funciona: pgrep mira la LINEA DE ORDENES, y `env VAR=1 cmd`
# no deja la variable ahi. El sintoma fue peor que un simple "no lo encuentra":
# el lanzador creia que no habia nada corriendo, arrancaba un segundo, y ese
# moria con "address already in use" mientras el primero seguia vivo.
#
# Y buscar por el nombre del script tiene el problema de siempre: el patron
# aparece en la linea de ordenes del propio pgrep. Un fichero de PID no se
# equivoca.
PID=${XDG_RUNTIME_DIR:-/tmp}/jarvis-nucleo.pid

aviso() { command -v notify-send >/dev/null && \
          notify-send -i "$RAIZ/jarvis-naranja.svg" "JARVIS" "$1"; }

modelo() {
    grep -oP 'MODELO = os\.environ\.get\("JARVIS_MODELO", "\K[^"]+' \
        "$RAIZ/cerebro/ollama.py" 2>/dev/null || echo "qwen3.5:4b-jarvis"
}

# Vivo = hay un pid apuntado Y ese proceso existe. Un pid huerfano de un
# reinicio no puede hacernos creer que sigue en marcha.
vivo() {
    [ -f "$PID" ] || return 1
    local p; p=$(cat "$PID" 2>/dev/null)
    [ -n "$p" ] && kill -0 "$p" 2>/dev/null
}
ollama_up() { curl -s --max-time 2 "$OLLAMA_URL/api/version" >/dev/null 2>&1; }
puerto_ocupado() { curl -s --max-time 2 "http://127.0.0.1:$PUERTO/" >/dev/null 2>&1; }

case "${1:-arranca}" in
  --stop)
      # Tambien lo que este ocupando el puerto sin fichero de pid: si no, un
      # huerfano impide arrancar de nuevo y --stop parece no hacer nada.
      if ! vivo && puerto_ocupado && command -v fuser >/dev/null; then
          fuser -k "$PUERTO/tcp" >/dev/null 2>&1
      fi
      if vivo; then
          kill "$(cat "$PID")" 2>/dev/null
          # margen para que suelte el puerto antes de que nadie lo reclame
          for _ in 1 2 3 4 5 6 7 8 9 10; do vivo || break; sleep 0.3; done
          vivo && kill -9 "$(cat "$PID")" 2>/dev/null
      fi
      rm -f "$PID"
      if ollama_up; then
          # keep_alive 0: suelta el modelo ya, no dentro de cinco minutos
          curl -s "$OLLAMA_URL/api/generate" \
               -d "{\"model\":\"$(modelo)\",\"keep_alive\":0}" >/dev/null 2>&1
          aviso "Detenido, grafica liberada"
      else
          aviso "Detenido"
      fi
      exit 0
      ;;

  --estado)
      # Se mira el pid Y el puerto. Con solo el pid, una instancia arrancada a
      # mano —o superviviente de un lanzador que murio— daba "parado" mientras
      # el panel contestaba tan tranquilo, que es la peor respuesta posible:
      # la que hace dudar de si el problema es el programa o quien pregunta.
      if vivo; then
          echo "JARVIS naranja: vivo"
          exit 0
      fi
      if puerto_ocupado; then
          echo "JARVIS naranja: vivo, pero arrancado por fuera de este lanzador"
          echo "  (no hay $PID; para pararlo: fuser -k $PUERTO/tcp)"
          exit 0
      fi
      echo "JARVIS naranja: parado"
      exit 1
      ;;
esac

if vivo || puerto_ocupado; then
    # Ya esta abierto: en vez de levantar otro —que moriria con "address already
    # in use" y dejaria un rastro confuso— se trae el panel al frente.
    aviso "Ya estaba en marcha"
    # La MISMA ventana que abre el arranque normal. Antes esto era xdg-open, que
    # abre el navegador POR DEFECTO en una pestaña: segun si JARVIS ya estaba
    # abierto o no, el mismo icono daba una aplicacion o una pestaña de Firefox.
    setsid "$RAIZ/ventana.sh" "http://127.0.0.1:$PUERTO/" >/dev/null 2>&1 &
    exit 0
fi

# ollama, si no esta ya. En segundo plano y sin esperar: el catalogo contesta
# las ordenes conocidas sin tocar el modelo, asi que JARVIS es util desde el
# primer segundo aunque ollama tarde en calentar.
if ! ollama_up && command -v ollama >/dev/null; then
    setsid ollama serve >/dev/null 2>&1 &
fi

# El indice de COFRE, si no esta. En segundo plano: JARVIS arranca sin el y el
# cerebro lo usa en cuanto este listo. Reconstruirlo a mano: saber/indexa.py
if [ ! -f "$RAIZ/datos/saber.jsonl" ]; then
    setsid "$PYTHON" "$RAIZ/saber/indexa.py" >/dev/null 2>&1 &
fi

: > "$REGISTRO" 2>/dev/null || REGISTRO=/dev/null
aviso "Arrancando"

MODO=(--panel --escucha)
[ "${1:-}" = "--panel" ] && MODO=(--panel)

# El pid se apunta ANTES del exec y es el de este mismo shell, que con exec
# pasa a ser el de Python: asi el fichero nunca apunta a un proceso que no es.
echo $$ > "$PID"
trap 'rm -f "$PID"' EXIT
exec "$PYTHON" -u "$RAIZ/jarvis.py" "${MODO[@]}" >>"$REGISTRO" 2>&1
