#!/usr/bin/env bash
# latido.sh — el latido de la granja SIN depender del init.
#
# POR QUÉ EXISTE (2026-07-22): el latido vivía en el crontab, y eso resultó frágil por dos motivos
# que se destaparon juntos tras un corte sucio:
#   1. Este laptop arranca a veces con `init=/usr/local/sbin/arje-zero` y a veces con OpenRC (KDE).
#      No hay systemd en NINGUNO de los dos. `cronie` es un servicio OpenRC ⇒ en el arranque arje
#      simplemente no está, y el latido no late. Peor: no late EN SILENCIO, nadie se entera.
#   2. Aun arrancando OpenRC, se vio el runlevel `default` quedar a medias (cronie/metalog/acpid
#      caídos, NetworkManager/dbus arriba) ⇒ ni siquiera ahí es garantía.
# El síntoma final es caro: sin latido el hub no siembra, el worker agota la cola y se queda idle
# quemando € sin moler (viola [[vps-nunca-idle]]).
#
# LA IDEA: colgar el latido de la SESIÓN en vez del init. Cualquier terminal que abras, en cualquier
# arranque, se asegura de que haya exactamente un latido vivo. Sin root, sin unidad de servicio, sin
# nada que mantener por duplicado en dos inits.
#
# CONTRA (asumido a propósito): no late con la máquina encendida pero sin sesión tuya abierta. Es un
# laptop, no un server: cuando no hay sesión tampoco hay nadie esperando la cosecha. Y el primer
# ciclo dispara AL INSTANTE de abrir la terminal, así que un arranque nunca arrastra el hueco (el
# cron, en cambio, esperaba hasta 30 min al próximo tick).
#
# SINGLETON: el lock lo lleva `cosecha-cron.sh` (así vale también si el cron de OpenRC dispara a la
# vez); acá hay un segundo lock, el del BUCLE, para no acumular un demonio por terminal abierta.
#
# Uso:
#   latido.sh --ensure    # idempotente y barato: si no hay latido, lo lanza. Esto va en ~/.zshrc
#   latido.sh --loop      # el bucle en sí (lo lanza --ensure; no hace falta llamarlo a mano)
#   latido.sh --status    # ¿late? desde cuándo, y cuándo fue el último ciclo
#   latido.sh --stop      # parar el latido
# Env: INTERVALO (def 1800s = los mismos 30 min que tenía el crontab)
set -uo pipefail

ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
INTERVALO="${INTERVALO:-1800}"
LOCK="$ROOT/work/.latido.lock"
LOG="$ROOT/work/latido.log"
COSECHA_LOG="$ROOT/work/cosecha-cron.log"
mkdir -p "$ROOT/work"

ts() { date -u +%FT%TZ; }

case "${1:---ensure}" in
  --ensure)
    # Barato a propósito: esto corre en CADA terminal que abrís. Un flock que falla es ~1ms y no
    # imprime nada — abrir una terminal no puede volverse lento ni ruidoso por el latido.
    exec 9>"$LOCK"
    if flock -n 9; then
      flock -u 9
      # setsid + nohup: que sobreviva al cierre de ESTA terminal. Si no, el latido se muere con la
      # ventana que lo parió y volvemos al problema de origen.
      setsid nohup "$0" --loop </dev/null >/dev/null 2>&1 &
      disown 2>/dev/null || true
    fi
    exit 0
    ;;

  --status)
    if exec 9>"$LOCK" && flock -n 9; then
      flock -u 9
      echo "latido: PARADO"
      exit 1
    else
      echo "latido: VIVO"
      [ -f "$LOG" ] && tail -3 "$LOG"
      exit 0
    fi
    ;;

  --stop)
    pkill -f "latido.sh --loop" && echo "latido: parado" || echo "latido: no había ninguno vivo"
    exit 0
    ;;

  --loop) ;;
  *) echo "uso: latido.sh --ensure|--loop|--status|--stop" >&2; exit 2 ;;
esac

# --- el bucle ---
# El lock se mantiene TOMADO mientras el bucle vive: es lo que hace que --ensure sepa que ya hay uno
# y que --status pueda responder sin pidfiles (un pidfile miente si el proceso murió; el lock no).
exec 9>"$LOCK"
flock -n 9 || exit 0   # otro latido ganó la carrera (dos terminales abiertas a la vez)

echo "── $(ts) latido arranca (pid $$, intervalo ${INTERVALO}s)" >>"$LOG"
trap 'echo "── $(ts) latido termina (pid $$)" >>"$LOG"' EXIT

while :; do
  # `|| true` + `set -uo pipefail` sin `-e`: un ciclo que falla (worker caído, red, gitea) NO puede
  # matar el latido. Se loguea y se reintenta al próximo tick; ésa es toda la robustez que hace falta.
  ( cd "$ROOT" && ./scripts/farm/cosecha-cron.sh ) >>"$COSECHA_LOG" 2>&1 || \
    echo "── $(ts) latido: el ciclo salió != 0 (ver cosecha-cron.log) — sigo" >>"$LOG"
  sleep "$INTERVALO"
done
