#!/bin/bash
# =============================================================================
# AulaSync — Instalador interactivo (servidor compartido con PHP-FPM)
# Ubuntu Server 22.04 / 24.04
#
# Requisitos previos: Apache 2 y MySQL ya instalados y en ejecución.
# Uso: sudo bash install-fpm.sh
# =============================================================================

set -e

# ── Modo no-interactivo (impide que dialogs de debconf/needrestart cuelguen el script) ──
# A partir de Ubuntu 22.10 'needrestart' viene preinstalado y, tras instalar paquetes
# que tocan librerías (fuentes, certbot, php-fpm…), abre un dialog whiptail preguntando
# qué servicios reiniciar. Ese dialog NO se silencia con -y -qq y deja el script
# colgado esperando input invisible. Estas variables eliminan esos prompts.
export DEBIAN_FRONTEND=noninteractive
export NEEDRESTART_MODE=a       # auto: reinicia servicios sin preguntar
export NEEDRESTART_SUSPEND=1    # backup: suprime needrestart en versiones antiguas que ignoran MODE

# ── Colores ───────────────────────────────────────────────────────────────────
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'

info()    { echo -e "${BLUE}[INFO]${NC}  $1"; }
success() { echo -e "${GREEN}[OK]${NC}    $1"; }
warn()    { echo -e "${YELLOW}[WARN]${NC}  $1"; }
error()   { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
ask()     { echo -en "${CYAN}[?]${NC}    $1 "; }
title()   { echo -e "\n${BOLD}${BLUE}━━━  $1  ━━━${NC}\n"; }

# ── Verificaciones previas ────────────────────────────────────────────────────
[ "$EUID" -ne 0 ] && error "Ejecuta como root: sudo bash install-fpm.sh"

ADMIN_USER="${SUDO_USER:-$(logname 2>/dev/null || echo '')}"
[ -z "$ADMIN_USER" ] && error "No se pudo detectar el usuario administrador. Ejecuta con sudo desde tu cuenta."

# Comprobar que Apache y MySQL/MariaDB están instalados antes de seguir.
# Aceptamos cualquiera de los dos motores: el paso 5 detecta cuál es y usa la
# sintaxis SQL adecuada (MariaDB no soporta 'IDENTIFIED WITH mysql_native_password').
command -v apache2 &>/dev/null || error "Apache no está instalado. Instálalo antes de ejecutar este script."
if ! command -v mysql &>/dev/null && ! command -v mariadb &>/dev/null; then
    error "Ni MySQL ni MariaDB están instalados. Instala uno antes de ejecutar este script."
fi
systemctl is-active --quiet apache2 || error "Apache no está en ejecución. Arráncalo con: systemctl start apache2"
# El servicio puede llamarse 'mysql', 'mysqld' o 'mariadb' según la distro
if ! systemctl is-active --quiet mariadb 2>/dev/null && \
   ! systemctl is-active --quiet mysql   2>/dev/null && \
   ! systemctl is-active --quiet mysqld  2>/dev/null; then
    error "El servicio de base de datos no está en ejecución (probado: mariadb, mysql, mysqld)."
fi

SERVER_IP=$(hostname -I | awk '{print $1}')
# Lista completa de IPs locales del servidor (para detectar si el gateway es este mismo host)
SERVER_IPS=$(hostname -I)
PHP_VERSION="8.4"
PHP_FPM_SOCK="/run/php/php${PHP_VERSION}-fpm-aulasync.sock"
ANSIBLE_DIR="/etc/ansible/aller"
GITHUB_REPO="https://github.com/Hugogub/AulaSync.git"
GITHUB_BRANCH="master"

# ── Detección automática ──────────────────────────────────────────────────────
# Directorio raíz de Apache (sin /public).
# Detecta el DocumentRoot de los VirtualHosts habilitados; si falla, usa /var/www/html.
APACHE_DOCROOT=""
if [ -d /etc/apache2/sites-enabled ]; then
    APACHE_DOCROOT=$(grep -rhE '^[[:space:]]*DocumentRoot[[:space:]]+' /etc/apache2/sites-enabled/ 2>/dev/null \
        | head -1 \
        | awk '{print $2}' \
        | tr -d '"' \
        | sed 's|/public$||' \
        | sed 's|/$||')
fi
# Si el resultado no es una ruta absoluta válida, aplicar fallback
if [ -z "$APACHE_DOCROOT" ] || [ "${APACHE_DOCROOT:0:1}" != "/" ]; then
    APACHE_DOCROOT="/var/www/html"
fi

DHCP_CONF_DETECTED=$(find /etc -name "dhcpd.conf" 2>/dev/null | head -1 || true)
GATEWAY_IP_DETECTED=""
if [ -n "$DHCP_CONF_DETECTED" ]; then
    # Filtrar:
    #   - líneas comentadas (empiezan por # opcionalmente con espacios delante)
    #   - líneas con 'option routers' al principio del token (no parciales)
    # Y elegir la IP mayoritaria: si el dhcpd.conf tiene varias subnets/grupos
    # con la misma IP de gateway, esa es casi con total seguridad la correcta.
    GATEWAY_IP_DETECTED=$(grep -E '^[[:space:]]*option[[:space:]]+routers[[:space:]]' "$DHCP_CONF_DETECTED" 2>/dev/null \
        | awk '{print $3}' | tr -d ';' \
        | sort | uniq -c | sort -rn | head -1 | awk '{print $2}' || true)
fi

# =============================================================================
# FASE 1 — RECOGER TODOS LOS PARÁMETROS (sin interrupciones después)
# =============================================================================
title "INSTALACIÓN DE AULASYNC"
echo -e "  Usuario administrador detectado: ${BOLD}${ADMIN_USER}${NC}"
echo -e "  IP del servidor:                 ${BOLD}${SERVER_IP}${NC}"
echo ""

# 1. Directorio de instalación
DEFAULT_APP_DIR="${APACHE_DOCROOT}/AulaSync"
ask "Directorio de instalación [${DEFAULT_APP_DIR}]:"
read -r INPUT; APP_DIR="${INPUT:-$DEFAULT_APP_DIR}"

# 2. ServerName del VirtualHost
ask "Nombre del VirtualHost (ServerName) [aulasync.ciclosallerulloa.gal]:"
read -r INPUT; SERVER_NAME="${INPUT:-aulasync.ciclosallerulloa.gal}"

# 3. GitHub token (solo si el repositorio no existe ya)
GITHUB_TOKEN=""
if [ ! -d "${APP_DIR}/.git" ]; then
    # Intentar leer el token desde ~/token
    TOKEN_FILE="/home/${ADMIN_USER}/token"
    if [ -f "$TOKEN_FILE" ] && [ -s "$TOKEN_FILE" ]; then
        GITHUB_TOKEN=$(tr -d '[:space:]' < "$TOKEN_FILE")
        info "Token de GitHub leído desde ${TOKEN_FILE}."
    else
        info "El directorio ${APP_DIR} no existe o no es un repositorio git."
        info "Se clonará automáticamente. Necesitas un GitHub personal access token."
        info "Consejo: guarda el token en ${TOKEN_FILE} para no tener que introducirlo manualmente."
        ask "GitHub personal access token:"
        read -rs GITHUB_TOKEN; echo
        [ -z "$GITHUB_TOKEN" ] && error "El token de GitHub es obligatorio para clonar el repositorio."
    fi
else
    info "Repositorio ya existente en ${APP_DIR} — se omite el clone."
fi

# 4. Nombre del esquema de BD
ask "Nombre del esquema de base de datos [gestorallerulloa]:"
read -r INPUT; DB_DATABASE="${INPUT:-gestorallerulloa}"

# 5. Usuario MySQL con permisos de creación
ask "Usuario MySQL con permisos de creación de esquemas [root]:"
read -r INPUT; DB_ADMIN_USER="${INPUT:-root}"
ask "Password de ${DB_ADMIN_USER} en MySQL (vacío si no tiene):"
# IFS= + -r garantiza que bash no interprete ningún carácter especial (=, !, $, etc.)
IFS= read -rs DB_ADMIN_PASSWORD; echo

# 6. Password del administrador del servidor
ask "Password de ${ADMIN_USER} en este servidor (para SSH/sudo):"
read -rs ADMIN_PASSWORD; echo
[ -z "$ADMIN_PASSWORD" ] && error "El password del administrador es obligatorio."

# 7. Email del admin para acceder a la app
ask "Email de ${ADMIN_USER} para el login en la app:"
read -r ADMIN_EMAIL
[ -z "$ADMIN_EMAIL" ] && error "El email del administrador es obligatorio."

# 8. Email remitente de notificaciones
ask "Email remitente para notificaciones [departamento@ciclosallerulloa.gal]:"
read -r INPUT; MAIL_FROM="${INPUT:-departamento@ciclosallerulloa.gal}"

# 9. Usuario y password de los equipos de aula
ask "Usuario administrador en los equipos de aula [adminlocal]:"
read -r INPUT; VM_USER="${INPUT:-adminlocal}"
ask "Password de ${VM_USER} en los equipos de aula:"
read -rs VM_PASSWORD; echo
[ -z "$VM_PASSWORD" ] && error "El password de los equipos de aula es obligatorio."

# 10. Ruta al dhcpd.conf
echo ""
if [ -n "$DHCP_CONF_DETECTED" ]; then
    info "dhcpd.conf detectado en: ${DHCP_CONF_DETECTED}"
    ask "Confirma la ruta [${DHCP_CONF_DETECTED}]:"
    read -r INPUT; DHCP_CONF_PATH="${INPUT:-$DHCP_CONF_DETECTED}"
else
    warn "No se encontró dhcpd.conf automáticamente."
    ask "Ruta completa al dhcpd.conf:"
    read -r DHCP_CONF_PATH
    [ -z "$DHCP_CONF_PATH" ] && error "La ruta al dhcpd.conf es obligatoria."
fi

# 11. HTTPS con Let's Encrypt
ask "¿Configurar HTTPS con Let's Encrypt (certbot)? [S/n]:"
read -r INPUT
if [[ "$INPUT" =~ ^[Nn] ]]; then
    USE_HTTPS=false
else
    USE_HTTPS=true
fi

# 12. Gateway para control de internet
if [ -n "$GATEWAY_IP_DETECTED" ]; then
    ask "IP del gateway detectada: ${BOLD}${GATEWAY_IP_DETECTED}${NC} — ¿es correcta? [S/n]:"
    read -r INPUT
    if [[ "$INPUT" =~ ^[Nn] ]]; then
        ask "Introduce la IP del gateway:"
        read -r GATEWAY_IP
        [ -z "$GATEWAY_IP" ] && error "La IP del gateway es obligatoria."
    else
        GATEWAY_IP="$GATEWAY_IP_DETECTED"
    fi
else
    ask "IP del gateway (para control de acceso a internet):"
    read -r GATEWAY_IP
    [ -z "$GATEWAY_IP" ] && error "La IP del gateway es obligatoria."
fi

GATEWAY_IS_LOCAL="false"
GATEWAY_USER=""
GATEWAY_PASSWORD=""

# Comprobar si el gateway coincide con alguna IP local del servidor.
# hostname -I puede devolver varias (LAN, VPN, etc.) — hay que verificarlas todas.
IS_LOCAL_GATEWAY=false
for ip in $SERVER_IPS; do
    if [ "$ip" = "$GATEWAY_IP" ]; then
        IS_LOCAL_GATEWAY=true
        break
    fi
done

if [ "$IS_LOCAL_GATEWAY" = "true" ]; then
    info "El gateway coincide con este servidor → el control de internet se aplica en local."
    GATEWAY_IS_LOCAL="true"
    # Este servidor acaba de pasar las verificaciones previas con apt-get → es Linux.
    GATEWAY_TYPE="iptables"
else
    info "El gateway (${GATEWAY_IP}) es un servidor externo. Se usará SSH para aplicar reglas."
    ask "Usuario SSH en el gateway [${ADMIN_USER}]:"
    read -r INPUT; GATEWAY_USER="${INPUT:-$ADMIN_USER}"
    ask "Password SSH del gateway:"
    read -rs GATEWAY_PASSWORD; echo
    [ -z "$GATEWAY_PASSWORD" ] && error "El password del gateway es obligatorio."
    # GATEWAY_TYPE se detecta automáticamente en FASE 2 (PASO 1b) tras instalar
    # sshpass: 'uname -s' en el gateway → FreeBSD = pfsense, Linux = iptables.
    GATEWAY_TYPE=""
fi

# Determinar protocolo para APP_URL
if [ "$USE_HTTPS" = "true" ]; then
    APP_PROTOCOL="https"
else
    APP_PROTOCOL="http"
fi

# Password de la BD generado automáticamente — solo hex, sin caracteres especiales
# que puedan dar problemas con el heredoc SQL o con el parser de .env.
DB_USER="aulasync"
DB_PASSWORD=$(openssl rand -hex 20)

# ── Confirmación final ────────────────────────────────────────────────────────
echo ""
title "RESUMEN DE CONFIGURACIÓN"
echo -e "  Directorio app:    ${BOLD}${APP_DIR}${NC}"
echo -e "  VirtualHost:       ${BOLD}${SERVER_NAME}${NC}"
echo -e "  BD:                ${BOLD}${DB_DATABASE}${NC}  (admin: ${DB_ADMIN_USER}, usuario app: ${DB_USER})"
echo -e "  Admin servidor:    ${BOLD}${ADMIN_USER}${NC}  <${ADMIN_EMAIL}>"
echo -e "  Equipos de aula:   ${BOLD}${VM_USER}${NC}"
echo -e "  dhcpd.conf:        ${BOLD}${DHCP_CONF_PATH}${NC}"
if [ "$GATEWAY_IS_LOCAL" = "true" ]; then
    echo -e "  Gateway:           ${BOLD}${GATEWAY_IP}${NC}  (este servidor)"
else
    echo -e "  Gateway:           ${BOLD}${GATEWAY_IP}${NC}  (externo — usuario: ${GATEWAY_USER})"
fi
HTTPS_LABEL=$([ "$USE_HTTPS" = true ] && echo "Sí (Let's Encrypt)" || echo 'No')
echo -e "  HTTPS:             ${BOLD}${HTTPS_LABEL}${NC}"
echo -e "  MAIL_FROM:         ${BOLD}${MAIL_FROM}${NC}"
echo ""
ask "¿Continuar con la instalación? [S/n]:"
read -r INPUT
[[ "$INPUT" =~ ^[Nn] ]] && { echo "Instalación cancelada."; exit 0; }
echo ""

# =============================================================================
# FASE 2 — INSTALACIÓN (sin más preguntas)
# =============================================================================

# ── PASO 1: Paquetes del sistema ──────────────────────────────────────────────
title "PASO 1/9 — Paquetes del sistema"

# 1a. Update del índice de apt. Quitamos -qq para que un repo roto se vea
#     (con set -e + -qq los fallos eran silenciosos).
info "Actualizando índice de paquetes…"
apt-get update || error "apt-get update falló. Revisa /etc/apt/sources.list y los repos en /etc/apt/sources.list.d/."

# 1b. Paquetes ESENCIALES — si fallan aquí el script no puede continuar.
#     Sin -qq para que cualquier error sea visible.
ESSENTIAL_PKGS=(
    curl wget git unzip zip
    software-properties-common apt-transport-https ca-certificates
    lsb-release gnupg jq acl sshpass
)
info "Instalando paquetes esenciales…"
apt-get install -y "${ESSENTIAL_PKGS[@]}" \
    || error "Falló la instalación de paquetes esenciales. Mira el output anterior para ver cuál."

# 1c. Paquetes OPCIONALES — cada uno con fallback claro si no está disponible:
#       - ttyd:  terminal SSH en navegador. En Ubuntu 22+/Debian 12+ está en repos;
#                en Debian 11 hay que usar backports. Si falta, la terminal web
#                del dashboard no funcionará pero el resto de AulaSync sí.
#       - pipx:  no se usa actualmente (Ansible va por PPA), se queda como
#                herramienta auxiliar disponible para el administrador.
#       - fonts: para que la terminal web tenga fuentes monospace presentables.
OPTIONAL_PKGS=(ttyd pipx fonts-dejavu-core fonts-liberation2)
for pkg in "${OPTIONAL_PKGS[@]}"; do
    if apt-get install -y "$pkg" 2>/dev/null; then
        info "  ✓ ${pkg} instalado"
    else
        warn "  ✗ ${pkg} no disponible en los repos de esta distro — se omite"
        if [ "$pkg" = "ttyd" ]; then
            warn "    Consecuencia: la terminal SSH web del dashboard no funcionará."
            warn "    Soluciones:"
            warn "      - Debian 11: sudo apt install -t bullseye-backports ttyd"
            warn "      - Compilar desde fuentes: https://github.com/tsl0922/ttyd"
        fi
    fi
done

# Refrescar caché de fuentes (silencioso si fc-cache no está)
command -v fc-cache &>/dev/null && fc-cache -fv > /dev/null 2>&1

# 1d. Certbot sólo si el usuario activó HTTPS
if [ "$USE_HTTPS" = "true" ]; then
    info "Instalando certbot (Let's Encrypt)…"
    apt-get install -y certbot python3-certbot-apache \
        || error "Falló la instalación de certbot. Comprueba conectividad o desactiva HTTPS."
fi

success "Paquetes instalados"

# ── PASO 1b: Detección automática del SO del gateway ───────────────────────────────
# Solo si el gateway es externo. Determina si usa iptables (Linux) o pfctl
# (pfSense/OPNsense, FreeBSD) preguntándole al gateway con 'uname -s'.
# sshpass acaba de instalarse en el paso 1 → ya disponible.
if [ "$GATEWAY_IS_LOCAL" = "false" ]; then
    title "PASO 1b — Detección del SO del gateway"
    info "Conectando a ${GATEWAY_USER}@${GATEWAY_IP} para detectar SO…"
    GW_OS=$(sshpass -p "$GATEWAY_PASSWORD" ssh \
        -o StrictHostKeyChecking=no \
        -o UserKnownHostsFile=/dev/null \
        -o ConnectTimeout=10 \
        "${GATEWAY_USER}@${GATEWAY_IP}" "uname -s" 2>/dev/null || echo "")

    case "$GW_OS" in
        FreeBSD)
            GATEWAY_TYPE="pfsense"
            success "Gateway detectado: FreeBSD (pfSense/OPNsense) → se usará pfctl"
            info "Recuerda: necesitarás configurar la tabla <aulasync_blocked> en pfSense (ver pasos finales)."
            ;;
        Linux)
            GATEWAY_TYPE="iptables"
            success "Gateway detectado: Linux → se usará iptables vía sudo"
            ;;
        *)
            error "No se pudo conectar al gateway ${GATEWAY_USER}@${GATEWAY_IP} para detectar el SO. Verifica que el gateway está accesible y las credenciales son correctas. (uname devolvió: '${GW_OS:-vacío}')"
            ;;
    esac
