#!/usr/bin/env bash
# Instalador de AgentiCo One para una VM Linux (probado para Ubuntu 24.04):
# descarga el binario desde la carpeta publicada, comprueba su suma y lo deja
# funcionando como servicio de systemd, con agentico-host para la sección Cloud.
# Después, agentico-host instala solo los componentes base (nginx, certbot, Go,
# git, SQLite, Node.js…) y, con --dominio, publica la web con HTTPS; este
# script muestra el avance. Guía completa: docs/SERVIDOR_LINUX.md.
#
# Uso, en la VM:
#
#   curl -fsSL https://SERVIDOR/pub/instalar.sh | sudo bash -s -- --url https://SERVIDOR/pub/ --admin tu@correo.cl
#
# Si ya hay una instalación en la carpeta, solo reemplaza el binario (guarda el
# anterior como agentico-one.anterior) y reinicia los servicios. Nunca borra ni
# reemplaza la configuración ni los datos.

# Todo va dentro de funciones y la última línea llama a main: si la descarga
# del script se corta a la mitad, bash no ejecuta un trozo suelto.

set -euo pipefail

usage() {
	cat <<'EOF'
Instala o actualiza AgentiCo One como servicio de systemd. Después,
agentico-host instala los componentes base: nginx, certbot, Go (oficial, en
/usr/local/go), git, SQLite, Node.js y npm, cron, ufw (sin activarlo) y utilidades.

  curl -fsSL https://SERVIDOR/pub/instalar.sh | sudo bash -s -- --url https://SERVIDOR/pub/ --admin tu@correo.cl

  --url URL         carpeta publicada con agentico-one, su .sha256 y los archivos
                    de instalación (o la variable AGENTICO_URL)
  --admin CORREO    quien administra: en una instalación nueva se crea users.json
                    con ese correo y perfil root (o la variable AGENTICO_ADMIN)
  --dominio NOMBRE  publica la web en ese dominio con nginx y pide el certificado
                    HTTPS (el dominio ya debe apuntar a esta VM)
  --sin-https       con --dominio, deja el sitio solo en HTTP
  --correo-cert C   correo para Let's Encrypt (por defecto el de --admin)
  --docker          instala también Docker (para la sección Docker)
  --sin-componentes no instala los componentes base
  --no-esperar      no espera a que terminen de instalarse los componentes
  --dir RUTA        carpeta del programa (por defecto /opt/agentico)
  --workspace RUTA  carpeta de trabajo (por defecto <dir>/workspace)
  --args "..."      opciones del binario para el servicio (por defecto -webapp)
  --hub URL         conecta este equipo como nodo del hub (por ejemplo https://hub.tu-dominio.cl)
  --hub-codigo C    código de alta que da el hub (sección Nodos → Agregar nodo)
  --hub-nombre N    nombre del nodo en el hub (por defecto, el del equipo)
  --es-hub          este equipo es el hub: concentra a los demás (sección Nodos)
  --sin-cloud       no instala agentico-host (sin Cloud ni componentes base)
  --no-iniciar      instala los servicios pero no los arranca
  --forzar          reinstala el binario aunque ya tenga la misma versión

Si --dir ya tiene una instalación, solo actualiza el binario y reinicia.
EOF
}

# Donde agentico-host lee la solicitud de componentes y deja su estado.
HOST_DATA=/var/lib/agentico-host
PROVISION_REQUEST="$HOST_DATA/aprovisionar.json"
PROVISION_STATE="$HOST_DATA/aprovisionamiento.json"

fail() { echo "Error: $*" >&2; exit 1; }
step() { printf '\n==> %s\n' "$*"; }

# base_url deja la carpeta publicada terminada en /. Acepta también la URL del
# binario o del propio instalador.
base_url() {
	local url="$1"
	url="${url%%\?*}"
	url="${url%/}"
	case "$url" in
	*/agentico-one | */instalar.sh | */agentico-one.sha256 | */agentico-one.version) url="${url%/*}" ;;
	esac
	printf '%s/' "$url"
}

