#!/usr/bin/env bash
set -Eeuo pipefail
umask 077

APP_DIR=/opt/infrahub-agent
ENV_FILE=/etc/infrahub-agent.env
SERVICE_FILE=/etc/systemd/system/infrahub-agent.service
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"

usage() {
  cat <<'HELP'
InfraHub Agent: автоматическая установка на Debian/Ubuntu.
Использование: sudo bash install.sh --url https://infra.936439.xyz --id 2 [--services ssh,docker,caddy] [--auto-update]
Токен запрашивается без эха (не включайте его в командную строку).
При повторном запуске конфигурация обновляется, служба перезапускается.
HELP
}
URL='' SERVER_ID='' SERVICES='ssh' INTERVAL='60' DOCKER_INVENTORY=false AUTO_UPDATE=false
while (($#)); do
  case "$1" in
    --url) URL="${2:?}"; shift 2;;
    --id) SERVER_ID="${2:?}"; shift 2;;
    --services) SERVICES="${2:?}"; shift 2;;
    --interval) INTERVAL="${2:?}"; shift 2;;
    --docker-count) DOCKER_INVENTORY=true; shift;;
    --auto-update) AUTO_UPDATE=true; shift;;
    -h|--help) usage; exit 0;;
    *) echo "Неизвестный аргумент: $1" >&2; usage >&2; exit 2;;
  esac
