#!/usr/bin/env bash
# ============================================================================
# ORBIT Player — OTA-Update-Script (Phase 13 — Tarball-Pipeline)
# Lumeon Media UG (haftungsbeschraenkt)
#
# Verwendung:
#   sudo bash scripts/orbit-update.sh              # Naechtliches Update pruefen + ausfuehren
#   sudo bash scripts/orbit-update.sh --rollback   # Sofort auf stable zurueckrollen
#
# Was passiert (normaler Modus):
#   1. Lokale Version aus VERSION lesen
#   2. latest.json vom Release-Server laden (Version + Tarball + SHA256)
#   3. Versionen vergleichen — nur bei neuerer Version weiter
#   4. Tarball + Checksum herunterladen
#   5. SHA256-Integritaetspruefung
#   6. Canary-Verzeichnis entpacken
#   7. pip install in isoliertem venv im Canary-Dir
#   8. Atomarer Symlink-Swap (ln -sfn + mv -T)
#   9. Crash-Counter zuruecksetzen, restart_reason=update
#  10. systemctl restart orbit-player
#
# Was passiert (--rollback):
#   1. Symlink zurueck auf stable (atomar)
#   2. Crash-Counter zuruecksetzen, restart_reason=rollback
#   3. systemctl restart orbit-player
#
# Fehlerfaelle: Bei Abbruch (Netz, SHA256-Mismatch, pip, etc.) wird das
# Canary-Dir aufgeraeumt. Kein Partial-State. Naechster Versuch morgen 04:00.
# ============================================================================

set -euo pipefail
IFS=$'\n\t'

# ----------------------------------------------------------------------------
# Farbcodes + Logger (Stil von provision.sh — [ORBIT]/[WARN]/[FEHLER])
# ----------------------------------------------------------------------------
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'

ts() { date '+%Y-%m-%d %H:%M:%S'; }

log()  { local m; m="$(ts) [ORBIT]  $*"; echo -e "${GREEN}[ORBIT]${NC} $*"; echo "$m" >>"$UPDATE_LOG" 2>/dev/null || true; }
info() { local m; m="$(ts) [INFO]   $*"; echo -e "${CYAN}[INFO]${NC}  $*"; echo "$m" >>"$UPDATE_LOG" 2>/dev/null || true; }
warn() { local m; m="$(ts) [WARN]   $*"; echo -e "${YELLOW}[WARN]${NC}  $*"; echo "$m" >>"$UPDATE_LOG" 2>/dev/null || true; }
err()  { local m; m="$(ts) [FEHLER] $*"; echo -e "${RED}[FEHLER]${NC} $*" >&2; echo "$m" >>"$UPDATE_LOG" 2>/dev/null || true; }

# ----------------------------------------------------------------------------
# Konstanten
# ----------------------------------------------------------------------------
LINK="/opt/orbit-player"
STABLE="/opt/orbit-player-stable"
CANARY="/opt/orbit-player-canary"
STATE_DIR="/var/run/orbit-update"
CRASH_FILE="$STATE_DIR/crash_count"
REASON_FILE="$STATE_DIR/restart_reason"
CANARY_TS="$STATE_DIR/canary_activated_at"
UPDATE_LOG="/var/log/orbit-player/update.log"

# Release-Server URLs
RELEASE_URL_PRIMARY="https://ota-player-update.ohloop.de"
RELEASE_URL_FALLBACK="http://87.106.55.53:8091"

# Temporaeres Download-Verzeichnis
DL_DIR="/tmp/orbit-update-dl"

# Log-Verzeichnis sicherstellen
mkdir -p "$(dirname "$UPDATE_LOG")" 2>/dev/null || true

# ----------------------------------------------------------------------------
# Root-Check
# ----------------------------------------------------------------------------
if [[ $EUID -ne 0 ]]; then
    err "Dieses Script muss als root ausgefuehrt werden: sudo bash scripts/orbit-update.sh"
    exit 1
fi

