#!/usr/bin/env bash
# Steam Server Manager — Linux-Server (Headless).
# Standard: .deb von https://dl.nova-realms.de/steam-server-manager
#
#   curl -fsSL https://dl.nova-realms.de/steam-server-manager/install.sh | sudo bash
#   sudo ./scripts/install.sh
#   sudo ./scripts/install.sh --from-source
#   sudo ./scripts/install.sh --bind-all
set -euo pipefail

FAMILY=""
BIND_ALL=0
NO_SERVICE=0
IN_PLACE=0
FROM_SOURCE=0
PREFIX="${SSM_PREFIX:-/opt/steam-server-manager}"
NODE_VER="${SSM_NODE_VERSION:-24.19.0}"
DL_BASE="${SSM_DOWNLOAD_BASE:-https://dl.nova-realms.de/steam-server-manager}"

usage() {
  cat <<'EOF'
Usage: install.sh [debian|fedora|arch|suse|generic] [Optionen]

Standard (Debian/Ubuntu): lädt das .deb von https://dl.nova-realms.de/steam-server-manager

  --from-source   aus diesem Git-Repo bauen statt Download
  --bind-all      Web-Panel auf allen Interfaces (SSM_BIND_ALL=1)
  --no-service    kein systemd (nur --from-source)
  --in-place      im Repo-Ordner belassen (nur --from-source)
  --prefix DIR    Zielverzeichnis bei --from-source (Standard: /opt/steam-server-manager)

Distro-Hinweis nur für --from-source bzw. SteamCMD-Libs.
Deinstallation: sudo apt remove steam-server-manager
                oder sudo ./scripts/uninstall.sh
EOF
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    -h|--help) usage; exit 0 ;;
    --bind-all) BIND_ALL=1 ;;
    --no-service) NO_SERVICE=1 ;;
    --in-place) IN_PLACE=1 ;;
    --from-source) FROM_SOURCE=1 ;;
    --prefix)
      PREFIX="${2:-}"
      if [[ -z "$PREFIX" || "$PREFIX" == -* ]]; then
        echo "--prefix erwartet ein Verzeichnis" >&2
        exit 1
      fi
      shift
      ;;
    --prefix=*) PREFIX="${1#--prefix=}" ;;
    debian|fedora|arch|suse|generic) FAMILY="$1" ;;
    *) echo "Unbekanntes Argument: $1" >&2; usage; exit 1 ;;
  esac
  shift
done

need_root() {
  if [[ "${EUID}" -ne 0 ]]; then
    echo "Bitte mit root/sudo ausführen." >&2
    exit 1
  fi
}

detect_family() {
  if [[ -n "$FAMILY" ]]; then
    return
  fi
  if [[ ! -f /etc/os-release ]]; then
    FAMILY=generic
    return
  fi
  # shellcheck disable=SC1091
  . /etc/os-release
  local id="${ID:-}" like="${ID_LIKE:-}"
  case "$id" in
    ubuntu|debian|linuxmint|pop|raspbian) FAMILY=debian ;;
    fedora|rhel|centos|rocky|almalinux|ol) FAMILY=fedora ;;
    arch|manjaro|endeavouros|cachyos) FAMILY=arch ;;
    opensuse*|sles) FAMILY=suse ;;
    *)
      case "$like" in
        *debian*|*ubuntu*) FAMILY=debian ;;
        *rhel*|*fedora*|*centos*) FAMILY=fedora ;;
        *arch*) FAMILY=arch ;;
        *suse*) FAMILY=suse ;;
        *) FAMILY=generic ;;
      esac
      ;;
  esac
}

pkg_debian() {
  export DEBIAN_FRONTEND=noninteractive
  apt-get update -y
  apt-get install -y ca-certificates curl tar xz-utils git build-essential python3 \
    lib32gcc-s1 libc6-i386 || apt-get install -y ca-certificates curl tar xz-utils git \
    build-essential python3 lib32gcc1 libc6-i386
}

pkg_debian_download() {
  export DEBIAN_FRONTEND=noninteractive
  apt-get update -y
  apt-get install -y ca-certificates curl
  apt-get install -y lib32gcc-s1 libc6-i386 2>/dev/null || apt-get install -y lib32gcc1 libc6-i386 2>/dev/null || true
}

pkg_fedora() {
  if command -v dnf >/dev/null 2>&1; then
    dnf install -y ca-certificates curl tar xz git gcc-c++ make python3 glibc.i686 libstdc++.i686
  else
    yum install -y ca-certificates curl tar xz git gcc-c++ make python3 glibc.i686 libstdc++.i686
  fi
}

pkg_arch() {
  pacman -Sy --noconfirm --needed ca-certificates curl tar xz git base-devel python lib32-gcc-libs lib32-glibc
}