valid_url() { [[ "$1" =~ ^(https?|file)://[^[:space:]\"\\]+$ ]]; }
valid_email() { [[ "$1" =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ]]; }
valid_domain() { [[ "$1" =~ ^([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}$ ]]; }

# download baja un archivo de la carpeta publicada a $2.
download() {
	local name="$1" dest="$2" progress="-sS"
	[ "$name" = "agentico-one" ] && [ -t 2 ] && progress="--progress-bar"
	curl -fL $progress --retry 3 --connect-timeout 20 -o "$dest/$name" "$BASE$name" ||
		fail "no se pudo descargar $BASE$name"
}

# download_optional baja un archivo que puede no estar publicado.
download_optional() {
	curl -fsSL --retry 2 --connect-timeout 20 -o "$2/$1" "$BASE$1" 2>/dev/null || rm -f "$2/$1"
}

# verify_binary comprueba la suma del binario descargado contra su .sha256.
verify_binary() {
	local dir="$1" expected actual
	expected="$(awk 'NR==1 {print tolower($1)}' "$dir/agentico-one.sha256")"
	[[ "$expected" =~ ^[0-9a-f]{64}$ ]] || fail "agentico-one.sha256 no tiene una suma válida."
	actual="$(sha256sum "$dir/agentico-one" | awk '{print $1}')"
	[ "$expected" = "$actual" ] || fail "la suma del binario descargado ($actual) no coincide con agentico-one.sha256 ($expected). No se instaló nada."
}

# version_of lee la versión (1.0.0.N) de un binario o de un .version.
version_of() {
	local text=""
	case "$1" in
	*.version) text="$(cat "$1" 2>/dev/null || true)" ;;
	*) [ -x "$1" ] && text="$("$1" -version 2>/dev/null || true)" ;;
	esac
	[[ "$text" =~ v?([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+) ]] && printf '%s' "${BASH_REMATCH[1]}"
	return 0
}

# write_users deja users.json con solo el administrador, con perfil root.
# Conserva dueño y permisos del archivo si ya existe.
write_users() {
	local file="$1" email="$2" name
	name="${email%%@*}"
	name="$(printf '%s' "$name" | tr -c 'A-Za-z0-9._-' '_')"
	printf '[\n  { "username": "%s", "email": "%s", "root": true }\n]\n' "$name" "$email" >"$file.instalar"
	cat "$file.instalar" >"$file"
	rm -f "$file.instalar"
}

# add_update_url agrega {"update": {"url": ...}} al comienzo de setup.json, si
# aún no tiene la sección. Solo entiende el formato que deja el binario
# (un "{" solo en la primera línea); con otro, avisa y no toca el archivo.
add_update_url() {
	local file="$1" url="$2"
	if grep -q '"update"[[:space:]]*:' "$file"; then
		echo "setup.json ya tiene la sección \"update\": no se cambia."
		return 0
	fi
	if [ "$(head -n 1 "$file" | tr -d '[:space:]')" != "{" ] || [ "$(tr -d '[:space:]' <"$file")" = "{}" ]; then
		echo "Aviso: no se pudo agregar la URL de actualización a $file; agrégala en Configuración → Instalación."
		return 0
	fi
	awk -v line="  \"update\": { \"url\": \"$url\" }," 'NR == 1 { print; print line; next } { print }' "$file" >"$file.instalar"
	cat "$file.instalar" >"$file"
	rm -f "$file.instalar"
	echo "URL de actualización: $url"
}

# write_provision_request deja la solicitud de componentes para agentico-host.
write_provision_request() {
	local docker="$1" domain="$2" email="$3" json
	json="{\"docker\": $([ "$docker" -eq 1 ] && echo true || echo false)"
	[ -n "$domain" ] && json="$json, \"domain\": \"$domain\""
	[ -n "$email" ] && json="$json, \"email\": \"$email\""
	json="$json}"
	install -d -m 0700 "$HOST_DATA"
	printf '%s\n' "$json" >"$PROVISION_REQUEST.instalar"
	chmod 0600 "$PROVISION_REQUEST.instalar"
	mv -f "$PROVISION_REQUEST.instalar" "$PROVISION_REQUEST"
	echo "Componentes base solicitados a agentico-host: $json"
}

# Variables propias del servicio web (las lee systemd con EnvironmentFile).
AGENTICO_ENV=/etc/agentico/agentico.env

# set_env deja KEY="valor" en agentico.env, reemplazando una línea anterior
# de la misma clave. Responde 0 si cambió algo.
set_env() {
	local key="$1" value="$2" line
	line="$key=\"$value\""
	install -d -m 0755 "$(dirname "$AGENTICO_ENV")"
	[ -f "$AGENTICO_ENV" ] || install -m 0600 /dev/null "$AGENTICO_ENV"
	if grep -qxF "$line" "$AGENTICO_ENV"; then
		return 1
	fi
	{ grep -v "^$key=" "$AGENTICO_ENV" || true; printf '%s\n' "$line"; } >"$AGENTICO_ENV.instalar"
	cat "$AGENTICO_ENV.instalar" >"$AGENTICO_ENV"
	rm -f "$AGENTICO_ENV.instalar"
	return 0
}

# configure_hub deja en agentico.env el papel de este equipo en el hub.
# Responde 0 si cambió algo (hay que reiniciar la web).
configure_hub() {
	local is_hub="$1" url="$2" code="$3" name="$4" changed=1
	if [ "$is_hub" -eq 1 ]; then
		set_env AGENTICO_HUB 1 && changed=0
		echo "Este equipo es el hub: la sección Nodos concentra a los demás AgentiCo One."
	fi
	if [ -n "$url" ]; then
		set_env AGENTICO_HUB_URL "$url" && changed=0
		[ -n "$code" ] && { set_env AGENTICO_HUB_CODE "$code" && changed=0; }
		[ -n "$name" ] && { set_env AGENTICO_HUB_NAME "$name" && changed=0; }
		echo "Nodo del hub $url${name:+ (como $name)}."
	fi
	return $changed
}

# hub_report muestra lo último que la web registró sobre su conexión al hub.
hub_report() {
	sleep 3
	journalctl -u agentico -n 80 --no-pager 2>/dev/null | sed -n 's/.*\(Hub: .*\)$/  \1/p' | tail -n 3 || true
}

# state_field lee un campo de texto del estado que deja agentico-host (JSON
# con un campo por línea; no hace falta jq, que aún no está instalado).
state_field() {
	[ -f "$PROVISION_STATE" ] || return 0
	sed -n "s/^  \"$1\": \"\\(.*\\)\",\\{0,1\\}\$/\\1/p" "$PROVISION_STATE" | head -n 1 | sed 's/\\"/"/g'
}

# wait_provision muestra el avance de los componentes hasta que terminan.
wait_provision() {
	local before="$1" limit=$((45 * 60)) waited=0 last="" started status current
	while [ "$waited" -lt "$limit" ]; do
		started="$(state_field started)"
		status="$(state_field status)"
		if [ -n "$started" ] && [ "$started" != "$before" ]; then
			current="$(state_field step)"
			if [ -n "$current" ] && [ "$current" != "$last" ]; then
				echo "  · $current…"
				last="$current"
			fi
			[ "$status" != "running" ] && break
		elif [ "$waited" -ge 60 ] && [ -f "$PROVISION_REQUEST" ]; then
			echo "Aviso: agentico-host no empezó a instalar los componentes (¿versión anterior a la que los instala?)."
			echo "       Revisa: journalctl -u agentico-host -n 50"
			return 1
		fi
		sleep 3
		waited=$((waited + 3))
	done
	case "$status" in
	succeeded) echo "Componentes base instalados." ;;
	failed)
		echo "Aviso: la instalación de componentes falló: $(state_field error)"
		echo "       Se reintenta al reiniciar agentico-host, o desde Cloud → Resumen → Componentes base."
		return 1
		;;
	*)
		echo "Aviso: los componentes siguen instalándose; mira el avance en Cloud → Resumen."
		return 1
		;;
	esac
	if grep -q '"warnings"' "$PROVISION_STATE"; then
		sed -n '/"warnings"/,/\]/p' "$PROVISION_STATE" | sed -n 's/^ *"\(.*\)",\{0,1\}$/  Aviso: \1/p'
	fi
	return 0
}

