#!/usr/bin/env bash
# Installation et mise à jour de Passerelle (Linux et macOS) : https://passerelle.valtiks.fr/docs
# Windows : voir install.ps1.
#
#   Première installation :  curl -fsSL https://passerelle.valtiks.fr/install.sh | LICENSE=VOTRE-CLÉ bash
#   Mise à jour          :  curl -fsSL https://passerelle.valtiks.fr/install.sh | bash
#
# Ce script :
#   1. vérifie Docker et l'architecture du serveur ;
#   2. active la licence (seuls la clé et une empreinte anonyme du serveur sont envoyées) ;
#   3. télécharge l'image Passerelle depuis le registre privé ;
#   4. démarre le conteneur (~/passerelle/compose.yml, jamais écrasé) ;
#   5. au premier lancement, relie Passerelle à votre réseau Tailscale et affiche son adresse HTTPS.
# Aucun port n'est ouvert sur le serveur : Passerelle n'est visible que par vos appareils Tailscale.

set -euo pipefail

SITE="${PASSERELLE_SITE:-https://passerelle.valtiks.fr}"
DIR="${PASSERELLE_DIR:-$HOME/passerelle}"
NAME="${PASSERELLE_NAME:-passerelle}"

# Empreinte SHA-256 (sha256sum sous Linux, shasum sous macOS).
sha256() { if command -v sha256sum >/dev/null; then sha256sum; else shasum -a 256; fi; }

# Identifiant stable de la machine, jamais envoyé en clair (seule son empreinte part).
machine_raw() {
  if [ -r /etc/machine-id ] && [ -s /etc/machine-id ]; then
    cat /etc/machine-id
  elif [ "$(uname -s)" = "Darwin" ]; then
    ioreg -rd1 -c IOPlatformExpertDevice | awk -F'"' '/IOPlatformUUID/{print $4}'
  fi
}

say() { printf '\033[1;33m›\033[0m %s\n' "$*"; }
die() { printf '\033[1;31m✗\033[0m %s\n' "$*" >&2; exit 1; }

# Tout est dans une fonction : un téléchargement interrompu n'exécute rien.
main() {
  local license="${LICENSE:-}"
  # Mise à jour : la clé est déjà enregistrée.
  if [ -z "$license" ] && [ -f "$DIR/.env" ]; then
    license=$(sed -n 's/^PASSERELLE_LICENSE=//p' "$DIR/.env")
  fi
  [ -n "$license" ] || die "Indiquez votre clé : curl -fsSL $SITE/install.sh | LICENSE=VOTRE-CLÉ bash"
  [[ "$license" =~ ^[A-Za-z0-9-]{10,64}$ ]] || die "La clé de licence n'a pas le bon format."

  command -v curl >/dev/null || die "curl est nécessaire."
  if [ "$(uname -s)" = "Darwin" ]; then
    command -v docker >/dev/null || die "Installez Docker Desktop (https://www.docker.com/products/docker-desktop/) ou OrbStack, lancez-le, puis relancez cette commande."
  else
    command -v docker >/dev/null || die "Docker n'est pas installé : https://docs.docker.com/engine/install/"
  fi
  docker compose version >/dev/null 2>&1 || die "Docker Compose (plugin « docker compose ») est nécessaire."
  docker info >/dev/null 2>&1 || die "Impossible de parler à Docker. Lancez le script avec un utilisateur du groupe docker."
  case "$(uname -m)" in
    x86_64 | amd64 | aarch64 | arm64) ;;
    *) die "Architecture $(uname -m) non prise en charge (x86_64 ou arm64 uniquement)." ;;
  esac

  mkdir -p "$DIR"
  chmod 700 "$DIR"

  # Empreinte du serveur : dérivée de machine-id, jamais envoyée en clair.
  local raw machine label
  raw=$(machine_raw || true)
  if [ -z "$raw" ]; then
    [ -s "$DIR/.machine-id" ] || head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n' >"$DIR/.machine-id"
    raw=$(cat "$DIR/.machine-id")
  fi
  machine=$(printf 'passerelle:%s' "$raw" | sha256 | cut -c1-64)
  label=$(hostname 2>/dev/null | tr -cd 'A-Za-z0-9.-' | cut -c1-60)

  say "Activation de la licence…"
  local body code resp msg
  body=$(printf '{"key":"%s","machineId":"%s","label":"%s"}' "$license" "$machine" "${label:-serveur}")
  resp=$(curl -sS -w '\n%{http_code}' -X POST -H 'Content-Type: application/json' --data "$body" "$SITE/api/license/activate") \
    || die "Le site $SITE ne répond pas. Réessayez dans un instant."
  code=$(printf '%s' "$resp" | tail -n1)
  resp=$(printf '%s' "$resp" | sed '$d')
  if [ "$code" != "200" ]; then
    msg=$(printf '%s' "$resp" | sed -n 's/.*"message":"\([^"]*\)".*/\1/p')
    die "${msg:-Activation refusée (code $code).}"
  fi
  local image host
  image=$(printf '%s' "$resp" | sed -n 's/.*"image":"\([^"]*\)".*/\1/p')
  host=$(printf '%s' "$resp" | sed -n 's/.*"host":"\([^"]*\)".*/\1/p')
  [ -n "$image" ] && [ -n "$host" ] || die "Réponse inattendue du site."

  say "Téléchargement de Passerelle…"
  printf '%s' "$machine" | docker login "$host" -u "$license" --password-stdin >/dev/null 2>&1 \
    || die "Connexion au registre refusée."
  docker pull -q "$image:latest" >/dev/null || die "Téléchargement impossible."
  docker logout "$host" >/dev/null 2>&1 || true

  # Réglages privés (clé, empreinte) : lisibles par vous seul.
  umask 077
  cat >"$DIR/.env" <<ENV