fi

# ── PASO 2: PHP 8.4-FPM ──────────────────────────────────────────────────────
title "PASO 2/9 — PHP ${PHP_VERSION}-FPM"
if ! grep -rq "ondrej/php" /etc/apt/sources.list.d/ 2>/dev/null; then
    add-apt-repository -y ppa:ondrej/php > /dev/null 2>&1
    apt-get update -qq
fi

apt-get install -y -qq \
    php${PHP_VERSION} php${PHP_VERSION}-fpm php${PHP_VERSION}-cli \
    php${PHP_VERSION}-mysql php${PHP_VERSION}-mbstring php${PHP_VERSION}-xml \
    php${PHP_VERSION}-bcmath php${PHP_VERSION}-curl php${PHP_VERSION}-zip \
    php${PHP_VERSION}-intl php${PHP_VERSION}-tokenizer php${PHP_VERSION}-dom \
    php${PHP_VERSION}-gd php${PHP_VERSION}-soap php${PHP_VERSION}-xsl \
    php${PHP_VERSION}-sockets

PHP_INI="/etc/php/${PHP_VERSION}/fpm/php.ini"
sed -i "s/^memory_limit = .*/memory_limit = 1280M/"              "$PHP_INI"
sed -i "s/^upload_max_filesize = .*/upload_max_filesize = 5000M/" "$PHP_INI"
sed -i "s/^post_max_size = .*/post_max_size = 5000M/"            "$PHP_INI"
sed -i "s/^max_execution_time = .*/max_execution_time = 6000/"   "$PHP_INI"
sed -i "s/^max_input_time = .*/max_input_time = 6000/"           "$PHP_INI"
sed -i "s/^;max_input_vars = .*/max_input_vars = 6000/"          "$PHP_INI"