# wait_active espera a que el servicio quede activo y siga así unos segundos
# (para no dar por bueno uno que se reinicia en bucle).
wait_active() {
	local unit="$1" i
	for i in $(seq 1 30); do
		if systemctl is-active --quiet "$unit"; then
			sleep 4
			systemctl is-active --quiet "$unit" && return 0
		fi
		sleep 1
	done
	return 1
}

show_listen() {
	local pid
	pid="$(systemctl show -p MainPID --value agentico 2>/dev/null || true)"
	[ -n "$pid" ] && [ "$pid" != "0" ] || return 0
	command -v ss >/dev/null 2>&1 || return 0
	ss -ltnpH 2>/dev/null | awk -v p="pid=$pid," 'index($0, p) { print "  escucha en " $4 }' || true
}

start_services() {
	local cloud="$1"
	if [ "$cloud" -eq 1 ]; then
		systemctl enable --now agentico-host
		wait_active agentico-host || {
			journalctl -u agentico-host -n 30 --no-pager || true
			fail "agentico-host no quedó activo (registro arriba)."
		}
		echo "agentico-host activo."
	fi
	systemctl enable --now agentico
	wait_active agentico || {
		journalctl -u agentico -n 40 --no-pager || true
		fail "agentico no quedó activo (registro arriba)."
	}
	echo "agentico activo."
	show_listen
}

