#!/usr/bin/env bash
set -euo pipefail

# =========================================================
# HUB INSTALLER (ARCHITECTURE CORE/STATE/CUSTOM + TELEMETRY)
# =========================================================

export DEBIAN_FRONTEND=noninteractive

# ---------------------------------------------------------
# 0. Couleurs / helpers / constantes
# ---------------------------------------------------------

red()    { echo -e "\033[0;31m$*\033[0m" >&2; }
green()  { echo -e "\033[0;32m$*\033[0m"; }
yellow() { echo -e "\033[1;33m$*\033[0m"; }
blue()   { echo -e "\033[0;34m$*\033[0m"; }
NC='\033[0m'

need_root() {
  if [ "${EUID:-$(id -u)}" -ne 0 ]; then
    red "[ERREUR] Ce script doit être exécuté en root (sudo)."
    exit 1
  fi
}

need_cmd() {
  command -v "$1" >/dev/null 2>&1
}

log_step() {
  echo
  blue "=================================================="
  blue "$1"
  blue "=================================================="
}

ensure_dir() {
  local path="$1"
  install -d -m 755 "$path"
}

ensure_file() {
  local path="$1"
  if [ ! -f "$path" ]; then
    : > "$path"
  fi
}

append_if_missing() {
  local file="$1"
  local key="$2"
  local value="$3"
  if ! grep -q "^${key}=" "$file" 2>/dev/null; then
    echo "${key}=${value}" >> "$file"
  fi
}

# ---------------------------------------------------------
# 1. Parsing arguments
# ---------------------------------------------------------

FORCE=0
SHOW_SECRETS=0
SKIP_PROVISION=0
REPAIR=0
UPGRADE=0
HUB_TOKEN=""

usage() {
  cat <<'EOF'
Usage:
  sudo ./install.sh [options]

Options:
  --force             Écrase les artefacts core régénérables
  --token VALUE       Lance le provisioning WG avec ce token
  --skip-provision    N'exécute pas le provisioning WG
  --repair            Répare la structure et les fichiers manquants
  --upgrade           Placeholder pour future logique upgrade
  --show-secrets      Affiche les secrets générés à la fin
  -h, --help          Affiche cette aide
EOF
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --force)
      FORCE=1
      shift
      ;;
    --token)
      [ $# -ge 2 ] || { red "[ERREUR] --token nécessite une valeur."; exit 1; }
      HUB_TOKEN="$2"
      shift 2
      ;;
    --skip-provision)
      SKIP_PROVISION=1
      shift
      ;;
    --repair)
      REPAIR=1
      shift
      ;;
    --upgrade)
      UPGRADE=1
      shift
      ;;
    --show-secrets)
      SHOW_SECRETS=1
      shift
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      red "[ERREUR] Paramètre inconnu: $1"
      usage
      exit 1
      ;;
  esac
done

# ---------------------------------------------------------
# 2. Vérifications préalables des fichiers de livraison
# ---------------------------------------------------------
need_root

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

# Sources livrées avec l'archive
SRC_COMPOSE="${SCRIPT_DIR}/docker-compose.yml"
SRC_UFW="${SCRIPT_DIR}/ufw_apply.sh"
SRC_WG_PROVISION="${SCRIPT_DIR}/wg_provision.sh"
SRC_HUB_STATUS="${SCRIPT_DIR}/hub_status.sh"

for f in "$SRC_COMPOSE" "$SRC_UFW" "$SRC_WG_PROVISION" "$SRC_HUB_STATUS"; do
  [ -s "$f" ] || { red "[ERREUR] Fichier source manquant ou vide dans le dossier d'installation: $f"; exit 1; }
done

# ---------------------------------------------------------
# 3. Préparation du système (Paquets, Réseau, Docker)
# ---------------------------------------------------------
log_step "[1/8] Préparation du système"

echo "Mise à jour des dépôts..."
apt-get update -qq

echo "Installation des paquets Core..."
apt-get install -y -qq docker.io ufw wireguard wireguard-tools samba curl openssl jq iproute2 iputils-ping

# Vérification rigoureuse de la présence des outils critiques
for c in docker curl openssl jq ss wg ping; do
  need_cmd "$c" || { red "[ERREUR] Commande critique manquante après installation : $c"; exit 1; }
done

if ! docker compose version >/dev/null 2>&1; then
    yellow "  -> 'docker compose' (V2) absent, installation du paquet dédié..."
    apt-get install -y -qq docker-compose
fi