systemctl enable php${PHP_VERSION}-fpm
systemctl start  php${PHP_VERSION}-fpm
success "PHP ${PHP_VERSION}-FPM instalado y activo"

# Pool FPM dedicado para AulaSync que corre como ${ADMIN_USER}.
# Esto es imprescindible para que Laravel pueda:
#   - Escribir en /etc/ansible/aller/ (que pertenece a adminserver)
#   - Leer la clave SSH privada de AulaSync
#   - Ejecutar ansible-playbook (instalado globalmente en /usr/bin/)
#   - Leer el token de ~/token
# Los otros apps del servidor (Moodle, Mantis, etc.) siguen usando el pool
# www por defecto como www-data.
cat > /etc/php/${PHP_VERSION}/fpm/pool.d/aulasync.conf <<POOLCONF
[aulasync]
user = ${ADMIN_USER}
group = ${ADMIN_USER}
listen = ${PHP_FPM_SOCK}
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 20
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 4
pm.max_requests = 500
php_admin_value[memory_limit] = 512M
php_admin_value[upload_max_filesize] = 100M
php_admin_value[post_max_size] = 100M
; Operaciones largas: actualizar sistema, instalar paquetes pesados,
; distribuir claves SSH a un aula entera. 1200s = 20 min.
; Debe ser >= timeout máximo de los scripts en BD (campo Script::timeout)
; y <= 'Timeout' / SetHandler timeout del VirtualHost de Apache.
php_admin_value[max_execution_time] = 1200
request_terminate_timeout = 1200s
POOLCONF

