#!/usr/bin/env bash
# =============================================================================
# barox SecPen - Updateskript
#
# Ablauf: laufende Scans prüfen -> Wartungsmodus -> Sicherung -> Dateien
# aktualisieren -> Abhängigkeiten -> Migrationen -> Caches -> Dienste neu
# starten -> Prüfung.
#
# Aufruf:  sudo bash deploy/update.sh [Quellverzeichnis]
# =============================================================================
set -euo pipefail

APP_DIR="${APP_DIR:-/var/www/html/barox-secpen}"
BACKUP_DIR="${BACKUP_DIR:-/var/backups/barox-secpen}"
APP_USER="${APP_USER:-www-data}"
PHP_BIN="${PHP_BIN:-/usr/bin/php}"

RED=$'\e[31m'; GREEN=$'\e[32m'; YELLOW=$'\e[33m'; BLUE=$'\e[34m'; RESET=$'\e[0m'
step() { echo -e "\n${BLUE}==>${RESET} $*"; }
ok()   { echo -e "  ${GREEN}OK${RESET}   $*"; }
warn() { echo -e "  ${YELLOW}!${RESET}    $*"; }
fail() { echo -e "  ${RED}FEHLER${RESET} $*"; exit 1; }

[[ $EUID -eq 0 ]] || fail "Bitte mit sudo bzw. als root ausführen."

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

echo "============================================================"
echo " barox SecPen - Update"
echo " Quelle: ${SOURCE_DIR}"
echo " Ziel:   ${APP_DIR}"
echo "============================================================"

# ------------------------------------------------------------ Laufende Scans
step "Laufende Scans prüfen"

cd "$APP_DIR"
# HOME muss beschreibbar sein: artisan tinker (PsySH) legt dort seine Konfig an.
# Bei APP_USER=www-data ist /var/www nicht beschreibbar - daher HOME=/tmp.
# Ein Fehler hier darf das Update nicht abbrechen (|| true -> RUNNING leer -> 0).
RUNNING="$(sudo -u "$APP_USER" env HOME=/tmp "$PHP_BIN" artisan tinker --execute \
    'echo \App\Models\ScanJob::active()->count();' 2>/dev/null | tail -1 | tr -d '[:space:]' || true)"

if [[ "${RUNNING:-0}" =~ ^[0-9]+$ ]] && [[ "${RUNNING}" -gt 0 ]]; then
    warn "Es laufen derzeit ${RUNNING} Scans."
    read -r -p "  Trotzdem fortfahren? Laufende Scans werden abgebrochen. [j/N] " CONTINUE
    [[ "${CONTINUE,,}" == "j" ]] || fail "Update abgebrochen."
    sudo -u "$APP_USER" "$PHP_BIN" artisan barox:emergency-stop on --reason="Systemupdate"
    ok "Not-Aus gesetzt, laufende Scans gestoppt"
    EMERGENCY_SET=1
else
    ok "Keine laufenden Scans"
    EMERGENCY_SET=0
fi

# ---------------------------------------------------------------- Sicherung
step "Sicherung erstellen"

mkdir -p "$BACKUP_DIR"
STAMP="$(date +%Y%m%d-%H%M%S)"

DB_NAME="$(grep -E '^DB_DATABASE=' "$APP_DIR/.env" | cut -d= -f2- | tr -d '"\r')"
DB_USER="$(grep -E '^DB_USERNAME=' "$APP_DIR/.env" | cut -d= -f2- | tr -d '"\r')"
DB_PASS="$(grep -E '^DB_PASSWORD=' "$APP_DIR/.env" | cut -d= -f2- | tr -d '"\r')"
DB_HOST="$(grep -E '^DB_HOST=' "$APP_DIR/.env" | cut -d= -f2- | tr -d '"\r')"

if command -v mysqldump >/dev/null; then
    # Zugangsdaten in einer temporaeren Optionsdatei uebergeben. --defaults-file
    # muss das erste Argument sein und sorgt dafuer, dass mysqldump AUSSCHLIESSLICH
    # diese Datei liest - so koennen weder /root/.my.cnf noch andere globale
    # [client]-Eintraege User/Passwort ueberschreiben. Zudem taucht das Passwort
    # nicht in der Prozessliste auf.
    MYSQL_CNF="$(mktemp)"
    chmod 600 "$MYSQL_CNF"
    {
        echo "[client]"
        echo "user=${DB_USER}"
        echo "password=${DB_PASS}"
        echo "host=${DB_HOST:-127.0.0.1}"
    } > "$MYSQL_CNF"

    if mysqldump --defaults-file="$MYSQL_CNF" --single-transaction --quick --no-tablespaces "$DB_NAME" \
        | gzip > "${BACKUP_DIR}/db-${STAMP}.sql.gz"; then
        ok "Datenbank gesichert: ${BACKUP_DIR}/db-${STAMP}.sql.gz"
    else
        rm -f "$MYSQL_CNF"
        fail "Datenbanksicherung fehlgeschlagen - Update abgebrochen."
    fi
    rm -f "$MYSQL_CNF"