if ! docker compose version >/dev/null 2>&1; then
    red "[ERREUR] Docker Compose V2 est introuvable. Ce script nécessite la V2."
    exit 1
fi

echo "Configuration système (Réseau avancé)..."
echo "net.ipv4.ip_nonlocal_bind=1" > /etc/sysctl.d/99-hub-bind.conf
sysctl -p /etc/sysctl.d/99-hub-bind.conf >/dev/null 2>&1

echo "Vérification de la disponibilité du port 53 (requis pour Pi-hole)..."
if ss -ltnup '( sport = :53 )' 2>/dev/null | grep -q ':53'; then
  yellow "Un service écoute déjà sur le port 53."
  if systemctl is-active --quiet systemd-resolved && ss -ltnup '( sport = :53 )' 2>/dev/null | grep -qE 'systemd-resolve|systemd-resolved'; then
    yellow "systemd-resolved occupe le port 53. Désactivation contrôlée..."
    [ -f /etc/resolv.conf ] && cp /etc/resolv.conf /etc/resolv.conf.bak_hub_install
    systemctl disable --now systemd-resolved >/dev/null 2>&1 || true
    rm -f /etc/resolv.conf
    printf 'nameserver 1.1.1.1\nnameserver 1.0.0.1\n' > /etc/resolv.conf
    
    # Vérification post-désactivation
    sleep 2
    if ss -ltnup '( sport = :53 )' 2>/dev/null | grep -q ':53'; then
      red "[ERREUR] Le port 53 n'a pas été libéré après la désactivation de systemd-resolved. Abandon."
      exit 1
    else
      green "systemd-resolved désactivé, resolv.conf reconfiguré et Port 53 confirmé libre."
    fi
  else
    red "[ERREUR] Le port 53 est occupé par un autre service. Libérez-le avant de relancer l'installation."
    ss -ltnup '( sport = :53 )' || true
    exit 1
  fi
else
  green "Le port 53 est libre."
fi

echo "Vérification de Docker..."
systemctl enable docker >/dev/null 2>&1 || true
systemctl start docker

# ---------------------------------------------------------
# 4. Définition des chemins Cibles
# ---------------------------------------------------------
HUB_ROOT="/opt/hub"

CORE_DIR="${HUB_ROOT}/core"
CORE_COMPOSE_DIR="${CORE_DIR}/compose/core"

STATE_DIR="${HUB_ROOT}/state"
STATE_ENV_DIR="${STATE_DIR}/env"
STATE_DOCKER_DIR="${STATE_DIR}/docker"
STATE_WG_DIR="${STATE_DIR}/wireguard"

CUSTOM_DIR="${HUB_ROOT}/custom"
CUSTOM_ENV_DIR="${CUSTOM_DIR}/env"
CUSTOM_OVERRIDES_DIR="${CUSTOM_DIR}/overrides"
CUSTOM_HOOKS_DIR="${CUSTOM_DIR}/hooks"

BACKUPS_DIR="${HUB_ROOT}/backups"
BACKUPS_ARCHIVES_DIR="${BACKUPS_DIR}/archives"

SCRIPTS_DIR="${HUB_ROOT}/scripts"

CORE_COMPOSE_FILE="${CORE_COMPOSE_DIR}/docker-compose.yml"
CORE_ENV_FILE="${STATE_ENV_DIR}/core.env"
CUSTOM_ENV_FILE="${CUSTOM_ENV_DIR}/custom.env"
CUSTOM_OVERRIDE_FILE="${CUSTOM_OVERRIDES_DIR}/docker-compose.override.yml"

UFW_TARGET="${SCRIPTS_DIR}/ufw_apply.sh"
WG_PROVISION_TARGET="${SCRIPTS_DIR}/wg_provision.sh"
HUB_STATUS_TARGET="${SCRIPTS_DIR}/hub_status.sh"

HUB_UP_TARGET="${SCRIPTS_DIR}/hub-up.sh"
HUB_DOWN_TARGET="${SCRIPTS_DIR}/hub-down.sh"
HUB_RESTART_TARGET="${SCRIPTS_DIR}/hub-restart.sh"
HUB_PS_TARGET="${SCRIPTS_DIR}/hub-ps.sh"
HUB_LOGS_TARGET="${SCRIPTS_DIR}/hub-logs.sh"
HUB_HEARTBEAT_TARGET="${SCRIPTS_DIR}/hub_heartbeat.sh"

DEFAULT_WG_IP="10.10.10.2"
DEFAULT_LAN_SUBNET="192.168.0.0/16"
DEFAULT_CONTROL_PLANE_API="http://10.10.10.1:8000"

