#!/bin/bash
### BEGIN INIT INFO
# Provides:          emalibre
# Required-Start:    $remote_fs $network $syslog
# Required-Stop:     $remote_fs $network $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: EMA Libre Carpincho web
### END INIT INFO

PATH=/usr/sbin:/usr/bin:/sbin:/bin
export PATH
. /lib/lsb/init-functions

appFilePath=/usr/share/emalibre
pidFile=/run/emalibre.pid
logDir=/var/log/emalibre
modeFile=/etc/emalibre_modo
foreverApp="$appFilePath/node_modules/forever/bin/forever"
# Mantener el registro de instalaciones anteriores iniciadas como root.
export HOME=/root
export FOREVER_ROOT=/root/.forever
nodeBin="$appFilePath/node_modules/node/bin/node"
export PATH="$appFilePath/node_modules/node/bin:$PATH"

fail() { echo "emalibre: $*" >&2; return 1; }
require_root() {
    [ "$(id -u)" -eq 0 ] || { fail "Este comando requiere root."; return 4; }
}
check_runtime() {
    [ -x "$nodeBin" ] || { fail "Falta el Node incluido en $nodeBin (Node.js >= 20)."; return 5; }
    "$nodeBin" -e 'process.exit(Number(process.versions.node.split(".")[0]) >= 20 ? 0 : 1)' || {
        fail "Se requiere Node.js >= 20."; return 5;
    }
    [ -f "$foreverApp" ] && [ -f "$appFilePath/gui_web/express.js" ] || {
        fail "La instalación web está incompleta."; return 5;
    }
}
node_run() { "$nodeBin" "$@" 9>&-; }
forever_cmd() { node_run "$foreverApp" "$@"; }
# Consultar el supervisor, sin confiar en archivos PID residuales.
# 0: activo; 3: detenido; 4: no se pudo consultar.
process_status() {
    "$nodeBin" - "$appFilePath" <<'JS'
const root = process.argv[2];
const timer = setTimeout(() => process.exit(4), 5000);
try {
    require(root + '/node_modules/forever').list(false, (err, processes) => {
        clearTimeout(timer);
        if (err) { console.error(err.message); process.exit(4); }
        const active = (processes || []).some(p => p.uid === 'EMA_Web');
        process.exit(active ? 0 : 3);
    });
} catch (err) { console.error(err.message); process.exit(4); }
JS
}
start() {
    local state mode
    process_status; state=$?
    case "$state" in
        0) echo "EMA Libre ya está ejecutándose."; return 0 ;;
        3) ;;
        *) fail "No se pudo consultar Forever."; return "$state" ;;
    esac
    if [ ! -f "$modeFile" ]; then
        printf 'web\n' > "$modeFile" || return 1
    fi
    mode=$(tr -d '[:space:]' < "$modeFile") || return 1
    [ "$mode" = web ] || { fail "Modo inválido: '$mode'; se esperaba web."; return 2; }
    mkdir -p "$logDir" || return 1
    rm -f "$pidFile" || return 1
    # Forever concatena sourceDir y script: el script debe ser relativo.
    forever_cmd start --uid EMA_Web --id EMA_Web --minUptime 5000 --spinSleepTime 5000 \
        --sourceDir "$appFilePath" --workingDir "$appFilePath" --pidFile "$pidFile" \
        -l "$logDir/forever.log" -o "$logDir/stdout.log" -e "$logDir/stderr.log" \
        -a -c "$nodeBin" "gui_web/express.js" iniciar || return $?
    # Forever arranca en segundo plano: esperar a que registre el supervisor.
    local attempt
    for attempt in {1..10}; do
        process_status; state=$?
        [ "$state" -eq 0 ] && { echo "EMA Libre iniciado."; return 0; }
        [ "$state" -eq 3 ] || return "$state"
        sleep 1
    done
    fail "El supervisor no arrancó; revise $logDir."
}
stop() {
    local state
    process_status; state=$?
    case "$state" in
        0) forever_cmd stop EMA_Web || return $? ;;
        3) echo "EMA Libre ya está detenido." ;;
        *) fail "No se pudo consultar Forever."; return "$state" ;;
    esac
    process_status; state=$?
    [ "$state" -eq 3 ] || { fail "No se pudo confirmar la parada."; return 1; }
    rm -f "$pidFile"
}
status() {
    local state
    process_status; state=$?
    case "$state" in
        0) echo "EMA Libre: supervisor activo." ;;
        3) echo "EMA Libre: detenido." ;;
        *) fail "Estado desconocido; no se pudo consultar Forever." ;;
    esac
    return "$state"
}
set_boot() {
    # El arranque del servicio es independiente del inicio del recolector.
    if [ -d /run/systemd/system ]; then
        systemctl "$1" emalibre
    else
        update-rc.d emalibre defaults && update-rc.d emalibre "$1"
    fi
}
view_conf() {
    printf 'Modo: '; cat "$modeFile" || return 1
    if [ -d /run/systemd/system ]; then
        systemctl is-enabled emalibre
    else
        echo 'Inicio del servicio: consultar enlaces /etc/rc[2-5].d/*emalibre'
    fi
    "$nodeBin" - "$appFilePath/conf/ConfiguracionGeneral.js" <<'JS'
const cfg = require(process.argv[2]).ConfGeneral;
for (const key of ['Tiempo_de_Recoleccion', 'UTC', 'SistemaMetrico', 'debug',
    'debugTamanio', 'NombreEstacion', 'Modo', 'inicio_automatico', 'Audio',
    'notificar_cambios', 'audioVoces', 'IP', 'Puerto', 'Ruta']) {
    console.log(key + ': ' + cfg[key]);
}
JS
}

case "${1:-}" in
    start|stop|restart|force-reload|status|uptime|reporte|view-conf|set-inicio-enabled|set-inicio-disable) ;;
    *) echo "Uso: $0 {start|stop|restart|force-reload|status|uptime|reporte|view-conf|set-inicio-enabled|set-inicio-disable}" >&2; exit 2 ;;
esac
require_root || exit $?
check_runtime || exit $?
case "$1" in
    start|stop|restart|force-reload|set-inicio-enabled|set-inicio-disable)
        # Evitar arranques/paradas simultáneos. Cerrar el descriptor en Node
        # impide que el daemon herede y retenga el bloqueo.
        exec 9>/run/lock/emalibre.lock || exit 1
        flock -w 30 9 || exit 1
        ;;
esac
case "$1" in
    start) start ;;
    stop) stop ;;
    restart|force-reload) stop && start ;;
    status) status ;;
    uptime) forever_cmd list ;;
    reporte) (cd "$appFilePath" && node_run core/reporte.js) ;;
    view-conf) view_conf ;;
    set-inicio-enabled) set_boot enable ;;
    set-inicio-disable) set_boot disable ;;
esac
exit $?