else
    warn "mysqldump fehlt - keine Datenbanksicherung erstellt."
fi

cp "$APP_DIR/.env" "${BACKUP_DIR}/env-${STAMP}.bak"
chmod 600 "${BACKUP_DIR}/env-${STAMP}.bak"
ok "Konfiguration gesichert"

# ------------------------------------------------------------ Wartungsmodus
step "Wartungsmodus aktivieren"
sudo -u "$APP_USER" "$PHP_BIN" artisan down --render="errors::503" --retry=60 || true
ok "Wartungsmodus aktiv"

cleanup() {
    cd "$APP_DIR" 2>/dev/null || return
    sudo -u "$APP_USER" "$PHP_BIN" artisan up >/dev/null 2>&1 || true
}
trap cleanup EXIT

# --------------------------------------------------------------- Dateien
step "Dateien aktualisieren"

if [[ "$SOURCE_DIR" != "$APP_DIR" ]]; then
    rsync -a --delete \
        --exclude '.env' --exclude 'storage/' --exclude 'vendor/' \
        --exclude 'bootstrap/cache/' \
        --exclude '.git/' --exclude 'node_modules/' \
        "$SOURCE_DIR/" "$APP_DIR/"
    ok "Dateien übernommen (.env, storage/ und bootstrap/cache/ unverändert)"
else
    ok "Quelle entspricht dem Zielverzeichnis"
fi

# Von Laravel benoetigte Laufzeitverzeichnisse sicherstellen (nicht im Repo).
mkdir -p \
    "$APP_DIR/bootstrap/cache" \
    "$APP_DIR/storage/framework/cache" \
    "$APP_DIR/storage/framework/sessions" \
    "$APP_DIR/storage/framework/views" \
    "$APP_DIR/storage/logs"

chown -R "$APP_USER:$APP_USER" "$APP_DIR"
chmod -R 775 "$APP_DIR/storage" "$APP_DIR/bootstrap/cache"

# ------------------------------------------------------------ Abhängigkeiten
step "Abhängigkeiten aktualisieren"
cd "$APP_DIR"
sudo -u "$APP_USER" composer install --no-dev --optimize-autoloader --no-interaction --prefer-dist
ok "Composer abgeschlossen"

# ------------------------------------------------------------------ Migration
step "Migrationen"
sudo -u "$APP_USER" "$PHP_BIN" artisan migrate --force
ok "Datenbankschema aktuell"

# Neue Systemprofile und Berechtigungen nachziehen (idempotent).
sudo -u "$APP_USER" "$PHP_BIN" artisan db:seed --class=RolePermissionSeeder --force
sudo -u "$APP_USER" "$PHP_BIN" artisan db:seed --class=BlocklistSeeder --force
ok "Rollen, Berechtigungen und Systemblocklist abgeglichen"

# --------------------------------------------------------------------- Cache
step "Caches erneuern"
sudo -u "$APP_USER" "$PHP_BIN" artisan config:clear
sudo -u "$APP_USER" "$PHP_BIN" artisan config:cache
sudo -u "$APP_USER" "$PHP_BIN" artisan route:cache
sudo -u "$APP_USER" "$PHP_BIN" artisan view:cache
ok "Caches erneuert"

# ------------------------------------------------------------------- Dienste
step "Dienste neu starten"
systemctl daemon-reload
for unit in barox-secpen-worker barox-secpen-health; do
    if systemctl is-enabled "$unit" >/dev/null 2>&1; then
        systemctl restart "$unit"
        ok "${unit} neu gestartet"
    else
        warn "${unit} ist nicht aktiviert"
    fi
done

# ------------------------------------------------------------- Wartungsmodus
step "Wartungsmodus beenden"
sudo -u "$APP_USER" "$PHP_BIN" artisan up
trap - EXIT
ok "Anwendung wieder erreichbar"

if [[ "$EMERGENCY_SET" -eq 1 ]]; then
    warn "Der Not-Aus ist noch aktiv. Nach der Prüfung aufheben mit:"
    echo "       sudo -u ${APP_USER} ${PHP_BIN} ${APP_DIR}/artisan barox:emergency-stop off"
fi

# -------------------------------------------------------------------- Prüfung
step "Installationsprüfung"
set +e
sudo -u "$APP_USER" "$PHP_BIN" artisan barox:preflight
sudo -u "$APP_USER" "$PHP_BIN" artisan barox:audit-verify
set -e

echo
echo "============================================================"
echo " Update abgeschlossen. Sicherung: ${BACKUP_DIR}/db-${STAMP}.sql.gz"
echo "============================================================"