# Configuration Array pour les commandes Docker Compose internes
DOCKER_OPTS=(
  -f "$CORE_COMPOSE_FILE"
  -f "$CUSTOM_OVERRIDE_FILE"
  --env-file "$CORE_ENV_FILE"
  --env-file "$CUSTOM_ENV_FILE"
)

# ---------------------------------------------------------
# 5. Création de l'arborescence
# ---------------------------------------------------------
log_step "[2/8] Création de l'arborescence"

ensure_dir "$HUB_ROOT"
ensure_dir "$CORE_COMPOSE_DIR"
ensure_dir "$STATE_ENV_DIR"

# Création explicite des sous-dossiers Docker pour garantir les permissions
ensure_dir "${STATE_DOCKER_DIR}/npm"
ensure_dir "${STATE_DOCKER_DIR}/npm/data"
ensure_dir "${STATE_DOCKER_DIR}/npm/letsencrypt"
ensure_dir "${STATE_DOCKER_DIR}/pihole"
ensure_dir "${STATE_DOCKER_DIR}/pihole/etc-pihole"
ensure_dir "${STATE_DOCKER_DIR}/pihole/etc-dnsmasq.d"

ensure_dir "$STATE_WG_DIR"
ensure_dir "$CUSTOM_ENV_DIR"
ensure_dir "$CUSTOM_OVERRIDES_DIR"
ensure_dir "$CUSTOM_HOOKS_DIR"
ensure_dir "$BACKUPS_ARCHIVES_DIR"
ensure_dir "$SCRIPTS_DIR"
green "Arborescence système structurée."

# ---------------------------------------------------------
# 6. Déploiement du Core (Produit)
# ---------------------------------------------------------
log_step "[3/8] Déploiement du core (Produit)"

if [ -f "$CORE_COMPOSE_FILE" ] && [ "$FORCE" -eq 0 ] && [ "$REPAIR" -eq 0 ]; then
  yellow "Le fichier docker-compose.yml existe déjà. Conservation (utiliser --force pour écraser)."
else
  install -m 644 -o root -g root "$SRC_COMPOSE" "$CORE_COMPOSE_FILE"
  green "docker-compose.yml (Core) déployé."
fi

# ---------------------------------------------------------
# 7. Initialisation du State (Données et Secrets)
# ---------------------------------------------------------
log_step "[4/8] Initialisation du State (Données)"

ensure_file "$CORE_ENV_FILE"
chown root:root "$CORE_ENV_FILE"
chmod 600 "$CORE_ENV_FILE"

append_if_missing "$CORE_ENV_FILE" "TZ" "Europe/Paris"
append_if_missing "$CORE_ENV_FILE" "WG_IP" "$DEFAULT_WG_IP"
append_if_missing "$CORE_ENV_FILE" "LAN_SUBNET" "$DEFAULT_LAN_SUBNET"
append_if_missing "$CORE_ENV_FILE" "CONTROL_PLANE_API" "$DEFAULT_CONTROL_PLANE_API"

# Migration douce de l'ancien mot de passe Pi-hole si présent
if grep -q "^PIHOLE_PASSWORD=" "$CORE_ENV_FILE" && ! grep -q "^WEBPASSWORD=" "$CORE_ENV_FILE"; then
  OLD_PASS="$(grep "^PIHOLE_PASSWORD=" "$CORE_ENV_FILE" | cut -d '=' -f2- | tr -d '\r')"
  echo "WEBPASSWORD=${OLD_PASS}" >> "$CORE_ENV_FILE"
  sed -i '/^PIHOLE_PASSWORD=/d' "$CORE_ENV_FILE"
  yellow "Migration de l'ancien PIHOLE_PASSWORD vers WEBPASSWORD effectuée."
fi

PIHOLE_GENERATED_NOW=0
if ! grep -q "^WEBPASSWORD=" "$CORE_ENV_FILE"; then
  PIHOLE_RANDOM_PASS="$(openssl rand -hex 16)"
  echo "WEBPASSWORD=${PIHOLE_RANDOM_PASS}" >> "$CORE_ENV_FILE"
  PIHOLE_GENERATED_NOW=1
  green "Mot de passe Pi-hole sécurisé généré."
fi

green "Fichier core.env prêt."

# ---------------------------------------------------------
# 8. Initialisation du Custom (Zone Client)
# ---------------------------------------------------------
log_step "[5/8] Initialisation du Custom (Zone Client)"

ensure_file "$CUSTOM_ENV_FILE"
ensure_file "$CUSTOM_OVERRIDE_FILE"