PASSERELLE_LICENSE=$license
PASSERELLE_MACHINE_ID=$machine
PASSERELLE_UPDATE_URL=$SITE/api/passerelle/latest.json
PASSERELLE_CHECK_URL=$SITE/api/license/check
PASSERELLE_INSTALL_URL=$SITE/install.sh
ENV

  if [ ! -f "$DIR/compose.yml" ]; then
    cat >"$DIR/compose.yml" <<YML
# Généré par install.sh. Vous pouvez le modifier : il ne sera pas écrasé.
services:
  passerelle:
    image: $image:latest
    container_name: $NAME
    hostname: $NAME
    restart: unless-stopped
    env_file: .env
    # Accès local de secours (tunnel SSH : ssh -L 9000:127.0.0.1:9000 serveur).
    # L'accès normal passe par Tailscale, sans port ouvert.
    ports:
      - "127.0.0.1:9000:9000"
    volumes:
      - home:/home/node
volumes:
  home:
YML
  fi

  say "Démarrage…"
  (cd "$DIR" && docker compose up -d --remove-orphans >/dev/null)
  local container
  container=$(cd "$DIR" && docker compose ps -q passerelle)
  for _ in $(seq 1 30); do docker exec "$container" tailscale version >/dev/null 2>&1 && break; sleep 1; done

  # Premier lancement : connexion à Tailscale (le lien s'ouvre sur votre téléphone ou ordinateur).
  if ! docker exec "$container" tailscale status >/dev/null 2>&1; then
    printf '\n\033[1m  Reliez Passerelle à votre réseau Tailscale\033[0m (compte gratuit : https://tailscale.com) :\n'
    printf '  ouvrez le lien qui s’affiche, connectez-vous et validez.\n\n'
    docker exec -it "$container" tailscale up --hostname="$NAME" </dev/tty || die "Connexion Tailscale interrompue. Relancez la commande."
  fi
  docker exec "$container" tailscale serve --bg --https=443 http://127.0.0.1:9000 >/dev/null
  docker exec "$container" tailscale serve --bg --https=8443 http://127.0.0.1:8081 >/dev/null

  local dns
  dns=$(docker exec "$container" sh -c 'tailscale status --json | python3 -c "import json,sys; print(json.load(sys.stdin)[\"Self\"][\"DNSName\"].rstrip(\".\"))"' 2>/dev/null || true)

  printf '\n\033[1;32m✓\033[0m Passerelle est prêt.\n'
  if [ "$(uname -s)" = "Darwin" ]; then
    printf '  Sur un Mac, les sessions s’arrêtent quand il se met en veille : pour du 24h/24, préférez un serveur.\n'
  fi
  if [ -n "$dns" ]; then
    printf '  Ouvrez \033[1mhttps://%s\033[0m depuis un appareil connecté à Tailscale.\n' "$dns"
  fi
  printf '  Premier accès : si la page ne charge pas, activez « HTTPS Certificates » dans\n'
  printf '  https://login.tailscale.com/admin/dns puis réessayez (une seule fois par réseau).\n'
  printf '  Guide : %s/docs\n' "$SITE"
}

main "$@"
