#!/usr/bin/env bash
# Installe & configure Zabbix agent (classique) selon OS + version choisie.
# - Propose 6.4 / 7.0 / 7.2 / 7.4 (défaut 7.4)
# - 7.4 -> binaire statique 7.4.5
# - 7.2 -> binaire statique 7.2.14
# - 7.0 -> binaire statique 7.0.21
# - 6.4 -> via dépôt officiel (apt/yum/zypper)
# - Configure uniquement Server / ServerActive / Hostname (PAS de AllowKey/UnsafeUserParameters ici)
set -euo pipefail

# -------- Helpers
die(){ echo "ERROR: $*" >&2; exit 1; }
msg(){ echo "[*] $*"; }
ok(){  echo "[✓] $*"; }

scan_network_for_zabbix(){
  msg "Recherche d'un serveur Zabbix sur le réseau (scan simplifié /24)..."
  local interfaces line ip_cidr addr own_ip prefix target i
  interfaces=$(ip -o -4 addr show scope global | grep -v '127.0.0.1')

  # respect env vars if set
  local timeout_val=${SCAN_TIMEOUT:-1}
  local cap_val=${SCAN_CAP:-4096}
  local use_nmap=${USE_NMAP_IF_PRESENT:-false}

  while IFS= read -r line; do
    ip_cidr=$(echo "$line" | awk '{print $4}')
    addr=${ip_cidr%%/*}
    # Validate IPv4
    if [[ ! "$addr" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
      continue
    fi
    own_ip="$addr"
    prefix=${addr%.*}

    msg "Scan du /24 ${prefix}.0/24 (hôtes 1..254)..."

    # If allowed and nmap present, use it to speed up scan
    if [[ "${use_nmap}" == "true" && $(command -v nmap >/dev/null 2>&1; echo $?) -eq 0 ]]; then
      msg "nmap détecté et usage demandé — utilisation de nmap pour accélérer le scan"
      # Grab hosts with port 10051 open
      mapfile -t nmhosts < <(nmap -n -p10051 --open -oG - "${prefix}.0/24" 2>/dev/null | awk '/Ports: .*10051\/open/ {print $2}')
      local cnt=0
      for target in "${nmhosts[@]:-}"; do
        [[ "$target" == "$own_ip" ]] && continue
        echo "$target"
        return 0
        ((cnt++)) || true
        if (( cnt>=cap_val )); then
          msg "Limite de scan atteinte (${cap_val})"
          break
        fi
      done
    else
      local scanned=0
      for i in {1..254}; do
        target="${prefix}.${i}"
        [[ "$target" == "$own_ip" ]] && continue
        # test TCP rapide via /dev/tcp
        if timeout "$timeout_val" bash -c ">/dev/tcp/${target}/10051" >/dev/null 2>&1; then
          msg "Serveur Zabbix potentiel trouvé: $target"
          if command -v nc >/dev/null 2>&1; then
            if nc -z -w 2 "$target" 10051 >/dev/null 2>&1; then
              ok "Serveur Zabbix trouvé: $target"
              echo "$target"
              return 0
            fi
          else
            ok "Serveur Zabbix trouvé: $target"
            echo "$target"
            return 0
          fi
        fi
        ((scanned++))
        if (( scanned>=cap_val )); then
          msg "Limite de scan atteinte (${cap_val}), arrêt du scan sur cette interface"
          break
        fi
      done
    fi
  done <<< "$interfaces"

  return 1
}

require_root(){
  if [[ ${EUID:-$(id -u)} -ne 0 ]]; then
    die "Exécute ce script en root (sudo $0)."
  fi
}

detect_os(){
  [[ -r /etc/os-release ]] || die "/etc/os-release introuvable"
  # shellcheck disable=SC1091
  . /etc/os-release
  OS_ID="$(echo "${ID}" | tr '[:upper:]' '[:lower:]')"
  OS_LIKE="$(echo "${ID_LIKE:-$ID}" | tr '[:upper:]' '[:lower:]')"
  CODENAME="${VERSION_CODENAME:-}"
  VERSION_ID_SHORT="${VERSION_ID%%.*}"

  if [[ "$OS_ID" =~ (debian|ubuntu) ]] || echo "$OS_LIKE" | grep -Eq "(debian|ubuntu)"; then
    PM="apt"
    [[ -n "$CODENAME" ]] || CODENAME="$(command -v lsb_release >/dev/null 2>&1 && lsb_release -sc || true)"
    [[ -n "$CODENAME" ]] || die "Impossible de déterminer le codename (Debian/Ubuntu)."
  elif echo "$OS_ID" | grep -Eq "(rhel|centos|rocky|almalinux|ol)"; then
    PM="$(command -v dnf >/dev/null 2>&1 && echo dnf || echo yum)"
    RHEL_MAJOR="${VERSION_ID_SHORT}"
  elif echo "$OS_ID" | grep -Eq "(opensuse|sles)"; then
    PM="zypper"
  else
    die "OS non supporté (seulement Debian/Ubuntu, RHEL-like 8/9, openSUSE). ID=$OS_ID"
  fi
  ok "OS détecté: ${PRETTY_NAME:-$OS_ID} (PM=$PM, codename=${CODENAME:-N/A})"
}

check_connectivity(){
  local server="$1"
  msg "Vérification de la connectivité avec le serveur Zabbix ($server)..."
  if ! ping -c 1 "$server" >/dev/null 2>&1; then
    die "Impossible de joindre le serveur Zabbix ($server). Vérifiez la connectivité réseau."
  fi
  if ! nc -z -w 5 "$server" 10051 >/dev/null 2>&1; then
    die "Le port Zabbix (10051) semble inaccessible sur $server. Vérifiez le pare-feu."
  fi
  ok "Connectivité avec le serveur Zabbix OK"
}

ensure_prereqs(){
  case "$PM" in
    apt)
      export DEBIAN_FRONTEND=noninteractive
      apt-get update -y || true
      apt-get install -y ca-certificates curl wget gnupg lsb-release tar netcat-openbsd iproute2
      ;;
    dnf) dnf install -y curl wget ca-certificates tar nc iproute ;;
    yum) yum install -y curl wget ca-certificates tar nc iproute ;;
    zypper) zypper --non-interactive refresh; zypper --non-interactive install -y curl wget ca-certificates tar netcat iproute2 ;;
  esac
}

ensure_user(){
  if ! id zabbix >/dev/null 2>&1; then
    msg "Création de l’utilisateur système 'zabbix'..."
    useradd -r -M -s /usr/sbin/nologin -d /var/lib/zabbix zabbix
  fi
  ok "User 'zabbix' OK"
}

write_agent_conf(){
  local cfg="/etc/zabbix/zabbix_agentd.conf" srv="$1" host="$2"
  mkdir -p /etc/zabbix 2>/dev/null

  # Backup existing config if present
  if [[ -f "$cfg" ]]; then
    local backup="${cfg}.bak.$(date +%Y%m%d-%H%M%S)"
    cp -a "$cfg" "$backup" 2>/dev/null
    ok "Sauvegarde de la configuration existante: $backup"
  fi

  # Prépare la configuration minimale dans une variable
  read -r -d '' conf_content << EOF
# Configuration Zabbix Agent
# Généré le $(date +%Y-%m-%d) à $(date +%H:%M:%S)
PidFile=/var/run/zabbix/zabbix_agentd.pid
LogFile=/var/log/zabbix/zabbix_agentd.log
Include=/etc/zabbix/zabbix_agentd.d/*.conf

# Configuration du serveur
Server=$srv
ServerActive=$srv
Hostname=$host

# Paramètres systèmes (gérés par systemd)
# User=zabbix
EOF

  # Écrit la configuration en une seule fois
  if echo "$conf_content" > "$cfg"; then
    chmod 644 "$cfg" 2>/dev/null
    ok "Configuration agent écrite dans $cfg"
    ok " - Server/ServerActive: $srv"
    ok " - Hostname: $host"
  else
    die "Erreur lors de l'écriture de la configuration dans $cfg"
  fi
}

install_from_repo(){
  local zver="$1"
  msg "Installation via dépôt Zabbix ${zver} …"

  case "$PM" in
    apt)
      # keyring + source.list
      local keyring="/usr/share/keyrings/zabbix.gpg"
      echo "[*] Ajout clé + dépôt APT"
      curl -fsSL https://repo.zabbix.com/zabbix-official-repo.key | gpg --dearmor > "$keyring"
      chmod 0644 "$keyring"
      echo "deb [signed-by=${keyring}] https://repo.zabbix.com/zabbix/${zver}/debian ${CODENAME} main" >/etc/apt/sources.list.d/zabbix.list
      apt-get update -y
      apt-get install -y zabbix-agent zabbix-agent2 zabbix-agent2-*
      ;;

    dnf|yum)
      # Crée un repo .repo directement (évite rpm zabbix-release versionné)
      local repofile="/etc/yum.repos.d/zabbix.repo"
      cat >"$repofile" <<EOF
[zabbix]
name=Zabbix Official Repository - $zver
baseurl=https://repo.zabbix.com/zabbix/${zver}/rhel/${RHEL_MAJOR}/\$basearch/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://repo.zabbix.com/zabbix-official-repo.key
EOF
      $PM makecache -y
      $PM install -y zabbix-agent zabbix-agent2 zabbix-agent2-*
      ;;

    zypper)
      zypper --non-interactive addrepo --refresh "https://repo.zabbix.com/zabbix/${zver}/sles/${VERSION_ID_SHORT}/x86_64/" "Zabbix-${zver}" || true
      rpm --import https://repo.zabbix.com/zabbix-official-repo.key || true
      zypper --non-interactive refresh
      zypper --non-interactive install -y zabbix-agent zabbix-agent2 zabbix-agent2-*
      ;;
  esac

  systemctl daemon-reload || true
  systemctl enable --now zabbix-agent
  ok "zabbix-agent installé via dépôt ${zver}"
}

install_static_agent(){
  # Installe un binaire statique (agentd) + unit systemd
  local url="$1" ver_exact="$2"
  local tmpdir
  tmpdir="$(mktemp -d)"
  msg "Téléchargement du binaire statique ${ver_exact}…"
  (cd "$tmpdir" && curl -fSL "$url" -o agent.tar.gz)
  (cd "$tmpdir" && tar -xzf agent.tar.gz)

  # Cherche le binaire zabbix_agentd dans l’archive
  local binpath
  binpath="$(find "$tmpdir" -type f -name 'zabbix_agentd' | head -n1)" || true
  [[ -x "$binpath" ]] || die "Binaire zabbix_agentd introuvable dans l’archive"

  install -D -m 0755 -o root -g root "$binpath" /usr/sbin/zabbix_agentd
  install -d -m 0755 -o zabbix -g zabbix /var/run/zabbix /var/log/zabbix
  install -d -m 0755 -o root -g root /etc/zabbix /etc/zabbix/zabbix_agentd.d

  # Unit systemd minimal
  cat >/etc/systemd/system/zabbix-agent.service <<'EOF'
[Unit]
Description=Zabbix Agent (static)
After=network.target

[Service]
Type=forking
User=zabbix
Group=zabbix
PIDFile=/var/run/zabbix/zabbix_agentd.pid
ExecStart=/usr/sbin/zabbix_agentd -c /etc/zabbix/zabbix_agentd.conf
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RuntimeDirectory=zabbix
RuntimeDirectoryMode=0755

[Install]
WantedBy=multi-user.target
EOF

  systemctl daemon-reload
  ok "Binaire statique installé (${ver_exact})"
}

test_agent(){
  local server="$1" wait_time=30
  msg "Test de la communication de l'agent..."
  systemctl is-active --quiet zabbix-agent || die "Le service zabbix-agent n'est pas actif"
  
  msg "Attente de l'établissement de la connexion (${wait_time}s)..."
  sleep "$wait_time"
  
  if ! grep -q "sending heartbeat message" /var/log/zabbix/zabbix_agentd.log; then
    die "Aucun heartbeat détecté dans les logs. Vérifiez la configuration et les pare-feux"
  fi
  
  local pid
  pid=$(cat /var/run/zabbix/zabbix_agentd.pid 2>/dev/null) || die "PID file introuvable"
  if ! kill -0 "$pid" 2>/dev/null; then
    die "Le processus zabbix_agentd (PID $pid) ne répond pas"
  fi
  
  ok "Tests de l'agent OK"
}

restart_agent(){
  systemctl enable --now zabbix-agent
  systemctl restart zabbix-agent
  systemctl is-active --quiet zabbix-agent || die "Le service zabbix-agent n'a pas démarré."
  ok "Service zabbix-agent actif"
}

# -------- Paramètres par défaut
DEFAULT_SERVER="172.0.16.245"
DEFAULT_VERSION="7.4"
LOGFILE="/var/log/zabbix_agent_install.log"

# -------- Début
{
require_root
detect_os
ensure_prereqs
ensure_user

# CLI parsing / variables and detection
SCAN_TIMEOUT=${SCAN_TIMEOUT:-1}
SCAN_CAP=${SCAN_CAP:-4096}
USE_NMAP_IF_PRESENT=${USE_NMAP_IF_PRESENT:-false}

show_help(){
  cat <<'EOF'
Usage: agent-zabbix.sh [OPTIONS]

Options:
  --server <IP|FQDN>    : Non-interactive — utilise l'adresse fournie comme serveur Zabbix
  --dry-run             : Lance un scan et affiche les résultats, sans modifier la configuration
  --yes                 : Accepte automatiquement les confirmations (useful for CI)
  --use-nmap-if-present : Si nmap est installé, l'utiliser pour accélérer le scan (ne l'installe pas)
  --scan-timeout <s>    : Timeout par hôte pour le test TCP (env var SCAN_TIMEOUT aussi accepté)
  --scan-cap <n>        : Nombre max d'hôtes à tester par interface (env var SCAN_CAP)
  --help                : Affiche cette aide

Behavior:
  - Si --server est fourni, le script est non-interactif et utilisera cette adresse.
  - Sans --server, le script propose le choix interactif (manuel / scan / dry-run).
  - Si un agent Zabbix est déjà installé :
      * si version >= 7.4 : le script propose de reconfigurer Server/Hostname sans réinstaller
      * si version < 7.4 : propose une mise à jour vers 7.4 (statique)

EOF
}

# CLI args
CLI_SERVER=""
CLI_DRYRUN=false
CLI_YES=false
while [[ $# -gt 0 ]]; do
  case "$1" in
    --server)
      CLI_SERVER="$2"; shift 2 ;;
    --dry-run)
      CLI_DRYRUN=true; shift ;;
    --yes)
      CLI_YES=true; shift ;;
    --use-nmap-if-present)
      USE_NMAP_IF_PRESENT=true; shift ;;
    --scan-timeout)
      SCAN_TIMEOUT="$2"; shift 2 ;;
    --scan-cap)
      SCAN_CAP="$2"; shift 2 ;;
    --help)
      show_help; exit 0 ;;
    *)
      echo "Unknown option: $1"; show_help; exit 1 ;;
  esac
done

get_installed_zabbix_version(){
  if command -v zabbix_agentd >/dev/null 2>&1; then
    zabbix_agentd -V 2>&1 | head -n1 | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?' || true
  elif [[ -f /usr/sbin/zabbix_agentd ]]; then
    /usr/sbin/zabbix_agentd -V 2>&1 | head -n1 | grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)?' || true
  else
    echo ""
  fi
}

version_ge_7_4(){
  local ver="$1"
  local maj=${ver%%.*}
  local rest=${ver#*.}
  local min=${rest%%.*}
  maj=${maj:-0}
  min=${min:-0}
  if (( maj > 7 )); then
    return 0
  elif (( maj == 7 && min >= 4 )); then
    return 0
  fi
  return 1
}

INST_VER=$(get_installed_zabbix_version)
DO_INSTALL=1
if [[ -n "$INST_VER" ]]; then
  msg "Agent Zabbix détecté: $INST_VER"
  if version_ge_7_4 "$INST_VER"; then
    msg "Version installée >= 7.4 — pas d'upgrade requis"
    DO_INSTALL=0
    if [[ "$CLI_YES" != "true" ]]; then
      read -r -p "Voulez-vous reconfigurer seulement le Server/Hostname (o/N) ? " anscfg
      if [[ "${anscfg,,}" != "o" && "${anscfg,,}" != "y" ]]; then
        msg "Aucune modification demandée. Sortie."
        exit 0
      fi
    fi
  else
    if [[ "$CLI_YES" == "true" ]]; then
      upg="y"
    else
      read -r -p "Version installée (${INST_VER}) < 7.4. Voulez-vous mettre à jour vers 7.4 (O/n) ? " upg
    fi
    if [[ "${upg,,}" == "o" || "${upg,,}" == "y" || -z "$upg" ]]; then
      msg "Préparation de la mise à jour vers 7.4..."
      ZBX_VER="7.4"; MODE="static"; STATIC_URL="https://cdn.zabbix.com/zabbix/binaries/stable/7.4/7.4.5/zabbix_agent-7.4.5-linux-3.0-amd64-static.tar.gz"; EXACT="7.4.5"
      install_static_agent "$STATIC_URL" "$EXACT"
      DO_INSTALL=0
      msg "Mise à jour réalisée. Nous allons reconfigurer l'agent."
    else
      msg "Pas de mise à jour demandée — on continue et on reconfigure l'agent existant."
      DO_INSTALL=0
    fi
  fi
fi

# Choix pour déterminer l'adresse du serveur Zabbix
echo "Comment déterminer l'adresse du serveur Zabbix ?"
echo " 1) Saisir manuellement"
echo " 2) Scanner le réseau (/24)"
echo " 3) Dry-run (scan affichage seulement, aucune modification)"
read -r -p "Choix (1-3) [2] : " SERVER_MODE_CHOICE
SERVER_MODE_CHOICE="${SERVER_MODE_CHOICE:-2}"

if [[ "$SERVER_MODE_CHOICE" == "1" ]]; then
  read -r -p "Adresse IP/FQDN du serveur Zabbix : " ZBX_SERVER
  [[ -z "$ZBX_SERVER" ]] && die "Adresse du serveur requise"
elif [[ "$SERVER_MODE_CHOICE" == "3" ]]; then
  msg "Mode dry-run: scan réseau (aucune modification ne sera faite)"
  scan_network_for_zabbix || msg "Aucun serveur trouvé"
  exit 0
else
  msg "Scan réseau demandé..."
  ZBX_SERVER=$(scan_network_for_zabbix) || true
  if [[ -z "$ZBX_SERVER" ]]; then
    read -r -p "Aucun serveur trouvé. Entrez une adresse manuellement : " ZBX_SERVER
    [[ -z "$ZBX_SERVER" ]] && die "Adresse du serveur requise"
  else
    read -r -p "Utiliser le serveur Zabbix trouvé ($ZBX_SERVER) ? [O/n] : " confirm
    if [[ "${confirm,,}" == "n" ]]; then
      read -r -p "Veuillez entrer l'adresse du serveur Zabbix : " ZBX_SERVER
      [[ -z "$ZBX_SERVER" ]] && die "Adresse du serveur requise"
    fi
  fi
fi

DEF_HOST="$(hostname -f 2>/dev/null || hostname)"
read -r -p "Hostname agent [${DEF_HOST}] : " ZBX_HOST
ZBX_HOST="${ZBX_HOST:-$DEF_HOST}"

if [[ "${DO_INSTALL:-1}" -eq 1 ]]; then
  echo "Choisissez la version de Zabbix Agent :"
  echo "  1) 6.4 (repo)"
  echo "  2) 7.0 (statique 7.0.21)"
  echo "  3) 7.2 (statique 7.2.14)"
  echo "  4) 7.4 (statique 7.4.5)  <-- défaut"
  read -r -p "Numéro (1-4) [4] : " CHOICE
  case "${CHOICE:-4}" in
    1) ZBX_VER="6.4" ; MODE="repo" ;;
    2) ZBX_VER="7.0" ; MODE="static" ; STATIC_URL="https://cdn.zabbix.com/zabbix/binaries/stable/7.0/7.0.21/zabbix_agent-7.0.21-linux-3.0-amd64-static.tar.gz" ; EXACT="7.0.21" ;;
    3) ZBX_VER="7.2" ; MODE="static" ; STATIC_URL="https://cdn.zabbix.com/zabbix/binaries/stable/7.2/7.2.14/zabbix_agent-7.2.14-linux-3.0-amd64-static.tar.gz" ; EXACT="7.2.14" ;;
    4|*) ZBX_VER="7.4" ; MODE="static" ; STATIC_URL="https://cdn.zabbix.com/zabbix/binaries/stable/7.4/7.4.5/zabbix_agent-7.4.5-linux-3.0-amd64-static.tar.gz" ; EXACT="7.4.5" ;;
  esac
else
  # Pas d'installation demandée (agent déjà installé ou déjà mis à jour)
  ZBX_VER="${INST_VER:-$DEFAULT_VERSION}"
  msg "Aucune installation à effectuer (Zabbix agent ${ZBX_VER})"
fi

# Installation (si demandé)
if [[ "${DO_INSTALL:-1}" -eq 1 ]]; then
  # Sanity check arch (amd64) & kernel >= 3.0
  if [[ "${MODE:-repo}" = "static" ]]; then
    ARCH="$(uname -m)"
    if [[ "$ARCH" != "x86_64" && "$ARCH" != "amd64" ]]; then
      msg "Architecture $ARCH non compatible avec le binaire statique fourni → fallback dépôt ${ZBX_VER}"
      MODE="repo"
    fi
  fi

  if [[ "${MODE:-repo}" = "static" ]]; then
    install_static_agent "$STATIC_URL" "$EXACT"
  else
    install_from_repo "$ZBX_VER"
  fi
else
  msg "Installation sautée (agent déjà présent ou mis à jour)."
fi

# Configuration de l'agent (sans AllowKey/UnsafeUserParameters)
write_agent_conf "$ZBX_SERVER" "$ZBX_HOST"

# Démarrage / redémarrage
restart_agent

echo
ok "Installation terminée."
echo "  - Version choisie: ${ZBX_VER} (${MODE})"
[[ "${MODE}" = "static" ]] && echo "  - Binaire exact: ${EXACT}"
echo "  - Zabbix Server: ${ZBX_SERVER}"
echo "  - Hostname     : ${ZBX_HOST}"
echo "  - OS           : ${PRETTY_NAME:-$OS_ID}"

# Test final de l'agent
test_agent "$ZBX_SERVER"

# Fin du logging
} 2>&1 | tee -a "$LOGFILE"

ok "Log d'installation disponible dans $LOGFILE"