if [ ! -s "$CUSTOM_OVERRIDE_FILE" ]; then
  cat > "$CUSTOM_OVERRIDE_FILE" <<'EOF'
# Docker Compose override client
# Ajouter ici des services supplémentaires (Plex, Nextcloud...) ou des surcharges.
EOF
fi

if [ ! -s "$CUSTOM_ENV_FILE" ]; then
  cat > "$CUSTOM_ENV_FILE" <<'EOF'
# Variables d'environnement personnalisées client
EOF
fi

# Verrouillage absolu des permissions après génération
chown root:root "$CUSTOM_ENV_FILE" "$CUSTOM_OVERRIDE_FILE"
chmod 600 "$CUSTOM_ENV_FILE"
chmod 644 "$CUSTOM_OVERRIDE_FILE"

green "Fichiers de personnalisation (Custom) sécurisés."

# ---------------------------------------------------------
# 9. Installation des Scripts Produit & Outils de Run
# ---------------------------------------------------------
log_step "[6/8] Génération de l'outillage d'exploitation et de télémétrie"

# Scripts métier
install -m 700 -o root -g root "$SRC_UFW" "$UFW_TARGET"
install -m 700 -o root -g root "$SRC_WG_PROVISION" "$WG_PROVISION_TARGET"
install -m 700 -o root -g root "$SRC_HUB_STATUS" "$HUB_STATUS_TARGET"

# Mini-scripts d'exploitation
cat > "$HUB_UP_TARGET" <<EOF
#!/usr/bin/env bash
set -euo pipefail
docker compose -f "$CORE_COMPOSE_FILE" -f "$CUSTOM_OVERRIDE_FILE" --env-file "$CORE_ENV_FILE" --env-file "$CUSTOM_ENV_FILE" up -d
EOF

cat > "$HUB_DOWN_TARGET" <<EOF
#!/usr/bin/env bash
set -euo pipefail
docker compose -f "$CORE_COMPOSE_FILE" -f "$CUSTOM_OVERRIDE_FILE" --env-file "$CORE_ENV_FILE" --env-file "$CUSTOM_ENV_FILE" down
EOF

cat > "$HUB_RESTART_TARGET" <<EOF
#!/usr/bin/env bash
set -euo pipefail
"$HUB_DOWN_TARGET"
"$HUB_UP_TARGET"
EOF

cat > "$HUB_PS_TARGET" <<EOF
#!/usr/bin/env bash
set -euo pipefail
docker compose -f "$CORE_COMPOSE_FILE" -f "$CUSTOM_OVERRIDE_FILE" --env-file "$CORE_ENV_FILE" --env-file "$CUSTOM_ENV_FILE" ps
EOF

cat > "$HUB_LOGS_TARGET" <<EOF
#!/usr/bin/env bash
set -euo pipefail
docker compose -f "$CORE_COMPOSE_FILE" -f "$CUSTOM_OVERRIDE_FILE" --env-file "$CORE_ENV_FILE" --env-file "$CUSTOM_ENV_FILE" logs --tail=200 "\$@"
EOF

# ==========================================
# --- SCRIPT DE TÉLÉMÉTRIE (Heartbeat) ---
# ==========================================
cat > "$HUB_HEARTBEAT_TARGET" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

CORE_ENV="/opt/hub/state/env/core.env"
if [ -f "$CORE_ENV" ]; then
  set -a
  source "$CORE_ENV"
  set +a
fi

API_BASE="${CONTROL_PLANE_API:-http://10.10.10.1:8000}"
STATUS_SCRIPT="/opt/hub/scripts/hub_status.sh"

need_cmd() { command -v "$1" >/dev/null 2>&1; }
for c in curl jq; do need_cmd "$c" || exit 1; done

STATUS_JSON="$("$STATUS_SCRIPT")"

PAYLOAD="$(jq -n \
  --argjson status "$STATUS_JSON" \
  '{
    status: $status
  }'
)"

curl -fsS \
  --connect-timeout 3 \
  --max-time 10 \
  -X POST "${API_BASE}/api/v1/hub-heartbeat" \
  -H "Content-Type: application/json" \
  --data "$PAYLOAD" \
  >/dev/null || echo "[WARNING] Heartbeat failed to reach $API_BASE" >&2
EOF

chmod 700 "$HUB_UP_TARGET" "$HUB_DOWN_TARGET" "$HUB_RESTART_TARGET" "$HUB_PS_TARGET" "$HUB_LOGS_TARGET" "$HUB_HEARTBEAT_TARGET"
chown root:root "$HUB_UP_TARGET" "$HUB_DOWN_TARGET" "$HUB_RESTART_TARGET" "$HUB_PS_TARGET" "$HUB_LOGS_TARGET" "$HUB_HEARTBEAT_TARGET"