# ----------------------------------------------------------------------------
# Cleanup-Trap — bei ERR/INT/TERM: unvollstaendiges Canary-Dir + Downloads entfernen
# ----------------------------------------------------------------------------
cleanup() {
    local exit_code=$?
    if [[ "$exit_code" -ne 0 ]]; then
        err "Update fehlgeschlagen (Exit $exit_code) — Cleanup laeuft"
        # Symlink zurueck auf stable falls er schon auf canary zeigt
        if [[ -L "$LINK" && "$(readlink "$LINK")" == "$CANARY" && -d "$STABLE" ]]; then
            ln -sfn "$STABLE" "${LINK}-new"
            mv -T "${LINK}-new" "$LINK"
            info "Symlink zurueck auf stable gesetzt"
        fi
        rm -rf "$CANARY" 2>/dev/null || true
        rm -rf "$DL_DIR" 2>/dev/null || true
    fi
    exit "$exit_code"
}
trap 'cleanup' ERR INT TERM

# ----------------------------------------------------------------------------
# State-Dir sicherstellen (tmpfs, ueberlebt keinen Reboot — gewollt)
# ----------------------------------------------------------------------------
mkdir -p "$STATE_DIR"

# ----------------------------------------------------------------------------
# Helper: Download mit Primaer-URL + Fallback
# ----------------------------------------------------------------------------
download() {
    local path="$1"
    local dest="$2"

    # Primaer: Caddy via DNS
    if curl -sSf --connect-timeout 10 --max-time 120 \
        "${RELEASE_URL_PRIMARY}/${path}" -o "$dest" 2>/dev/null; then
        return 0
    fi

    warn "Primaer-URL fehlgeschlagen — Fallback auf direkte IP"

    # Fallback: direkte IP
    if curl -sSf --connect-timeout 10 --max-time 120 \
        "${RELEASE_URL_FALLBACK}/${path}" -o "$dest" 2>/dev/null; then
        return 0
    fi

    err "Download fehlgeschlagen fuer: $path (beide URLs versucht)"
    return 1
}

# ============================================================================
# Modus: --rollback
# ============================================================================
if [[ "${1:-}" == "--rollback" ]]; then
    log "ROLLBACK angefordert — Symlink zurueck auf stable"

    if [[ ! -d "$STABLE" ]]; then
        err "Kein stable-Verzeichnis vorhanden ($STABLE) — Rollback unmoeglich"
        exit 1
    fi

    # Atomarer Symlink-Swap zurueck auf stable
    ln -sfn "$STABLE" "${LINK}-new"
    mv -T "${LINK}-new" "$LINK"

    # Crash-Counter zuruecksetzen
    echo "0" > "$CRASH_FILE"
    echo "rollback" > "$REASON_FILE"
    rm -f "$CANARY_TS"

    systemctl restart orbit-player
    log "Rollback abgeschlossen — Symlink zeigt auf stable"
    exit 0
fi

# ============================================================================
# Normaler Modus: Update pruefen + ausfuehren
# ============================================================================

# --- Schritt 1: Lokale Version lesen ---
CURRENT=$(cat "$LINK/VERSION" 2>/dev/null || echo "v0.0.0")
info "Aktuelle Version: $CURRENT"

# --- Schritt 2: latest.json vom Release-Server laden ---
mkdir -p "$DL_DIR"

if ! download "latest.json" "$DL_DIR/latest.json"; then
    err "Konnte latest.json nicht laden — kein Netz?"
    rm -rf "$DL_DIR"
    exit 0  # Kein Fehler — morgen erneut versuchen (D-21)
fi

# JSON parsen
LATEST=$(jq -r '.version' "$DL_DIR/latest.json" 2>/dev/null || echo "")
TARBALL_NAME=$(jq -r '.tarball' "$DL_DIR/latest.json" 2>/dev/null || echo "")
EXPECTED_SHA=$(jq -r '.sha256' "$DL_DIR/latest.json" 2>/dev/null || echo "")

if [[ -z "$LATEST" || -z "$TARBALL_NAME" || -z "$EXPECTED_SHA" ]]; then
    err "latest.json unvollstaendig oder fehlerhaft"
    rm -rf "$DL_DIR"
    exit 1
fi

info "Remote-Version: $LATEST (Tarball: $TARBALL_NAME)"

