#!/usr/bin/env bash
# =============================================================================
# barox SecPen - Installationsskript
#
# Prüft Voraussetzungen, legt Benutzer und Verzeichnisse an, installiert die
# Abhängigkeiten, bereitet die .env vor, führt Migrationen und Seeder aus und
# installiert die systemd-Dienste.
#
# Bestehende Apache-, PHP- oder MySQL-Konfigurationen werden NICHT verändert.
# Die vorbereiteten Dateien werden nach /etc/... nur kopiert, wenn dort noch
# nichts liegt; andernfalls wird ein Hinweis mit Diff-Befehl ausgegeben.
#
# Aufruf:  sudo bash deploy/install.sh
# =============================================================================
set -euo pipefail

APP_NAME="barox SecPen"
APP_DIR="${APP_DIR:-/var/www/html/barox-secpen}"
DATA_DIR="${DATA_DIR:-/var/lib/barox-secpen}"
LOG_DIR="${LOG_DIR:-/var/log/barox-secpen}"
APP_USER="${APP_USER:-www-data}"
APP_GROUP="${APP_GROUP:-www-data}"
WEB_USER="${WEB_USER:-www-data}"
DB_NAME="${DB_NAME:-barox_secpen}"
DB_USER="${DB_USER:-barox_secpen}"
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="$(dirname "$SCRIPT_DIR")"

echo "============================================================"
echo " ${APP_NAME} - Installation"
echo " Quelle:     ${SOURCE_DIR}"
echo " Zielpfad:   ${APP_DIR}"
echo " Datenpfad:  ${DATA_DIR}"
echo " Datenbank:  ${DB_NAME}"
echo "============================================================"

# ----------------------------------------------------------- Voraussetzungen
step "Voraussetzungen prüfen"

command -v "$PHP_BIN" >/dev/null || fail "PHP wurde nicht gefunden ($PHP_BIN)."
PHP_VERSION="$("$PHP_BIN" -r 'echo PHP_VERSION;')"
if ! "$PHP_BIN" -r 'exit(version_compare(PHP_VERSION, "8.3.0", ">=") ? 0 : 1);'; then
    fail "PHP 8.3 oder neuer wird benötigt (gefunden: ${PHP_VERSION})."
fi
ok "PHP ${PHP_VERSION}"

MISSING_EXT=()
for ext in pdo_mysql dom simplexml libxml json openssl mbstring curl fileinfo tokenizer ctype; do
    "$PHP_BIN" -m | grep -qi "^${ext}$" || MISSING_EXT+=("$ext")