systemctl restart php${PHP_VERSION}-fpm
[ ! -S "$PHP_FPM_SOCK" ] && error "El socket PHP-FPM no se creó en ${PHP_FPM_SOCK}"
success "Pool FPM 'aulasync' corriendo como ${ADMIN_USER} en ${PHP_FPM_SOCK}"

# ── PASO 3: Composer ──────────────────────────────────────────────────────────
title "PASO 3/9 — Composer"
PHP_BIN="/usr/bin/php${PHP_VERSION}"
if [ ! -x "$PHP_BIN" ]; then
    error "No se encuentra ${PHP_BIN}. El paso 2 debe instalar PHP antes que este."
fi
COMPOSER_BIN="/usr/local/bin/composer"

# Si ya hay un composer del sistema (apt install composer) y su versión es >= 2, lo reutilizamos.
if [ ! -f "$COMPOSER_BIN" ] && command -v composer &>/dev/null; then
    SYS_COMPOSER=$(command -v composer)
    SYS_VER=$(timeout 15 "$PHP_BIN" "$SYS_COMPOSER" --version --no-ansi --no-plugins 2>/dev/null | grep -oE 'version [0-9]+' | awk '{print $2}')
    if [ -n "$SYS_VER" ] && [ "$SYS_VER" -ge 2 ]; then
        info "Reutilizando Composer del sistema en ${SYS_COMPOSER} (v${SYS_VER})."
        COMPOSER_BIN="$SYS_COMPOSER"
    fi
fi

if [ ! -f "$COMPOSER_BIN" ]; then
    info "Descargando instalador de Composer…"
    curl -4 --connect-timeout 10 --max-time 60 -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php \
        || error "No se pudo descargar el instalador de Composer. Comprueba red/proxy o instala manualmente con: sudo apt install composer"
    "$PHP_BIN" /tmp/composer-setup.php --quiet --install-dir=/usr/local/bin --filename=composer \
        || error "Falló la instalación de Composer."
    rm -f /tmp/composer-setup.php
    [ ! -f "$COMPOSER_BIN" ] && error "Composer no quedó instalado en ${COMPOSER_BIN}."
fi
chmod +x "$COMPOSER_BIN"
COMPOSER_VER=$(timeout 15 "$PHP_BIN" "$COMPOSER_BIN" --version --no-ansi --no-plugins 2>/dev/null | head -1 || echo 'composer (versión desconocida)')
success "${COMPOSER_VER}"

# ── PASO 4: Node.js 22 ────────────────────────────────────────────────────────
title "PASO 4/9 — Node.js 22"
if ! command -v node &>/dev/null; then
    curl -4 -fsSL https://deb.nodesource.com/setup_22.x | bash - > /dev/null 2>&1
    apt-get install -y -qq nodejs
fi
success "Node.js $(node --version) / npm $(npm --version)"

# ── PASO 5: Configurar MySQL/MariaDB ───────────────────────────────────────
title "PASO 5/9 — Configurar base de datos"

# Escapar comillas simples por si el password contiene alguna (SQL-injection safe).
DB_PASSWORD_ESC=$(printf '%s' "$DB_PASSWORD" | sed "s/'/''/g")

# Detectar motor: MariaDB usa el binario 'mariadb' (el alias 'mysql' está
# deprecado desde MariaDB 10.5) y NO soporta 'IDENTIFIED WITH mysql_native_password'.
# MySQL sí usa esa sintaxis y la necesita para que PDO de PHP no falle con
# caching_sha2_password.
DB_CLIENT="mysql"
command -v mariadb &>/dev/null && DB_CLIENT="mariadb"

IS_MARIADB=false
if "$DB_CLIENT" --version 2>/dev/null | grep -qi mariadb; then
    IS_MARIADB=true
    info "Motor detectado: MariaDB (cliente: ${DB_CLIENT})"
else
    info "Motor detectado: MySQL (cliente: ${DB_CLIENT})"
fi

# Usar fichero de opciones temporal con permisos 600 para el password de root.
# Evita problemas con caracteres especiales (=, !, $, etc.) en la línea de comandos
# y además elimina el warning "Using a password on the command line is insecure".
MYSQL_CNF=$(mktemp)
chmod 600 "$MYSQL_CNF"
cat > "$MYSQL_CNF" <<CNF
[client]
user=${DB_ADMIN_USER}
password="${DB_ADMIN_PASSWORD}"
CNF

# Sintaxis de creación condicional al motor:
#   MySQL:   IDENTIFIED WITH mysql_native_password BY '…'
#            (necesario para que PDO no falle con caching_sha2_password en MySQL 8)
#   MariaDB: IDENTIFIED BY '…'
#            (ya usa mysql_native_password por defecto; la cláusula 'WITH' no es válida)
# En ambos casos crear el usuario para 'localhost' (socket) y '127.0.0.1' (TCP),
# que ambos motores tratan como hosts distintos.
if [ "$IS_MARIADB" = "true" ]; then
    AUTH_CLAUSE="IDENTIFIED BY '${DB_PASSWORD_ESC}'"
else
    AUTH_CLAUSE="IDENTIFIED WITH mysql_native_password BY '${DB_PASSWORD_ESC}'"
fi