# ==========================================
# --- SYSTEMD POUR TÉLÉMÉTRIE ---
# ==========================================
cat > /etc/systemd/system/hub-heartbeat.service <<EOF
[Unit]
Description=Hub Heartbeat Sender
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=$HUB_HEARTBEAT_TARGET
EOF

cat > /etc/systemd/system/hub-heartbeat.timer <<EOF
[Unit]
Description=Run Hub Heartbeat every 5 minutes

[Timer]
OnBootSec=2min
OnUnitActiveSec=5min
Persistent=true
Unit=hub-heartbeat.service

[Install]
WantedBy=timers.target
EOF

systemctl daemon-reload >/dev/null 2>&1 || true
systemctl enable --now hub-heartbeat.timer >/dev/null 2>&1 || true

green "Scripts métier, utilitaires et télémétrie (Heartbeat) générés et activés."

# ---------------------------------------------------------
# 10. Lancement de la Stack
# ---------------------------------------------------------
log_step "[7/8] Lancement de l'infrastructure Docker"

if ! docker compose "${DOCKER_OPTS[@]}" config >/dev/null 2>&1; then
  red "[ERREUR] La configuration Docker Compose fusionnée est invalide."
  exit 1
fi

"$HUB_UP_TARGET"

echo "Vérification post-installation..."
sleep 5
"$HUB_PS_TARGET"

# ---------------------------------------------------------
# 11. Provisioning WireGuard (ZTP)
# ---------------------------------------------------------
log_step "[8/8] Provisioning WireGuard"

if [ "$SKIP_PROVISION" -eq 1 ]; then
  yellow "Provisioning WireGuard ignoré (--skip-provision)."
elif [ -z "$HUB_TOKEN" ]; then
  yellow "Aucun token fourni (--token) : provisioning WireGuard ignoré."
else
  echo " -> Lancement de l'agent de provisioning..."
  "$WG_PROVISION_TARGET" "$HUB_TOKEN"

  echo -e "\nVérification du tunnel WireGuard..."
  sleep 2
  wg show || yellow "WireGuard actif mais sans peer visible."

  echo -e "\nTest connectivité VPS..."
  ping -c 2 -W 2 10.10.10.1 || yellow "Ping VPS non confirmé (L'API peut filtrer l'ICMP)."
fi

# ---------------------------------------------------------
# 12. Résumé final
# ---------------------------------------------------------
FINAL_WG_IP="$(grep "^WG_IP=" "$CORE_ENV_FILE" | cut -d '=' -f2 | tr -d '\r')"
STORED_PIHOLE_PASS="$(grep "^WEBPASSWORD=" "$CORE_ENV_FILE" | cut -d '=' -f2 | tr -d '\r')"

echo
blue "=================================================="
green " 🎉 INSTALLATION HUB TERMINÉE (TEST) 🚀"
blue "=================================================="
echo " Arborescence prête dans : /opt/hub"

if [ -n "${FINAL_WG_IP:-}" ]; then
  echo " Admin NPM (VPN)     : http://${FINAL_WG_IP}:81"
  echo " Admin Pi-hole (VPN) : http://${FINAL_WG_IP}:8053"
else
  yellow " WG_IP non défini dans core.env : accès admin VPN non déterminé."
fi

echo -e " DNS Pi-hole (LAN/WG): port 53 (UDP/TCP)\n"

if [ "$SHOW_SECRETS" -eq 1 ]; then
  red "[SECRET] Mot de passe Pi-hole : $STORED_PIHOLE_PASS"
else
  yellow "🔑 Le mot de passe Pi-hole est stocké en sécurité."
  echo -e "   Pour le voir, tapez : ${NC}sudo cat $CORE_ENV_FILE"
fi

echo
echo "Scripts d'exploitation au quotidien :"
echo "  Démarrer : $HUB_UP_TARGET"
echo "  Arrêter  : $HUB_DOWN_TARGET"
echo "  Redémarrer : $HUB_RESTART_TARGET"
echo "  Statut   : $HUB_PS_TARGET"
echo "  Logs     : $HUB_LOGS_TARGET"

echo
yellow "🛡️ PARE-FEU : Le script UFW est prêt mais non activé."
echo "   Pour verrouiller la box une fois le réseau validé :"
echo "   sudo $UFW_TARGET"
blue "=================================================="