done
if [[ ${#MISSING_EXT[@]} -gt 0 ]]; then
    fail "Fehlende PHP-Erweiterungen: ${MISSING_EXT[*]} (z. B. apt install php8.3-{mysql,xml,mbstring,curl})"
fi
ok "Alle benötigten PHP-Erweiterungen vorhanden"

for ext in snmp redis; do
    "$PHP_BIN" -m | grep -qi "^${ext}$" && ok "ext-${ext} (optional) vorhanden" \
        || warn "ext-${ext} fehlt (optional, empfohlen für ${ext})"
done

command -v composer >/dev/null || fail "Composer wurde nicht gefunden."
ok "Composer $(composer --version --no-ansi 2>/dev/null | head -1)"

command -v nmap >/dev/null && ok "Nmap $(nmap --version | head -1)" \
    || warn "Nmap ist nicht installiert (apt install nmap) - ohne Nmap sind keine Scans möglich."
command -v ping >/dev/null || warn "ping fehlt (apt install iputils-ping)"
command -v snmpget >/dev/null || warn "snmpget fehlt (apt install snmp) - nur nötig ohne ext-snmp"
command -v mysql >/dev/null || warn "mysql-Client fehlt - die Datenbank muss manuell angelegt werden."
command -v apache2 >/dev/null || warn "Apache2 wurde nicht gefunden."

# --------------------------------------------------------- Benutzer / Gruppen
step "Systembenutzer einrichten"

if id "$APP_USER" >/dev/null 2>&1; then
    ok "Benutzer ${APP_USER} existiert bereits"
else
    useradd --system --home-dir "$DATA_DIR" --shell /usr/sbin/nologin --user-group "$APP_USER"
    ok "Benutzer ${APP_USER} angelegt"
fi

# Apache muss die Anwendung lesen können; schreiben darf nur barox.
if id "$WEB_USER" >/dev/null 2>&1; then
    usermod -aG "$APP_GROUP" "$WEB_USER"
    ok "${WEB_USER} zur Gruppe ${APP_GROUP} hinzugefügt"
fi

# ----------------------------------------------------------- Dateien kopieren
step "Anwendung bereitstellen"

if [[ "$SOURCE_DIR" != "$APP_DIR" ]]; then
    mkdir -p "$APP_DIR"
    # .env und storage/ des Zielsystems bleiben erhalten.
    rsync -a --delete \
        --exclude '.env' --exclude 'storage/' --exclude 'vendor/' \
        --exclude '.git/' --exclude 'node_modules/' \
        "$SOURCE_DIR/" "$APP_DIR/"
    ok "Dateien nach ${APP_DIR} kopiert"
else
    ok "Installation erfolgt direkt im Quellverzeichnis"
fi

# --------------------------------------------------------------- Verzeichnisse
step "Verzeichnisse und Berechtigungen"

mkdir -p "$DATA_DIR"/{scans,reports,attachments} "$LOG_DIR"
mkdir -p "$APP_DIR"/storage/{app/private,framework/{cache/data,sessions,views},logs}
mkdir -p "$APP_DIR"/bootstrap/cache

chown -R "$APP_USER:$APP_GROUP" "$DATA_DIR" "$LOG_DIR" "$APP_DIR"
chmod 750 "$DATA_DIR" "$DATA_DIR"/scans "$DATA_DIR"/reports "$DATA_DIR"/attachments "$LOG_DIR"
chmod -R 775 "$APP_DIR"/storage "$APP_DIR"/bootstrap/cache
# Der Rest der Anwendung ist für den Webserver nur lesbar.
find "$APP_DIR" -path "$APP_DIR/storage" -prune -o -type d -exec chmod 755 {} \;
find "$APP_DIR" -path "$APP_DIR/storage" -prune -o -type f -exec chmod 644 {} \;
chmod 640 "$APP_DIR/.env" 2>/dev/null || true
ok "Verzeichnisse angelegt (Rohdaten liegen ausserhalb des DocumentRoot)"

# ---------------------------------------------------------------- Composer
step "Abhängigkeiten installieren"

cd "$APP_DIR"
sudo -u "$APP_USER" COMPOSER_ALLOW_SUPERUSER=0 composer install \
    --no-dev --optimize-autoloader --no-interaction --prefer-dist
ok "Composer-Abhängigkeiten installiert"

# --------------------------------------------------------------------- .env
step ".env vorbereiten"

if [[ -f "$APP_DIR/.env" ]]; then
    ok ".env existiert bereits - unverändert übernommen"
else
    cp "$APP_DIR/.env.example" "$APP_DIR/.env"
    chown "$APP_USER:$APP_GROUP" "$APP_DIR/.env"
    chmod 640 "$APP_DIR/.env"
    ok ".env aus .env.example erzeugt"

    read -r -p "  Datenbank-Passwort für ${DB_USER} (leer = später eintragen): " DB_PASS
    if [[ -n "${DB_PASS}" ]]; then
        sed -i "s|^DB_PASSWORD=.*|DB_PASSWORD=${DB_PASS}|" "$APP_DIR/.env"
    fi
fi

if ! grep -q '^APP_KEY=base64:' "$APP_DIR/.env"; then
    sudo -u "$APP_USER" "$PHP_BIN" artisan key:generate --force
    ok "Application Key erzeugt"
else
    ok "Application Key vorhanden"
fi

# ------------------------------------------------------------------ Datenbank
step "Datenbank"

if command -v mysql >/dev/null && mysql -e "SELECT 1" >/dev/null 2>&1; then
    if mysql -e "USE ${DB_NAME}" >/dev/null 2>&1; then
        ok "Datenbank ${DB_NAME} existiert bereits - wird nicht verändert"
    else
        read -r -p "  Datenbank ${DB_NAME} jetzt anlegen? [j/N] " CREATE_DB
        if [[ "${CREATE_DB,,}" == "j" ]]; then
            read -r -s -p "  Passwort für ${DB_USER}: " NEW_DB_PASS; echo
            mysql <<SQL
CREATE DATABASE ${DB_NAME} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS '${DB_USER}'@'localhost' IDENTIFIED BY '${NEW_DB_PASS}';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX, DROP, REFERENCES ON ${DB_NAME}.* TO '${DB_USER}'@'localhost';
FLUSH PRIVILEGES;
SQL
            sed -i "s|^DB_PASSWORD=.*|DB_PASSWORD=${NEW_DB_PASS}|" "$APP_DIR/.env"
            ok "Datenbank und Benutzer angelegt"
        else
            warn "Datenbank wurde nicht angelegt - bitte manuell einrichten."
        fi
    fi
else
    warn "Kein administrativer MySQL-Zugang - Datenbank bitte manuell anlegen."
fi

# ------------------------------------------------------------------ Migration
step "Migrationen und Seed-Daten"

if sudo -u "$APP_USER" "$PHP_BIN" artisan migrate --force; then
    ok "Migrationen ausgeführt"
    sudo -u "$APP_USER" "$PHP_BIN" artisan db:seed --force
    ok "Rollen, Berechtigungen, Scanprofile und Geräteprofile angelegt"
else
    warn "Migrationen fehlgeschlagen - bitte Datenbankzugang in .env prüfen."
fi

# ---------------------------------------------------------------- Dienste
step "systemd-Dienste installieren"

for unit in barox-secpen-worker.service barox-secpen-health.service \
            barox-secpen-scheduler.service barox-secpen-scheduler.timer; do
    target="/etc/systemd/system/${unit}"
    if [[ -f "$target" ]]; then
        if ! diff -q "$SCRIPT_DIR/$unit" "$target" >/dev/null; then
            warn "${unit} existiert und weicht ab - nicht überschrieben."
            echo "       Vergleich: diff ${target} ${SCRIPT_DIR}/${unit}"
        else
            ok "${unit} bereits aktuell"
        fi
    else
        install -m 0644 "$SCRIPT_DIR/$unit" "$target"
        ok "${unit} installiert"
    fi
done

systemctl daemon-reload
ok "systemd neu geladen"

# -------------------------------------------------------------- Apache / PHP
step "Apache- und PHP-Konfiguration vorbereiten"

APACHE_TARGET="/etc/apache2/sites-available/barox-secpen.conf"
if [[ -f "$APACHE_TARGET" ]]; then
    warn "${APACHE_TARGET} existiert bereits - nicht überschrieben."
    echo "       Vergleich: diff ${APACHE_TARGET} ${SCRIPT_DIR}/apache-barox-secpen.conf"
else
    install -m 0644 "$SCRIPT_DIR/apache-barox-secpen.conf" "$APACHE_TARGET"
    ok "VirtualHost nach ${APACHE_TARGET} kopiert (noch nicht aktiviert)"
    echo "       Aktivieren:  a2enmod rewrite headers ssl && a2ensite barox-secpen && systemctl reload apache2"
fi

FPM_TARGET="/etc/php/8.3/fpm/pool.d/barox-secpen.conf"
if [[ -d /etc/php/8.3/fpm/pool.d ]]; then
    if [[ -f "$FPM_TARGET" ]]; then
        warn "${FPM_TARGET} existiert bereits - nicht überschrieben."
    else
        install -m 0644 "$SCRIPT_DIR/php-fpm-barox-secpen.conf" "$FPM_TARGET"
        ok "PHP-FPM-Pool nach ${FPM_TARGET} kopiert"
        echo "       Aktivieren:  systemctl reload php8.3-fpm"
    fi
fi

LOGROTATE_TARGET="/etc/logrotate.d/barox-secpen"
if [[ -f "$LOGROTATE_TARGET" ]]; then
    warn "${LOGROTATE_TARGET} existiert bereits - nicht überschrieben."
else
    install -m 0644 "$SCRIPT_DIR/logrotate-barox-secpen" "$LOGROTATE_TARGET"
    ok "Logrotate-Konfiguration installiert"
fi

# ------------------------------------------------------------------- Caches
step "Konfiguration zwischenspeichern"

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 erzeugt"

# ------------------------------------------------------------------- Prüfung
step "Installationsprüfung"

set +e
sudo -u "$APP_USER" "$PHP_BIN" artisan barox:preflight
PREFLIGHT_STATUS=$?
set -e

echo
echo "============================================================"
if [[ $PREFLIGHT_STATUS -eq 0 ]]; then
    echo -e "${GREEN} Installation abgeschlossen.${RESET}"
else
    echo -e "${YELLOW} Installation abgeschlossen - die Prüfung meldet offene Punkte.${RESET}"
fi
cat <<'HINT'

Nächste Schritte:

  1. .env prüfen  (APP_URL, DB, SMTP, Entra ID, Nessus, Allowlist-Grenzen)
  2. Apache aktivieren:
       a2enmod rewrite headers ssl
       a2ensite barox-secpen
       systemctl reload apache2
  3. Dienste starten:
       systemctl enable --now barox-secpen-worker
       systemctl enable --now barox-secpen-health
       systemctl enable --now barox-secpen-scheduler.timer
  4. Rohsocket-Rechte für Nmap prüfen:  deploy/nmap-capabilities.md
  5. Anmelden und zuerst die Allowlist füllen - ohne freigegebenes Netz
     kann kein Scan gestartet werden.

HINT
echo "============================================================"