# --- Schritt 3: Versionsvergleich ---
if [[ "$LATEST" == "$CURRENT" ]]; then
    log "Keine neue Version verfuegbar (aktuell: $CURRENT)"
    rm -rf "$DL_DIR"
    exit 0
fi

# Semantische Versionspruefung: nur updaten wenn Remote neuer
# Strip 'v' prefix fuer sort -V Vergleich
current_stripped="${CURRENT#v}"
latest_stripped="${LATEST#v}"

newer=$(printf '%s\n%s' "$current_stripped" "$latest_stripped" | sort -V | tail -1)
if [[ "$newer" == "$current_stripped" ]]; then
    log "Lokale Version ($CURRENT) ist gleich oder neuer als Remote ($LATEST) — kein Update"
    rm -rf "$DL_DIR"
    exit 0
fi

log "Neue Version gefunden: $CURRENT -> $LATEST"

# --- Schritt 4: Tarball herunterladen ---
info "Lade Tarball: $TARBALL_NAME"
if ! download "$TARBALL_NAME" "$DL_DIR/$TARBALL_NAME"; then
    err "Tarball-Download fehlgeschlagen"
    rm -rf "$DL_DIR"
    exit 1
fi

# --- Schritt 5: SHA256-Integritaetspruefung (D-24) ---
info "Pruefe SHA256-Integritaet..."
ACTUAL_SHA=$(sha256sum "$DL_DIR/$TARBALL_NAME" | cut -d' ' -f1)

if [[ "$ACTUAL_SHA" != "$EXPECTED_SHA" ]]; then
    err "SHA256-Mismatch! Erwartet: $EXPECTED_SHA, Erhalten: $ACTUAL_SHA"
    err "Tarball moeglicherweise korrumpiert oder manipuliert — Update abgebrochen"
    rm -rf "$DL_DIR"
    exit 1
fi

log "SHA256 verifiziert: $ACTUAL_SHA"

# --- Schritt 6: Canary-Dir entpacken ---
rm -rf "$CANARY"
mkdir -p "$CANARY"

info "Entpacke Tarball nach $CANARY"
tar xzf "$DL_DIR/$TARBALL_NAME" -C "$CANARY" || {
    err "Entpacken fehlgeschlagen"
    rm -rf "$CANARY"
    rm -rf "$DL_DIR"
    exit 1
}

# Download-Verzeichnis aufraeumen (Tarball nicht mehr noetig)
rm -rf "$DL_DIR"

# Plausibilitaetspruefung: VERSION-File muss existieren
if [[ ! -f "$CANARY/VERSION" ]]; then
    err "Entpacktes Verzeichnis enthaelt keine VERSION-Datei — ungueltige Release"
    rm -rf "$CANARY"
    exit 1
fi

CANARY_VERSION=$(cat "$CANARY/VERSION")
info "Canary-Version: $CANARY_VERSION"

# --- Schritt 7: pip install in isoliertem venv (D-15) ---
info "Erstelle venv und installiere Abhaengigkeiten in $CANARY/venv"
python3 -m venv --system-site-packages "$CANARY/venv"
"$CANARY/venv/bin/pip" install --quiet --upgrade pip
"$CANARY/venv/bin/pip" install --quiet -r "$CANARY/requirements.txt" || {
    err "pip install fehlgeschlagen"
    rm -rf "$CANARY"
    exit 1  # D-19: Cleanup und Fehler
}
chown -R orbit:orbit "$CANARY"

# --- Schritt 8: Atomarer Symlink-Swap (D-14) ---
info "Atomarer Symlink-Swap: $LINK -> $CANARY"
ln -sfn "$CANARY" "${LINK}-new"
mv -T "${LINK}-new" "$LINK"

# --- Schritt 9: State zuruecksetzen + Restart ---
echo "0" > "$CRASH_FILE"
echo "update" > "$REASON_FILE"
date +%s > "$CANARY_TS"

systemctl restart orbit-player

# --- Schritt 10: Abschluss-Log ---
log "Update auf $LATEST abgeschlossen — Player neugestartet"
log "Canary-Bewertung laeuft (48h). Bei >3 Crashes: automatischer Rollback."