restart_services() {
	if [ -f /etc/systemd/system/agentico-host.service ]; then
		systemctl restart agentico-host
		wait_active agentico-host || fail "agentico-host no quedó activo; revisa: journalctl -u agentico-host -n 50"
		echo "agentico-host reiniciado."
	fi
	systemctl restart agentico
	wait_active agentico || fail "agentico no quedó activo; revisa: journalctl -u agentico -n 50. El binario anterior está en agentico-one.anterior."
	echo "agentico reiniciado."
	show_listen
}

main() {
	local url="${AGENTICO_URL:-}" admin="${AGENTICO_ADMIN:-}" dir="/opt/agentico" workspace="" args=""
	local cloud=1 start=1 force=0 components=1 wait=1 docker=0 https=1 domain="" cert_email=""
	local hub_url="" hub_code="" hub_name="" is_hub=0
	while [ $# -gt 0 ]; do
		case "$1" in
		--url) url="${2:-}"; shift 2 ;;
		--admin) admin="${2:-}"; shift 2 ;;
		--dir) dir="${2:-}"; shift 2 ;;
		--workspace) workspace="${2:-}"; shift 2 ;;
		--args) args="${2:-}"; shift 2 ;;
		--dominio) domain="${2:-}"; shift 2 ;;
		--sin-https) https=0; shift ;;
		--correo-cert) cert_email="${2:-}"; shift 2 ;;
		--docker) docker=1; shift ;;
		--sin-componentes | --sin-paquetes) components=0; shift ;;
		--no-esperar) wait=0; shift ;;
		--sin-cloud) cloud=0; shift ;;
		--no-iniciar) start=0; shift ;;
		--forzar) force=1; shift ;;
		--hub) hub_url="${2:-}"; shift 2 ;;
		--hub-codigo) hub_code="${2:-}"; shift 2 ;;
		--hub-nombre) hub_name="${2:-}"; shift 2 ;;
		--es-hub) is_hub=1; shift ;;
		-h | --help) usage; exit 0 ;;
		*) fail "opción desconocida: $1 (usa --help)" ;;
		esac
	done

	[ "$(id -u)" -eq 0 ] || fail "ejecuta el instalador con sudo (… | sudo bash -s -- …)."
	[ -n "$url" ] || fail "indica --url con la carpeta publicada (la que tiene agentico-one)."
	BASE="$(base_url "$url")"
	valid_url "$BASE" || fail "la URL no es válida: $url"
	[ -z "$admin" ] || valid_email "$admin" || fail "el correo de --admin no es válido: $admin"
	cert_email="${cert_email:-$admin}"
	if [ -n "$domain" ]; then
		domain="${domain,,}"
		valid_domain "$domain" || fail "el dominio no es válido: $domain (sin http:// ni rutas)"
		if [ "$https" -eq 1 ] && [ -z "$cert_email" ]; then
			fail "para el certificado HTTPS indica --admin o --correo-cert (o usa --sin-https)."
		fi
		[ -z "$cert_email" ] || valid_email "$cert_email" || fail "el correo de --correo-cert no es válido: $cert_email"
	fi
	[ "$https" -eq 1 ] || cert_email=""
	if [ -n "$hub_url" ]; then
		hub_url="${hub_url%/}"
		[[ "$hub_url" =~ ^https://[A-Za-z0-9.-]+(:[0-9]+)?$ || "$hub_url" =~ ^http://(localhost|127\.0\.0\.1)(:[0-9]+)?$ ]] ||
			fail "--hub debe ser la dirección HTTPS del hub, sin ruta (por ejemplo https://hub.tu-dominio.cl)."
		[ -z "$hub_code" ] || [[ "$hub_code" =~ ^[0-9a-f]{32}$ ]] || fail "el código de alta no es válido (son 32 caracteres hexadecimales)."
		[ -z "$hub_name" ] || [[ "$hub_name" =~ ^[A-Za-z0-9][A-Za-z0-9\ ._-]{0,62}$ ]] || fail "el nombre del nodo admite letras, números, espacios, punto, guion y guion bajo."
		[ "$is_hub" -eq 0 ] || fail "usa --es-hub o --hub, no ambos."
	elif [ -n "$hub_code" ] || [ -n "$hub_name" ]; then
		fail "--hub-codigo y --hub-nombre van con --hub."
	fi
	if [ "$cloud" -eq 0 ] && [ "$components" -eq 1 ]; then
		echo "Aviso: sin agentico-host (--sin-cloud) no se instalan los componentes base."
		components=0
	fi
	[ "$(uname -s)" = "Linux" ] || fail "este instalador es para Linux."
	case "$(uname -m)" in x86_64 | amd64) ;; *) fail "el binario publicado es para x86_64 y este equipo es $(uname -m)." ;; esac
	for cmd in curl sha256sum systemctl install awk sed; do
		command -v "$cmd" >/dev/null 2>&1 || fail "falta el programa $cmd (sudo apt-get install -y $cmd)."
	done
	[ -d /run/systemd/system ] || fail "este equipo no arrancó con systemd."
	case "$dir" in /*) ;; *) fail "--dir debe ser una ruta absoluta." ;; esac
	dir="${dir%/}"

	# Global (no local): el trap de salida corre cuando main ya terminó.
	TMP_DIR="$(mktemp -d)"
	trap 'rm -rf "${TMP_DIR:-}"' EXIT
	local tmp="$TMP_DIR"

	step "Descargando AgentiCo One desde $BASE"
	download agentico-one.sha256 "$tmp"
	download_optional agentico-one.version "$tmp"
	download agentico-one "$tmp"
	verify_binary "$tmp"
	chmod 0755 "$tmp/agentico-one"
	local new_version
	new_version="$(version_of "$tmp/agentico-one")"
	[ -n "$new_version" ] || new_version="$(version_of "$tmp/agentico-one.version")"
	echo "Suma correcta. Versión descargada: ${new_version:-desconocida}"

	# Componentes base: se piden si nunca se instalaron o si ahora se piden
	# Docker o un dominio. agentico-host los instala al arrancar; repetirlos no
	# cambia lo que ya está.
	local request=0 state_before=""
	if [ "$components" -eq 1 ] && { [ ! -f "$PROVISION_REQUEST.hecho" ] || [ "$docker" -eq 1 ] || [ -n "$domain" ]; }; then
		request=1
		state_before="$(state_field started)"
	fi

	local bin="$dir/agentico-one" unit=/etc/systemd/system/agentico.service
	if [ -x "$bin" ] && [ -f "$unit" ]; then
		# Actualización: solo el binario; configuración y datos quedan igual.
		local current
		current="$(version_of "$bin")"
		step "Actualizando la instalación de $dir (${current:-versión desconocida} → ${new_version:-?})"
		[ "$request" -eq 1 ] && write_provision_request "$docker" "$domain" "$cert_email"
		local hub_changed=1
		configure_hub "$is_hub" "$hub_url" "$hub_code" "$hub_name" && hub_changed=0
		if [ -n "$current" ] && [ "$current" = "$new_version" ] && [ "$force" -eq 0 ]; then
			echo "Ya tiene la versión $current: el binario no se cambia (usa --forzar para reinstalarla)."
			if [ "$request" -eq 1 ]; then
				# agentico-host lee la solicitud al arrancar.
				systemctl restart agentico-host
				wait_active agentico-host || fail "agentico-host no quedó activo; revisa: journalctl -u agentico-host -n 50"
			fi
			if [ "$hub_changed" -eq 0 ]; then
				# La web lee las variables del hub al arrancar.
				systemctl restart agentico
				wait_active agentico || fail "agentico no quedó activo; revisa: journalctl -u agentico -n 50"
				echo "agentico reiniciado."
			fi
		else
			local group owner mode
			group="$(stat -c %G "$bin")" owner="$(stat -c %U "$bin")" mode="$(stat -c %a "$bin")"
			cp -p "$bin" "$bin.anterior"
			install -o "$owner" -g "$group" -m "$mode" "$tmp/agentico-one" "$bin.nuevo"
			mv -f "$bin.nuevo" "$bin"
			echo "Binario reemplazado; el anterior quedó en $bin.anterior."
			restart_services
		fi
	else
		# Instalación nueva (o binario suelto sin servicio).
		step "Instalando AgentiCo One en $dir"
		for name in instalar-servicio.sh agentico.service agentico-host.service; do
			download "$name" "$tmp"
		done
		local had_setup=0 had_users=0
		[ -f "$dir/setup.json" ] && had_setup=1
		[ -f "$dir/users.json" ] && had_users=1
		install -d -m 0755 "$dir"
		if [ -e "$bin" ]; then
			cp -p "$bin" "$bin.anterior"
			echo "Había un binario sin servicio: quedó en $bin.anterior."
		fi
		install -m 0755 "$tmp/agentico-one" "$bin.nuevo"
		mv -f "$bin.nuevo" "$bin"

		local service_args=(--dir "$dir")
		[ -n "$workspace" ] && service_args+=(--workspace "$workspace")
		[ -n "$args" ] && service_args+=(--args "$args")
		[ "$cloud" -eq 0 ] && service_args+=(--sin-cloud)
		bash "$tmp/instalar-servicio.sh" "${service_args[@]}" >"$tmp/servicio.log" 2>&1 || {
			cat "$tmp/servicio.log" >&2
			fail "no se pudo instalar el servicio (detalle arriba)."
		}
		# Del resumen de instalar-servicio.sh basta lo anterior a "Siguientes pasos":
		# este instalador hace esos pasos.
		sed '/^Siguientes pasos:/,$d' "$tmp/servicio.log"

		step "Configuración"
		if [ "$had_users" -eq 0 ] && [ -f "$dir/users.json" ]; then
			if [ -n "$admin" ]; then
				write_users "$dir/users.json" "$admin"
				echo "users.json: entra $admin, con perfil root."
			else
				echo "Aviso: users.json tiene la lista de acceso de la distribución. Agrega tu correo con \"root\": true:"
				echo "  sudo nano $dir/users.json && sudo systemctl restart agentico"
			fi
		elif [ -n "$admin" ]; then
			echo "users.json ya existía: no se cambia (no se agregó $admin)."
		fi
		if [ "$had_setup" -eq 0 ] && [ -f "$dir/setup.json" ]; then
			case "$BASE" in
			http://* | https://*) add_update_url "$dir/setup.json" "${BASE}agentico-one" ;;
			esac
		fi
		[ "$request" -eq 1 ] && write_provision_request "$docker" "$domain" "$cert_email"
		configure_hub "$is_hub" "$hub_url" "$hub_code" "$hub_name" || true

		if [ "$start" -eq 0 ]; then
			step "Servicios instalados, sin arrancar"
			if [ "$cloud" -eq 1 ]; then
				echo "  sudo systemctl enable --now agentico-host agentico"
				[ "$request" -eq 1 ] && echo "  (al arrancar, agentico-host instala los componentes base)"
			else
				echo "  sudo systemctl enable --now agentico"
			fi
			exit 0
		fi
		step "Arrancando los servicios"
		# Un agentico-one lanzado a mano (por ejemplo, como root) ocupa el puerto.
		if command -v pgrep >/dev/null 2>&1 && pgrep -x agentico-one >/dev/null 2>&1; then
			echo "Aviso: ya hay un agentico-one corriendo fuera del servicio (pgrep -a agentico-one);"
			echo "       si ocupa el puerto, detenlo y ejecuta: sudo systemctl restart agentico"
		fi
		start_services "$cloud"
	fi

	if [ "$request" -eq 1 ]; then
		if [ "$wait" -eq 1 ]; then
			step "agentico-host está instalando los componentes base (unos minutos)"
			wait_provision "$state_before" || true
		else
			step "agentico-host instala los componentes base en segundo plano"
			echo "  Avance: Cloud → Resumen → Componentes base, o journalctl -u agentico-host -f"
		fi
	fi

	if [ -n "$hub_url" ] && [ "$start" -eq 1 ]; then
		step "Conexión con el hub $hub_url"
		hub_report
		echo "  El nodo aparece en la sección Nodos del hub cuando se conecta."
	fi
	step "Listo: AgentiCo One $(version_of "$bin") en $dir"
	local address
	address="$(state_field address)"
	if [ -n "$address" ]; then
		echo "  Entra en:      $address"
	elif [ -n "$domain" ]; then
		echo "  Dominio:       $domain (se publica al terminar los componentes base)"
	else
		echo "  Para publicar la web en un dominio, vuelve a ejecutar el instalador con --dominio,"
		echo "  o hazlo desde Cloud → Resumen → Componentes base."
	fi
	cat <<EOF
  Configuración: $dir/setup.json y $dir/users.json (o desde la web: Configuración → Instalación).
                 Revisa ahí el dominio público y los destinatarios del aviso de arranque.
  Registro:      journalctl -u agentico -f
  Actualizar:    desde la web, o vuelve a ejecutar este instalador.
EOF
}

if [ -z "${AGENTICO_INSTALAR_SOLO_FUNCIONES:-}" ]; then
	main "$@"
fi