pkg_suse() {
  zypper --non-interactive install -y ca-certificates curl tar xz git gcc-c++ make python3 \
    libgcc_s1-32bit libstdc++6-32bit || zypper --non-interactive install -y ca-certificates curl tar xz git gcc-c++ make python3
}

node_major() {
  node -p "process.versions.node.split('.')[0]" 2>/dev/null || echo 0
}

install_node_tarball() {
  local arch raw dest name url
  raw="$(uname -m)"
  case "$raw" in
    x86_64) arch=x64 ;;
    aarch64|arm64) arch=arm64 ;;
    *) echo "Keine Node-Tarball für Architektur $raw. Node >= 24 selbst installieren." >&2; exit 1 ;;
  esac
  dest=/usr/local
  name="node-v${NODE_VER}-linux-${arch}"
  url="https://nodejs.org/dist/v${NODE_VER}/${name}.tar.xz"
  echo "Installiere Node ${NODE_VER} nach ${dest} …"
  curl -fsSL "$url" | tar -xJ -C /tmp
  mkdir -p "$dest"
  cp -a "/tmp/${name}/." "$dest/"
  rm -rf "/tmp/${name}"
  hash -r || true
}

ensure_node() {
  local major
  major="$(node_major)"
  if [[ "$major" -ge 24 ]]; then
    echo "Node $(node -v) ist ausreichend."
    return
  fi
  echo "Node 24+ fehlt (gefunden: $(command -v node >/dev/null && node -v || echo keins))."
  install_node_tarball
  if [[ "$(node_major)" -lt 24 ]]; then
    echo "Node 24 konnte nicht bereitgestellt werden." >&2
    exit 1
  fi
}

repo_root() {
  local here
  here="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
  echo "$here"
}

run_as_build_user() {
  local cmd="$1"
  local npm_user="${SUDO_USER:-root}"
  if [[ "$npm_user" != root && "${EUID}" -eq 0 ]]; then
    if command -v runuser >/dev/null 2>&1; then
      runuser -u "$npm_user" -- bash -lc "$cmd"
    else
      su -s /bin/bash "$npm_user" -c "$cmd"
    fi
  else
    bash -lc "$cmd"
  fi
}

build_in_repo() {
  local root
  root="$(repo_root)"
  if [[ ! -f "$root/package.json" ]]; then
    echo "package.json nicht gefunden in $root — --from-source braucht das Git-Repo." >&2
    exit 1
  fi
  local npm_user="${SUDO_USER:-root}"
  echo "npm install + build in $root (User: $npm_user)"
  local path_export='export PATH=/usr/local/bin:/usr/bin:$PATH'
  run_as_build_user "$path_export; cd '$root' && npm install && npm run build"
  if [[ ! -f "$root/out/main/headless.js" ]]; then
    echo "Build fehlgeschlagen: out/main/headless.js fehlt." >&2
    exit 1
  fi
}

deploy_to_prefix() {
  local root dest
  root="$(repo_root)"
  dest="$PREFIX"
  mkdir -p "$dest"
  rm -rf "$dest/out" "$dest/node_modules"
  cp -a "$root/package.json" "$dest/"
  if [[ -f "$root/package-lock.json" ]]; then
    cp -a "$root/package-lock.json" "$dest/"
  fi
  cp -a "$root/out" "$dest/"
  echo "Produktions-Abhängigkeiten in $dest …"
  (
    export PATH=/usr/local/bin:/usr/bin:$PATH
    cd "$dest"
    if [[ -f package-lock.json ]]; then
      npm ci --omit=dev --ignore-scripts
    else
      npm install --omit=dev --ignore-scripts
    fi
  )
  local user="${SUDO_USER:-root}"
  if [[ "$user" != root ]]; then
    chown -R "$user:$user" "$dest"
  fi
  echo "$root" >"$dest/.ssm-source"
}

install_app_from_source() {
  local root dest
  root="$(repo_root)"
  dest="$PREFIX"
  build_in_repo
  if [[ "$IN_PLACE" -eq 1 ]] || [[ "$root" == "$dest" ]]; then
    PREFIX="$root"
    echo "Installation bleibt in $PREFIX"
    return
  fi
  deploy_to_prefix
}

write_service() {
  local dest user unit env_bind node_bin
  dest="$PREFIX"
  user="${SUDO_USER:-root}"
  env_bind=""
  if [[ "$BIND_ALL" -eq 1 ]]; then
    env_bind="Environment=SSM_BIND_ALL=1"
  fi
  node_bin="$(command -v node)"
  unit=/etc/systemd/system/steam-server-manager.service
  cat >"$unit" <<EOF
[Unit]
Description=Steam Server Manager (Headless Web-Panel)
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=${user}
WorkingDirectory=${dest}
Environment=NODE_ENV=production
Environment=SSM_HEADLESS=1
${env_bind}
ExecStart=${node_bin} ${dest}/out/main/headless.js
Restart=on-failure
RestartSec=5
TimeoutStopSec=60

[Install]
WantedBy=multi-user.target
EOF
  systemctl daemon-reload
  systemctl enable --now steam-server-manager.service
  echo "systemd: steam-server-manager.service aktiv (User ${user}, Autostart beim Boot)."
}