"$DB_CLIENT" --defaults-file="$MYSQL_CNF" <<SQL
CREATE DATABASE IF NOT EXISTS \`${DB_DATABASE}\`
    CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
DROP USER IF EXISTS '${DB_USER}'@'localhost';
DROP USER IF EXISTS '${DB_USER}'@'127.0.0.1';
CREATE USER '${DB_USER}'@'localhost'   ${AUTH_CLAUSE};
CREATE USER '${DB_USER}'@'127.0.0.1'   ${AUTH_CLAUSE};
GRANT ALL PRIVILEGES ON \`${DB_DATABASE}\`.* TO '${DB_USER}'@'localhost';
GRANT ALL PRIVILEGES ON \`${DB_DATABASE}\`.* TO '${DB_USER}'@'127.0.0.1';
FLUSH PRIVILEGES;
SQL
MYSQL_RC=$?
rm -f "$MYSQL_CNF"
[ $MYSQL_RC -ne 0 ] && error "Falló la configuración de la base de datos. Revisa usuario/password de admin."
success "Esquema '${DB_DATABASE}' y usuario '${DB_USER}' configurados (motor: $([ "$IS_MARIADB" = "true" ] && echo MariaDB || echo MySQL))"

# ── PASO 6: Configurar Apache con PHP-FPM ────────────────────────────────────
title "PASO 6/9 — Configurar Apache + PHP-FPM"
a2enmod proxy_fcgi setenvif rewrite headers proxy proxy_http proxy_wstunnel > /dev/null
a2enconf php${PHP_VERSION}-fpm             > /dev/null

# Vhost inicial: solo HTTP con la app servida.
# Sirve para dos escenarios:
#   - USE_HTTPS=false: queda como configuración final.
#   - USE_HTTPS=true: necesario para que certbot resuelva el reto ACME en el
#     puerto 80 antes de emitir el certificado. Se reescribe después.
cat > /etc/apache2/sites-available/aulasync.conf <<APACHECONF
<VirtualHost *:80>
    ServerName ${SERVER_NAME}
    DocumentRoot ${APP_DIR}/public

    # Operaciones largas (actualizar sistema, instalar paquetes pesados sobre
    # un aula entera) tardan minutos. 1200s = 20 min, alineado con el
    # 'request_terminate_timeout' del pool PHP-FPM 'aulasync'.
    Timeout 1200

    <FilesMatch "\.php\$">
        SetHandler "proxy:unix:${PHP_FPM_SOCK}|fcgi://localhost?disablereuse=on&connectiontimeout=10&timeout=1200"
    </FilesMatch>

    <Directory ${APP_DIR}/public>
        AllowOverride All
        Require all granted
        Options -Indexes +FollowSymLinks
    </Directory>

    # ttyd: terminal SSH en navegador (puertos 7681+ por aula)
    # El iframe carga /terminal/<puerto>/ → Apache reenvía a localhost:<puerto>
    RewriteEngine On
    RewriteCond %{HTTP:Upgrade} websocket [NC]
    RewriteCond %{HTTP:Connection} upgrade [NC]
    RewriteRule ^/terminal/([0-9]+)/(.*)\$ ws://127.0.0.1:\$1/\$2 [P,L]

    # timeout=1200 mantiene viva la conexión WebSocket SSH durante sesiones largas
    ProxyPassMatch "^/terminal/([0-9]+)/(.*)" "http://127.0.0.1:\$1/\$2" timeout=1200
    ProxyPassReverse "/" "http://127.0.0.1:10000/"

    ErrorLog  \${APACHE_LOG_DIR}/aulasync_error.log
    CustomLog \${APACHE_LOG_DIR}/aulasync_access.log combined
</VirtualHost>
APACHECONF

a2ensite aulasync.conf > /dev/null
systemctl restart apache2
success "VirtualHost '${SERVER_NAME}' activado con PHP ${PHP_VERSION}-FPM"

# ── PASO 6b: HTTPS + reverse proxy ttyd ───────────────────────────────────────
if [ "$USE_HTTPS" = "true" ]; then
    title "PASO 6b — HTTPS con Let's Encrypt + reverse proxy ttyd"
    info "Ejecutando certbot certonly para ${SERVER_NAME}…"
    info "(usa --webroot: emite el certificado sin tocar la configuración de Apache)"

    if certbot certonly --webroot -w "${APP_DIR}/public" \
         -d "${SERVER_NAME}" --non-interactive --agree-tos \
         --register-unsafely-without-email 2>&1 | tail -5; then
        success "Certificado SSL obtenido para ${SERVER_NAME}"
    else
        warn "certbot falló. Comprueba que el DNS de ${SERVER_NAME} apunta a este servidor"
        warn "y que el puerto 80 está accesible desde Internet (necesario para el reto ACME)."
        warn "Puedes ejecutarlo manualmente después con: sudo certbot certonly --apache -d ${SERVER_NAME}"
    fi

    # Reescribir el vhost a la estructura final si el certificado se ha emitido:
    #   *:80  → redirect 301 a HTTPS
    #   *:443 → app HTTPS con reverse proxy de ttyd embebido
    CERT_FILE="/etc/letsencrypt/live/${SERVER_NAME}/fullchain.pem"
    if [ -f "$CERT_FILE" ]; then
        a2enmod ssl > /dev/null
        cat > /etc/apache2/sites-available/aulasync.conf <<APACHECONF
<VirtualHost *:80>
    ServerName ${SERVER_NAME}
    # Timeout heredado del bloque HTTPS; en realidad un redirect tarda ms,
    # pero lo dejamos por coherencia con el resto del vhost.
    Timeout 1200
    Redirect permanent / https://${SERVER_NAME}/
    ErrorLog  \${APACHE_LOG_DIR}/aulasync_error.log
    CustomLog \${APACHE_LOG_DIR}/aulasync_access.log combined
</VirtualHost>

<IfModule mod_ssl.c>
<VirtualHost *:443>
    ServerName ${SERVER_NAME}
    DocumentRoot ${APP_DIR}/public

    # Operaciones largas (actualizar sistema, instalar paquetes pesados sobre
    # un aula entera) tardan minutos. 1200s = 20 min, alineado con el
    # 'request_terminate_timeout' del pool PHP-FPM 'aulasync'.
    # NOTA: para mod_proxy_fcgi con SetHandler, el timeout efectivo es el del
    # 'Timeout' del VHost más el 'timeout=' del SetHandler; 'ProxyTimeout' a
    # nivel de VHost no es válido en este contexto (módulo proxy lo rechaza
    # si no hay <Proxy> envolviendo).
    Timeout 1200

    SSLEngine on
    SSLCertificateFile    /etc/letsencrypt/live/${SERVER_NAME}/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/${SERVER_NAME}/privkey.pem
    Include /etc/letsencrypt/options-ssl-apache.conf

    <FilesMatch "\.php\$">
        SetHandler "proxy:unix:${PHP_FPM_SOCK}|fcgi://localhost?disablereuse=on&connectiontimeout=10&timeout=1200"
    </FilesMatch>

    <Directory ${APP_DIR}/public>
        AllowOverride All
        Require all granted
        Options -Indexes +FollowSymLinks
    </Directory>

    # ── Reverse proxy para ttyd (terminal SSH interactiva) ─────────────
    # El iframe carga /terminal/<puerto>/ → Apache reenvía a localhost:<puerto>
    RewriteEngine On
    RewriteCond %{HTTP:Upgrade} websocket [NC]
    RewriteCond %{HTTP:Connection} upgrade [NC]
    RewriteRule ^/terminal/([0-9]+)/(.*)\$ ws://127.0.0.1:\$1/\$2 [P,L]

    # ttyd: timeout=1200 mantiene viva la conexión WebSocket SSH durante
    # sesiones largas; sin esto se corta en 60s (default de mod_proxy).
    ProxyPassMatch "^/terminal/([0-9]+)/(.*)" "http://127.0.0.1:\$1/\$2" timeout=1200
    ProxyPassReverse "/" "http://127.0.0.1:10000/"

    ErrorLog  \${APACHE_LOG_DIR}/aulasync_error.log
    CustomLog \${APACHE_LOG_DIR}/aulasync_access.log combined
</VirtualHost>
</IfModule>
APACHECONF

        systemctl reload apache2
        success "VirtualHost HTTPS activado con redirect 301 en :80 y reverse proxy ttyd"
    else
        warn "No se encontró ${CERT_FILE}. Se mantiene el vhost HTTP-only."
        warn "Reintenta el certificado manualmente con: sudo certbot certonly --apache -d ${SERVER_NAME}"
    fi
else
    info "HTTPS omitido. La terminal SSH se accederá directamente por HTTP en los puertos 10000–10999."
    info "Si activas HTTPS más adelante, necesitarás configurar el reverse proxy de ttyd."
fi

# ── PASO 7: Repositorio AulaSync ────────────────────────────────────────────
title "PASO 7/9 — Repositorio AulaSync"
git config --global user.name  "$ADMIN_USER"
git config --global user.email "$ADMIN_EMAIL"

if [ ! -d "${APP_DIR}/.git" ]; then
    # El repo no existe — clonar
    REPO_URL="https://${GITHUB_TOKEN}@${GITHUB_REPO#https://}"
    mkdir -p "$(dirname "$APP_DIR")"
    git clone --branch "$GITHUB_BRANCH" "$REPO_URL" "$APP_DIR"
    success "Repositorio clonado en ${APP_DIR}"
else
    info "Repositorio ya existe. Omitiendo clone."
fi

# Aplicar permisos base al directorio del proyecto
chown -R "${ADMIN_USER}:www-data" "$APP_DIR"
chmod g+s "$APP_DIR"
success "Permisos base aplicados: ${ADMIN_USER}:www-data + setgid"

# ── PASO 8: Configurar Laravel ────────────────────────────────────────────────
title "PASO 8/9 — Configurar Laravel"
cd "$APP_DIR"
PHP_BIN="/usr/bin/php${PHP_VERSION}"

# Forzar IPv4 en Composer para evitar cuelgues por IPv6 mal enrutado.
export COMPOSER_DISABLE_NETWORK=0
export COMPOSER_ALLOW_SUPERUSER=1

info "Instalando dependencias PHP con Composer (puede tardar varios minutos)…"
timeout 600 "$PHP_BIN" -d memory_limit=-1 "$COMPOSER_BIN" install \
    --no-dev --optimize-autoloader --no-interaction --no-plugins --prefer-dist \
    || error "Composer install falló o superó el timeout de 10 minutos."

info "Compilando assets con npm (puede tardar varios minutos)…"
timeout 300 npm install --silent || error "npm install falló o superó el timeout de 5 minutos."
timeout 300 npm run build --silent || error "npm run build falló o superó el timeout de 5 minutos."

[ ! -f .env ] && cp .env.example .env
APP_KEY=$($PHP_BIN artisan key:generate --show)

cat > .env <<ENVEOF
APP_NAME=AulaSync
APP_ENV=production
APP_KEY=${APP_KEY}
APP_DEBUG=false
APP_URL=${APP_PROTOCOL}://${SERVER_NAME}

APP_LOCALE=es
APP_FALLBACK_LOCALE=es
APP_FAKER_LOCALE=es_ES
APP_TIMEZONE=Europe/Madrid

APP_MAINTENANCE_DRIVER=file

BCRYPT_ROUNDS=12

LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=error

# ── Base de datos ─────────────────────────────────────────────────────────────
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=${DB_DATABASE}
DB_USERNAME=${DB_USER}
DB_PASSWORD=${DB_PASSWORD}

SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null

BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database
CACHE_STORE=database

# ── Correo ────────────────────────────────────────────────────────────────────
MAIL_MAILER=log
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_FROM_ADDRESS="${MAIL_FROM}"
MAIL_FROM_NAME="AulaSync"

VITE_APP_NAME="AulaSync"

# ── Ansible ───────────────────────────────────────────────────────────────────
ANSIBLE_SERVER_HOST=127.0.0.1
ANSIBLE_SERVER_USER=${ADMIN_USER}
ANSIBLE_SERVER_PASSWORD=${ADMIN_PASSWORD}
ANSIBLE_KEY_FILE=${ANSIBLE_DIR}/aulasync_ed25519
ANSIBLE_USER=${VM_USER}
ANSIBLE_INVENTARIO_PATH=${ANSIBLE_DIR}/inventario.yml
ANSIBLE_SCRIPTS_DIR=${ANSIBLE_DIR}/scripts
ANSIBLE_PLAYBOOK_BIN=/usr/bin/ansible-playbook

# ── AulaSync ──────────────────────────────────────────────────────────────────
AULASYNC_EXECUTION_MODE=local
AULASYNC_WOL_BROADCAST_MASK=255.255.0.0
AULASYNC_SSH_KEY_PATH=${ANSIBLE_DIR}/aulasync_ed25519
AULASYNC_VM_USER=${VM_USER}
AULASYNC_VM_PASSWORD=${VM_PASSWORD}
AULASYNC_DEPLOY_USER=${VM_USER}
AULASYNC_DEPLOY_KEY_PATH=
AULASYNC_DHCP_CONF_PATH=${DHCP_CONF_PATH}
AULASYNC_GATEWAY_HOST=${GATEWAY_IP}
AULASYNC_GATEWAY_IS_LOCAL=${GATEWAY_IS_LOCAL}
AULASYNC_GATEWAY_TYPE=${GATEWAY_TYPE}
AULASYNC_GATEWAY_USER=${GATEWAY_USER}
AULASYNC_GATEWAY_PASSWORD=${GATEWAY_PASSWORD}

# ── Terminal SSH interactiva (ttyd) ──────────────────────────────────────────
# El navegador del cliente accede a http://<host>:<puerto>/ → ttyd corre en este servidor.
AULASYNC_TERMINAL_HOST=${SERVER_NAME}
AULASYNC_TERMINAL_PORT_MIN=10000
AULASYNC_TERMINAL_PORT_MAX=10999
AULASYNC_TERMINAL_TTYD_BIN=/usr/bin/ttyd

# ── Setup temporal (se elimina tras aulasync:setup) ───────────────────────────
AULASYNC_SETUP_NAME=${ADMIN_USER}
AULASYNC_SETUP_EMAIL=${ADMIN_EMAIL}
AULASYNC_SETUP_PASSWORD=${ADMIN_PASSWORD}
ENVEOF

$PHP_BIN artisan migrate --force
$PHP_BIN artisan db:seed --force
$PHP_BIN artisan aulasync:setup
$PHP_BIN artisan aulasync:inventario
$PHP_BIN artisan config:cache
$PHP_BIN artisan route:cache
$PHP_BIN artisan view:cache

success "Laravel configurado"

# ── PASO 9: Permisos y Ansible ────────────────────────────────────────────────
title "PASO 9/9 — Permisos y Ansible"

# Permisos
id -nG "$ADMIN_USER" | grep -qw www-data || usermod -aG www-data "$ADMIN_USER"
chown -R "${ADMIN_USER}:www-data" "$APP_DIR"
# Excluir node_modules y .git del chmod masivo:
#   - node_modules: sus binarios necesitan conservar el bit +x que npm les asigna;
#     un chmod 644 indiscriminado lo eliminaría y rompería 'npm run build'.
#   - .git: objetos del repo no deben tocarse.
find "$APP_DIR" -not -path "*/node_modules/*" -not -path "*/.git/*" -type f -exec chmod 644 {} \;
find "$APP_DIR" -not -path "*/node_modules/*" -not -path "*/.git/*" -type d -exec chmod 755 {} \;
chown -R www-data:www-data "${APP_DIR}/storage" "${APP_DIR}/bootstrap/cache"
chmod -R 775 "${APP_DIR}/storage" "${APP_DIR}/bootstrap/cache"
success "Permisos: código → ${ADMIN_USER}:www-data | storage → www-data"

# Ansible — instalación global (sistema) desde PPA oficial
# El PPA de Ansible proporciona ansible-core >= 2.16, necesario para que los
# módulos funcionen en equipos con Python 3.12+ (Debian 13 Trixie y superiores).
# El paquete 'ansible' de los repos por defecto de Ubuntu 22.04 trae ansible-core
# 2.10, demasiado viejo: falla con 'No module named ansible.module_utils.six.moves'.
#
# El binario queda en /usr/bin/ansible-playbook, disponible en todos los contextos:
#   - PHP-FPM (como ${ADMIN_USER})
#   - Cron del scheduler (como root)
#   - Terminal interactiva
if ! add-apt-repository --list 2>/dev/null | grep -q 'ansible/ansible'; then
    add-apt-repository --yes --update ppa:ansible/ansible > /dev/null 2>&1
    # Eliminar el paquete ansible de los repos por defecto si existe (2.10.x),
    # porque conflicta con ansible-core del PPA al compartir /usr/bin/ansible.
    apt-get remove -y -qq ansible 2>/dev/null || true
    apt-get --fix-broken install -y -qq 2>/dev/null || true
fi
apt-get install -y -qq ansible
success "Ansible $(ansible --version 2>/dev/null | head -1 || echo '(versión desconocida)') instalado a nivel global"

# Instalar la colección ansible.posix (necesaria para el módulo authorized_key
# que usa distribuir-clave.yml). Se instala como root para que esté disponible
# en todos los contextos de ejecución.
info "Instalando colección ansible.posix…"
if ansible-galaxy collection install ansible.posix --force 2>&1 | tail -5; then
    success "Colección ansible.posix instalada"
else
    error "No se pudo instalar ansible.posix. Comprueba conectividad y vuelve a lanzar manualmente: ansible-galaxy collection install ansible.posix"
fi

mkdir -p "${ANSIBLE_DIR}/scripts"
chown -R "${ADMIN_USER}:${ADMIN_USER}" "$ANSIBLE_DIR"
chmod 755 "$ANSIBLE_DIR"

cat > "${ANSIBLE_DIR}/ansible.cfg" <<ANSIBLECFG
[defaults]
host_key_checking   = False
remote_user         = ${VM_USER}
interpreter_python  = /usr/bin/python3
forks               = 50
timeout             = 3

[ssh_connection]
# ServerAliveInterval=10 + ServerAliveCountMax=3 detectan conexiones SSH zombi
# en 30s. Sin esto, una sesión establecida que deja de responder bloquea
# check-status indefinidamente y deja todo el aula en estado 'pending'.
ssh_args = -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=3 -o ServerAliveInterval=10 -o ServerAliveCountMax=3
ANSIBLECFG

chown "${ADMIN_USER}:${ADMIN_USER}" "${ANSIBLE_DIR}/ansible.cfg"
success "Ansible instalado y configurado en ${ANSIBLE_DIR}"

# Generar par de claves SSH de AulaSync (si no existen ya).
# Se generan aquí para que el inventario pueda referenciarlas desde el inicio.
# La distribución a los equipos se hace después desde el dashboard.
SSH_KEY_PATH="${ANSIBLE_DIR}/aulasync_ed25519"
if [ ! -f "$SSH_KEY_PATH" ]; then
    sudo -u "$ADMIN_USER" ssh-keygen -t ed25519 -f "$SSH_KEY_PATH" -N '' -C 'aulasync@apolo' 2>&1
    chown "${ADMIN_USER}:${ADMIN_USER}" "${SSH_KEY_PATH}" "${SSH_KEY_PATH}.pub"
    chmod 600 "$SSH_KEY_PATH"
    chmod 644 "${SSH_KEY_PATH}.pub"
    success "Par de claves SSH generado en ${SSH_KEY_PATH}"
else
    info "Claves SSH ya existentes en ${SSH_KEY_PATH} — se reutilizan."
fi

# Copiar los ficheros estáticos del repo (check-status.sh, ping.yml, playbooks
# de distribución de clave, y scripts de iptables) al directorio de Ansible.
# Estos ficheros son necesarios para que funcionen:
#   - 'Verificar Estado' en el dashboard (check-status.sh)
#   - 'Distribuir Clave SSH' (distribuir-clave.yml)
#   - Control de internet (scripts/internet-control.sh)
# El resto de scripts (apagar, WOL, etc.) los sube sincronizarScripts() desde el seeder.
ANSIBLE_ASSETS="${APP_DIR}/ansible"
if [ -d "$ANSIBLE_ASSETS" ]; then
    # Ficheros base (no sobrescribir ansible.cfg que acabamos de generar con los valores del .env)
    for f in check-status.sh ping.yml adhoc-cmd.yml distribuir-clave.yml; do
        if [ -f "${ANSIBLE_ASSETS}/${f}" ]; then
            cp "${ANSIBLE_ASSETS}/${f}" "${ANSIBLE_DIR}/${f}"
            chown "${ADMIN_USER}:${ADMIN_USER}" "${ANSIBLE_DIR}/${f}"
        fi
    done
    # check-status.sh debe ser ejecutable
    chmod +x "${ANSIBLE_DIR}/check-status.sh" 2>/dev/null || true

    # Scripts auxiliares (los del seeder se suben con sincronizarScripts, pero internet-control.sh
    # es estático y no está en el seeder porque no se gestiona desde la UI)
    if [ -d "${ANSIBLE_ASSETS}/scripts" ]; then
        for f in "${ANSIBLE_ASSETS}/scripts"/*.sh; do
            [ -f "$f" ] || continue
            bn=$(basename "$f")
            cp "$f" "${ANSIBLE_DIR}/scripts/${bn}"
            chown "${ADMIN_USER}:${ADMIN_USER}" "${ANSIBLE_DIR}/scripts/${bn}"
            chmod +x "${ANSIBLE_DIR}/scripts/${bn}"
        done
    fi
    success "Ficheros estáticos de Ansible copiados desde ${ANSIBLE_ASSETS}"
else
    warn "No se encontró ${ANSIBLE_ASSETS} en el repo — algunos scripts auxiliares podrían faltar."
fi

# Configurar sudoers para iptables.
# Caso A (gateway local): crear /etc/sudoers.d/aulasync-iptables aquí mismo.
# Caso B (gateway externo): conectar por SSH al gateway con sshpass y crear allí
#                           el sudoers. Requiere que GATEWAY_USER pueda hacer sudo
#                           al menos una vez con password (el propio GATEWAY_PASSWORD).
if [ "$GATEWAY_IS_LOCAL" = "true" ]; then
    SUDOERS_FILE="/etc/sudoers.d/aulasync-iptables"
    echo "${ADMIN_USER} ALL=(ALL) NOPASSWD: /usr/sbin/iptables, /sbin/iptables" > "$SUDOERS_FILE"
    chmod 0440 "$SUDOERS_FILE"
    if ! visudo -cf "$SUDOERS_FILE" >/dev/null 2>&1; then
        rm -f "$SUDOERS_FILE"
        warn "No se pudo configurar sudoers para iptables; hazlo manualmente."
    else
        success "sudoers NOPASSWD para iptables configurado en ${SUDOERS_FILE}"
    fi
else
    if [ "$GATEWAY_TYPE" = "pfsense" ]; then
        info "Gateway pfSense detectado — no se configura sudoers (pfSense usa pfctl, no iptables)."
        info "Ver los pasos finales para configurar la tabla <aulasync_blocked> en pfSense."
    else
        info "Configurando sudoers NOPASSWD para iptables en el gateway ${GATEWAY_IP} vía SSH…"
        # Usamos sshpass (ya instalado en el paso 1) + un heredoc que ejecuta como
        # root en el gateway. El comando completo se envía por stdin a 'sudo -S -p "" bash'
        # para que sudo lea el password del stdin sin prompt visible.
        REMOTE_CMD="echo '${GATEWAY_USER} ALL=(ALL) NOPASSWD: /usr/sbin/iptables, /sbin/iptables' > /etc/sudoers.d/aulasync-iptables && chmod 0440 /etc/sudoers.d/aulasync-iptables && visudo -cf /etc/sudoers.d/aulasync-iptables"
        if sshpass -p "$GATEWAY_PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=10 \
                "${GATEWAY_USER}@${GATEWAY_IP}" \
                "echo '${GATEWAY_PASSWORD}' | sudo -S -p '' bash -c \"${REMOTE_CMD}\"" >/dev/null 2>&1; then
            success "sudoers NOPASSWD para iptables configurado en el gateway ${GATEWAY_IP}"
        else
            warn "No se pudo configurar sudoers en el gateway automáticamente."
            warn "Conexión SSH o permisos sudo insuficientes. Hazlo manualmente (ver pasos finales)."
        fi
    fi
fi

# Cron para Laravel Scheduler (actualización de estado de equipos cada minuto)
# Ejecuta dos tareas:
#   - aulasync:check-status   → cada minuto (on/off)
#   - aulasync:check-extended → diariamente a las 07:00 (SSH + WOL + futuras)
CRON_LINE="* * * * * cd ${APP_DIR} && php artisan schedule:run >> /dev/null 2>&1"
if crontab -l 2>/dev/null | grep -qF "artisan schedule:run"; then
    warn "El cron de Laravel Scheduler ya estaba configurado en root, no se modifica."
else
    (crontab -l 2>/dev/null; echo "${CRON_LINE}") | crontab -
    success "Cron configurado en root: schedule:run cada minuto"
fi

# ── Resumen final ─────────────────────────────────────────────────────────────
echo ""
echo -e "${GREEN}══════════════════════════════════════════════${NC}"
echo -e "${GREEN}   AulaSync instalado correctamente           ${NC}"
echo -e "${GREEN}══════════════════════════════════════════════${NC}"
echo ""
echo -e "  URL:    ${BLUE}${APP_PROTOCOL}://${SERVER_NAME}${NC}"
echo -e "  Login:  ${BLUE}${ADMIN_EMAIL}${NC}"
echo -e "  App:    ${BLUE}${APP_DIR}${NC}"
echo -e "  Config: ${BLUE}${APP_DIR}/.env${NC}"
echo ""
echo -e "${YELLOW}PASOS FINALES:${NC}"
echo "  1. Desde el dashboard → Distribuir clave SSH a los equipos (la clave ya fue generada)"
if [ "$GATEWAY_IS_LOCAL" = "false" ]; then
    echo ""
    if [ "$GATEWAY_TYPE" = "pfsense" ]; then
        echo -e "  ${BOLD}2. Configuración requerida en pfSense:${NC}"
        echo "     AulaSync usa pfctl para controlar internet. Configura en pfSense:"
        echo ""
        echo -e "     ${CYAN}a) Firewall > Tables > New:${NC}"
        echo "        Nombre: aulasync_blocked   Marcar: Persistent"
        echo ""
        echo -e "     ${CYAN}b) Firewall > Rules > Floating > New:${NC}"
        echo "        Action: Block | Quick: marcado | Interface: WAN"
        echo "        Direction: out | Source: Table aulasync_blocked | Dest: any"
        echo "        Guardar y Apply Changes."
        echo ""
        echo -e "     ${CYAN}c) Verificar desde Apolo:${NC}"
        echo -e "       ${CYAN}ssh ${GATEWAY_USER}@${GATEWAY_IP}${NC}"
        echo "        (opción 8 si aparece el menú de pfSense)"
        echo -e "       ${CYAN}pfctl -t aulasync_blocked -T show${NC}"
    else
        echo -e "  ${BOLD}2. Verificación del gateway ${GATEWAY_IP}:${NC}"
        echo "     El instalador ha intentado configurar sudoers NOPASSWD en el gateway."
        echo "     Comprueba que funciona desde este servidor:"
        echo ""
        echo -e "       ${CYAN}ssh ${GATEWAY_USER}@${GATEWAY_IP} 'sudo iptables -L FORWARD -n'${NC}"
        echo ""
        echo "     Si te pide password, la configuración automática falló. Entra al"
        echo "     gateway y ejecuta manualmente como root:"
        echo ""
        echo -e "       ${CYAN}echo '${GATEWAY_USER} ALL=(ALL) NOPASSWD: /usr/sbin/iptables, /sbin/iptables' \\${NC}"
        echo -e "       ${CYAN}     | sudo tee /etc/sudoers.d/aulasync-iptables${NC}"
        echo -e "       ${CYAN}sudo chmod 0440 /etc/sudoers.d/aulasync-iptables${NC}"
    fi
    echo ""
    echo "  3. Si el dhcpd.conf está en otro equipo, asegúrate de que "
    echo "     ${DHCP_CONF_PATH} es accesible desde este servidor"
    echo "     (o sincronízalo periódicamente)."
fi
echo ""
warn "Guarda en un gestor de contraseñas las credenciales del .env antes de cerrar esta sesión."
echo ""