done
(( EUID == 0 )) || { echo 'Нужен root: sudo bash install.sh ...' >&2; exit 1; }
[[ "$URL" =~ ^https://[A-Za-z0-9.-]+(:[0-9]+)?/?$ ]] || { echo 'Требуется HTTPS URL без пути' >&2; exit 2; }
URL="${URL%/}"
[[ "$SERVER_ID" =~ ^[1-9][0-9]*$ ]] || { echo '--id должен быть положительным числом' >&2; exit 2; }
[[ "$INTERVAL" =~ ^[0-9]+$ ]] && (( INTERVAL >= 30 && INTERVAL <= 3600 )) || { echo '--interval: 30..3600' >&2; exit 2; }
[[ "$SERVICES" =~ ^[A-Za-z0-9_.@,-]+$ ]] || { echo 'Неправильный список служб' >&2; exit 2; }
[[ -s "$SCRIPT_DIR/agent.py" && -s "$SCRIPT_DIR/requirements.txt" ]] || { echo 'Рядом с install.sh должны лежать agent.py и requirements.txt' >&2; exit 1; }
command -v systemctl >/dev/null || { echo 'Требуется systemd' >&2; exit 1; }

printf 'Токен агента для сервера #%s (ввод скрыт): ' "$SERVER_ID" >&2
IFS= read -r -s TOKEN </dev/tty || { echo 'Требуется интерактивный терминал' >&2; exit 1; }
printf '\n' >&2
[[ -n "$TOKEN" && "$TOKEN" != *$'\n'* && "$TOKEN" != *$'\r'* ]] || { echo 'Некорректный токен' >&2; exit 2; }

# A broken venv may exist even when `python3 -m venv --help` succeeds.
if ! command -v python3 >/dev/null; then
  apt-get update
  DEBIAN_FRONTEND=noninteractive apt-get install -y python3 ca-certificates
fi
if [[ ! -x "$APP_DIR/.venv/bin/python" ]] || ! "$APP_DIR/.venv/bin/python" -m pip --version >/dev/null 2>&1; then
  apt-get update
  DEBIAN_FRONTEND=noninteractive apt-get install -y python3-venv ca-certificates
  # Install the matching versioned venv package if the distro requires it.
  if ! python3 -m venv --copies --clear /tmp/infrahub-venv-check-$$ >/dev/null 2>&1; then
    VENV_PKG="python$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')-venv"
    DEBIAN_FRONTEND=noninteractive apt-get install -y "$VENV_PKG"
  fi
  rm -rf "/tmp/infrahub-venv-check-$$"
fi
if ! id sp-agent >/dev/null 2>&1; then
  useradd --system --no-create-home --shell /usr/sbin/nologin sp-agent
fi
install -d -o root -g root -m 0755 "$APP_DIR"
install -o root -g root -m 0644 "$SCRIPT_DIR/agent.py" "$APP_DIR/agent.py"
install -o root -g root -m 0644 "$SCRIPT_DIR/VERSION" "$APP_DIR/VERSION"
if [[ ! -x "$APP_DIR/.venv/bin/python" ]] || ! "$APP_DIR/.venv/bin/python" -m pip --version >/dev/null 2>&1; then
  rm -rf "$APP_DIR/.venv"
  python3 -m venv "$APP_DIR/.venv"
fi
"$APP_DIR/.venv/bin/python" -m pip --version >/dev/null || { echo 'pip не работает внутри venv' >&2; exit 1; }
"$APP_DIR/.venv/bin/python" -m pip install --disable-pip-version-check --no-cache-dir -r "$SCRIPT_DIR/requirements.txt"
# pip can inherit restrictive umask=077. The read-only venv must be readable by sp-agent.
chmod -R a+rX "$APP_DIR/.venv"
runuser -u sp-agent -- "$APP_DIR/.venv/bin/python" -c 'import psutil,requests; assert callable(psutil.disk_usage) and callable(requests.post)' || {
  echo 'Библиотеки не загружаются от пользователя sp-agent' >&2; exit 1;
}


# Секрет не попадает ни в shell history, ни в аргументы процессов.
# Не допускаем пробельные символы и спецсимволы, опасные для EnvironmentFile.
[[ "$TOKEN" =~ ^[A-Za-z0-9_.~+/-]+$ ]] || { echo 'Токен содержит неподдерживаемые символы; настройте env вручную' >&2; exit 2; }
TMP_ENV="$(mktemp /etc/.infrahub-agent.env.XXXXXXXX)"
trap 'rm -f "$TMP_ENV"' EXIT
cat > "$TMP_ENV" <<EOF
SERVERPILOT_URL=$URL
SERVERPILOT_SERVER_ID=$SERVER_ID
SERVERPILOT_AGENT_TOKEN=$TOKEN
SERVERPILOT_INTERVAL=$INTERVAL
SERVERPILOT_SERVICES=$SERVICES
SERVERPILOT_DOCKER=false
SERVERPILOT_DOCKER_INVENTORY=$DOCKER_INVENTORY
EOF
chmod 0600 "$TMP_ENV"
chown root:root "$TMP_ENV"
mv -f "$TMP_ENV" "$ENV_FILE"
trap - EXIT
unset TOKEN
cat > "$SERVICE_FILE" <<'EOF'
[Unit]
Description=InfraHub Linux monitoring agent
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
User=sp-agent
Group=sp-agent
EnvironmentFile=/etc/infrahub-agent.env
WorkingDirectory=/opt/infrahub-agent
ExecStart=/opt/infrahub-agent/.venv/bin/python /opt/infrahub-agent/agent.py
Restart=always
RestartSec=15
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictSUIDSGID=true
CapabilityBoundingSet=

[Install]
WantedBy=multi-user.target
EOF
chmod 0644 "$SERVICE_FILE"
if [[ "$DOCKER_INVENTORY" == true ]]; then
  if ! command -v docker >/dev/null 2>&1; then
    echo 'Docker отсутствует, подсчёт контейнеров будет отключён' >&2
    sed -i 's/^SERVERPILOT_DOCKER_INVENTORY=true$/SERVERPILOT_DOCKER_INVENTORY=false/' "$ENV_FILE"
    systemctl disable --now infrahub-docker-inventory.timer 2>/dev/null || true
  else
    install -d -o root -g root -m 0755 /usr/local/lib/infrahub
    install -o root -g root -m 0755 "$SCRIPT_DIR/docker-inventory.py" /usr/local/lib/infrahub/docker-inventory.py
    install -o root -g root -m 0644 "$SCRIPT_DIR/infrahub-docker-inventory.service" /etc/systemd/system/infrahub-docker-inventory.service
    install -o root -g root -m 0644 "$SCRIPT_DIR/infrahub-docker-inventory.timer" /etc/systemd/system/infrahub-docker-inventory.timer
    systemctl daemon-reload
    systemctl enable --now infrahub-docker-inventory.timer
    systemctl start infrahub-docker-inventory.service
  fi
else
  systemctl disable --now infrahub-docker-inventory.timer 2>/dev/null || true
fi
# Auto-updating is opt-in; never implicitly grant network-fetched code execution.
if [[ "$AUTO_UPDATE" == true ]]; then
  install -d -o root -g root -m 0755 /usr/local/lib/infrahub
  install -o root -g root -m 0755 "$SCRIPT_DIR/update-agent.py" /usr/local/lib/infrahub/update-agent.py
  install -o root -g root -m 0644 "$SCRIPT_DIR/infrahub-agent-update.service" /etc/systemd/system/infrahub-agent-update.service
  install -o root -g root -m 0644 "$SCRIPT_DIR/infrahub-agent-update.timer" /etc/systemd/system/infrahub-agent-update.timer
  systemctl daemon-reload
  systemctl enable --now infrahub-agent-update.timer
fi
systemctl daemon-reload
systemctl enable infrahub-agent.service
systemctl restart infrahub-agent.service
sleep 3
if ! systemctl is-active --quiet infrahub-agent.service; then
  journalctl -u infrahub-agent -n 25 --no-pager >&2
  echo 'ОШИБКА: служба агента не запустилась' >&2
  exit 1
fi
printf '\nАгент запущен; проверяйте поступление метрик в InfraHub.\n'
printf 'Журнал: journalctl -u infrahub-agent -n 30 --no-pager\n' 