deb_arch() {
  case "$(uname -m)" in
    x86_64) echo amd64 ;;
    aarch64|arm64) echo arm64 ;;
    *) echo "" ;;
  esac
}

apply_bind_all_deb() {
  mkdir -p /etc
  if [[ ! -f /etc/default/steam-server-manager ]]; then
    printf '# SSM_BIND_ALL=1\n' >/etc/default/steam-server-manager
  fi
  if grep -q '^SSM_BIND_ALL=' /etc/default/steam-server-manager; then
    sed -i 's/^#\?SSM_BIND_ALL=.*/SSM_BIND_ALL=1/' /etc/default/steam-server-manager
  else
    printf 'SSM_BIND_ALL=1\n' >>/etc/default/steam-server-manager
  fi
  if command -v systemctl >/dev/null && [ -d /run/systemd/system ]; then
    systemctl restart steam-server-manager.service || true
  fi
}

install_from_download() {
  local arch version url file sha_url
  arch="$(deb_arch)"
  if [[ -z "$arch" ]]; then
    echo "Keine .deb für Architektur $(uname -m). Nutze --from-source." >&2
    exit 1
  fi
  if ! command -v curl >/dev/null; then
    echo "curl fehlt." >&2
    exit 1
  fi
  version="$(curl -fsSL "$DL_BASE/latest.txt" 2>/dev/null | tr -d '\r[:space:]' || true)"
  if [[ -n "$version" ]]; then
    url="$DL_BASE/steam-server-manager_${version}_${arch}.deb"
  else
    echo "latest.txt nicht erreichbar, versuche unversionierte Datei."
    url="$DL_BASE/steam-server-manager_${arch}.deb"
  fi
  file="/tmp/$(basename "$url")"
  echo "Lade $url …"
  if ! curl -fL --retry 3 -o "$file" "$url"; then
    echo "Download fehlgeschlagen: $url" >&2
    echo "Datei auf ${DL_BASE}/ ablegen oder mit --from-source aus dem Repo installieren." >&2
    exit 1
  fi
  sha_url="${url}.sha256"
  if curl -fsSL -o "${file}.sha256" "$sha_url"; then
    echo "Prüfe SHA256 …"
    if command -v sha256sum >/dev/null; then
      (cd "$(dirname "$file")" && sha256sum -c "$(basename "$file").sha256")
    fi
  else
    echo "Hinweis: keine .sha256 unter $sha_url — Checksumme übersprungen."
  fi
  export DEBIAN_FRONTEND=noninteractive
  if command -v apt-get >/dev/null; then
    apt-get install -y "$file" || { dpkg -i "$file" && apt-get install -f -y; }
  else
    dpkg -i "$file"
  fi
  rm -f "$file" "${file}.sha256"
  if [[ "$BIND_ALL" -eq 1 ]]; then
    apply_bind_all_deb
  fi
  echo "Paket steam-server-manager installiert."
  echo "Daten: /var/lib/steam-server-manager  Dienstbenutzer: ssm"
}

can_use_deb() {
  [[ "$FAMILY" == debian ]] || command -v dpkg >/dev/null
}

finish_msg() {
  echo
  echo "Fertig. Panel: http://127.0.0.1:7842/  (erster Besuch legt den Admin an)"
  if [[ "$BIND_ALL" -eq 1 ]]; then
    echo "SSM_BIND_ALL=1 gesetzt — nur im LAN, HTTPS/Token empfohlen."
  fi
  echo "Logs: journalctl -u steam-server-manager -f"
}

main() {
  need_root
  detect_family
  echo "Linux-Familie: $FAMILY"

  if [[ "$FROM_SOURCE" -eq 0 ]] && can_use_deb; then
    pkg_debian_download
    install_from_download
    finish_msg
    echo "Deinstallieren: sudo apt remove steam-server-manager"
    return
  fi

  if [[ "$FROM_SOURCE" -eq 0 ]]; then
    echo "Kein Debian/Ubuntu (.deb). Baue aus dem Quellcode. Für das Paket: Debian/Ubuntu oder --from-source."
    FROM_SOURCE=1
  fi

  case "$FAMILY" in
    debian) pkg_debian ;;
    fedora) pkg_fedora ;;
    arch) pkg_arch ;;
    suse) pkg_suse ;;
    generic) echo "Generic: curl/tar/git und 32-Bit-Libs selbst sicherstellen." ;;
  esac
  ensure_node
  install_app_from_source
  if [[ "$NO_SERVICE" -eq 0 ]]; then
    write_service
  fi
  finish_msg
  echo "Dateien: $PREFIX"
  echo "Deinstallieren: sudo $(repo_root)/scripts/uninstall.sh"
}

main
