#!/bin/bash
# bpm-setup — Operations-CLI für Backup Proxy Manager
#
# Wird vom postinst aufgerufen, ist aber auch standalone als root nutzbar
# für Admin-PW-Reset, .env-Edit, Mail-/LDAP-Test.
set -e

BACKEND=/usr/share/backup-manager/backend
ENV_FILE=/etc/backup-manager/.env
TLS_DIR=/etc/backup-manager/tls
TLS_CERT="$TLS_DIR/cert.pem"
TLS_KEY="$TLS_DIR/key.pem"

# Wrapper für artisan-Aufrufe als www-data. HOME wird explizit auf das
# BPM-Daten-Verzeichnis gezeigt — runuser zieht sonst HOME=/var/www aus
# /etc/passwd, was nicht schreibbar ist (psysh/tinker fällt auf die Nase).
# runuser statt sudo, weil minimales Debian kein sudo vorinstalliert hat.
artisan_as_www() {
    runuser -u www-data -- env HOME=/var/lib/backup-manager/home \
        php "$BACKEND/artisan" "$@"
}

# Liefert das mysql-Kommando-Präfix (Programm + Auth-Args) für root-Zugriff.
# Identische Logik wie im postinst — debian.cnf zuerst, danach unix_socket.
# Leere Ausgabe = kein Auth-Pfad gefunden, Aufrufer muss prüfen.
mysql_root_cmd() {
    if [ -r /etc/mysql/debian.cnf ] && \
       mysql --defaults-file=/etc/mysql/debian.cnf -e 'SELECT 1' >/dev/null 2>&1; then
        echo "mysql --defaults-file=/etc/mysql/debian.cnf"
        return
    fi
    if mysql -u root -e 'SELECT 1' >/dev/null 2>&1; then
        echo "mysql -u root"
        return
    fi
    echo ""
}

usage() {
    cat <<EOF
bpm-setup — Backup Proxy Manager Operations

Befehle:
  reset-admin-password         Admin-Passwort auf neuen Random-Wert setzen
                               und in /root/bpm-credentials.txt schreiben.
  status                       Service-Status (nginx, php-fpm, supervisor,
                               mariadb, iscsid) + Worker.
  artisan <args...>            Artisan-Wrapper als www-data im richtigen
                               Working-Dir (z. B. 'bpm-setup artisan tinker').
  edit-env                     \$EDITOR /etc/backup-manager/.env, danach
                               config:clear automatisch.
  show-env                     Inhalt von /etc/backup-manager/.env (Secrets
                               redacted).

  cert-info                    Subject, Issuer, SANs und Gültigkeit des
                               aktuellen TLS-Zertifikats anzeigen.
  install-cert <cert> <key>    PKI-Zertifikat einspielen (validiert Pärchen,
                               testet nginx-Config, lädt nginx neu). Die
                               Original-Files werden NICHT verschoben.
  regenerate-cert              Neues selbstsigniertes Zertifikat erzeugen
                               (z. B. nach Hostname-/IP-Änderung). 10 Jahre
                               gültig, RSA 4096.

  backup                       BPM Self-Backup auslösen (DB + .env + ssh-keys),
                               verschlüsselt auf das erste gemountete Storage-
                               Target. Voraussetzung: Backup-Passwort in der UI
                               (Administration → System-Backup) gesetzt.
  restore <datei>              BPM-Restore aus einem Backup-Archiv. Fragt
                               interaktiv nach dem Backup-Passwort. ⚠ Spielt
                               die DB ein, überschreibt .env + ssh-keys und
                               synchronisiert das MariaDB-Benutzerpasswort
                               an die wiederhergestellte .env. Anmelden danach
                               mit dem Admin-Passwort vom QUELL-System.

Erfordert root.
EOF
    exit 1
}

require_root() {
    [ "$(id -u)" -eq 0 ] || { echo "bpm-setup: root erforderlich"; exit 1; }
}

cmd_reset_admin_password() {
    require_root
    NEW_PW=$(openssl rand -base64 18 | tr -d '=/+\n' | cut -c1-20)

    cd "$BACKEND"
    artisan_as_www tinker --execute="
        \$u = \App\Models\User::where('username','admin')
            ->where('type','local')
            ->first();
        if (! \$u) { echo 'NO_ADMIN'; exit; }
        \$u->update(['password' => '${NEW_PW}']);
        echo 'OK';
    " | tail -1 | grep -q '^OK' || {
        echo "Konnte Admin-PW nicht setzen (Admin-User vorhanden? DB erreichbar?)"
        exit 1
    }

    APP_URL=$(grep -E '^APP_URL=' "$ENV_FILE" | cut -d= -f2- | tr -d '"')
    cat > /root/bpm-credentials.txt <<EOF
Backup Proxy Manager — Admin-Passwort zurückgesetzt
====================================================

URL:      ${APP_URL}/
Username: admin
Passwort: ${NEW_PW}

Reset-Zeitpunkt: $(date '+%Y-%m-%d %H:%M:%S %Z')

Bitte nach Login in der UI ändern und diese Datei löschen.
EOF
    chmod 0600 /root/bpm-credentials.txt
    chown root:root /root/bpm-credentials.txt

    echo "OK — neues Admin-Passwort:"
    echo "  ${NEW_PW}"
    echo "  (auch in /root/bpm-credentials.txt)"
}

cmd_status() {
    for svc in mariadb nginx php8.4-fpm supervisor cron iscsid; do
        printf "%-15s " "$svc"
        if systemctl is-active "$svc" >/dev/null 2>&1; then
            echo "active"
        else
            echo "INAKTIV"
        fi
    done
    echo
    if [ -x /usr/bin/supervisorctl ]; then
        supervisorctl status backup-manager-worker:* 2>&1 || true
    fi
}

cmd_artisan() {
    require_root
    cd "$BACKEND"
    shift
    artisan_as_www "$@"
}

cmd_edit_env() {
    require_root
    "${EDITOR:-vi}" "$ENV_FILE"
    chown root:www-data "$ENV_FILE"
    chmod 0640 "$ENV_FILE"
    cd "$BACKEND"
    artisan_as_www config:clear >/dev/null
    if [ -x /usr/bin/supervisorctl ]; then
        supervisorctl restart 'backup-manager-worker:*' >/dev/null 2>&1 || true
    fi
    echo "OK — .env aktualisiert, config geleert, worker neugestartet."
}

cmd_show_env() {
    require_root
    sed -E 's/^(APP_KEY|JWT_SECRET|DB_PASSWORD|MAIL_PASSWORD)=.*/\1=<redacted>/' "$ENV_FILE"
}

cmd_cert_info() {
    [ -f "$TLS_CERT" ] || { echo "Kein Cert unter $TLS_CERT"; exit 1; }
    echo "Datei:    $TLS_CERT"
    openssl x509 -in "$TLS_CERT" -noout \
        -subject -issuer -startdate -enddate \
        -ext subjectAltName 2>/dev/null
    echo
    echo "Fingerprint (SHA-256):"
    openssl x509 -in "$TLS_CERT" -noout -fingerprint -sha256 \
        | sed 's/^/  /'
}

cmd_install_cert() {
    require_root
    local NEW_CERT="${1:-}"
    local NEW_KEY="${2:-}"
    [ -f "$NEW_CERT" ] || { echo "Cert-Datei nicht lesbar: $NEW_CERT"; exit 1; }
    [ -f "$NEW_KEY"  ] || { echo "Key-Datei nicht lesbar: $NEW_KEY";  exit 1; }

    # Sanity: Cert + Key müssen zueinander passen — Modulus-Hashes vergleichen.
    local CERT_HASH KEY_HASH
    CERT_HASH=$(openssl x509 -in "$NEW_CERT" -noout -modulus 2>/dev/null \
        | openssl md5 | awk '{print $NF}')
    KEY_HASH=$(openssl rsa -in "$NEW_KEY" -noout -modulus 2>/dev/null \
        | openssl md5 | awk '{print $NF}')
    if [ -z "$CERT_HASH" ] || [ -z "$KEY_HASH" ] || [ "$CERT_HASH" != "$KEY_HASH" ]; then
        echo "Cert und Key passen nicht zusammen (Modulus-Mismatch)."
        echo "  cert: ${CERT_HASH:-?}"
        echo "  key:  ${KEY_HASH:-?}"
        exit 1
    fi

    install -d -o root -g root -m 0755 "$TLS_DIR"

    # Backup vor dem Überschreiben — falls jemand den Cert-Tausch
    # zurückrollen will.
    if [ -f "$TLS_CERT" ]; then
        cp -p "$TLS_CERT" "${TLS_CERT}.bak.$(date +%Y%m%d-%H%M%S)"
    fi
    if [ -f "$TLS_KEY" ]; then
        cp -p "$TLS_KEY" "${TLS_KEY}.bak.$(date +%Y%m%d-%H%M%S)"
    fi

    cp "$NEW_CERT" "$TLS_CERT"
    cp "$NEW_KEY"  "$TLS_KEY"
    chown root:root "$TLS_CERT" "$TLS_KEY"
    chmod 0644 "$TLS_CERT"
    chmod 0600 "$TLS_KEY"

    if ! nginx -t >/dev/null 2>&1; then
        echo "nginx -t schlägt fehl — Backup wird wiederhergestellt." >&2
        nginx -t
        # Letztes Backup wiederherstellen
        ls -1t "${TLS_CERT}.bak."* 2>/dev/null | head -1 | xargs -r -I{} cp -p {} "$TLS_CERT"
        ls -1t "${TLS_KEY}.bak."*  2>/dev/null | head -1 | xargs -r -I{} cp -p {} "$TLS_KEY"
        exit 1
    fi

    systemctl reload nginx
    echo "OK — neues Cert eingespielt, nginx reloaded."
    cmd_cert_info
}

cmd_regenerate_cert() {
    require_root

    if [ -f "$TLS_CERT" ]; then
        cp -p "$TLS_CERT" "${TLS_CERT}.bak.$(date +%Y%m%d-%H%M%S)"
    fi
    if [ -f "$TLS_KEY" ]; then
        cp -p "$TLS_KEY" "${TLS_KEY}.bak.$(date +%Y%m%d-%H%M%S)"
    fi
    rm -f "$TLS_CERT" "$TLS_KEY"

    # Generation-Funktion aus dem postinst inline duplizieren — bewusst, damit
    # bpm-setup nicht auf postinst-Code angewiesen ist.
    local FQDN SHORT IPS SAN_FILE DNS_IDX IP_IDX ip
    FQDN=$(hostname -f 2>/dev/null || hostname)
    SHORT=$(hostname -s 2>/dev/null || hostname)
    SAN_FILE=$(mktemp)
    cat > "$SAN_FILE" <<EOF
[req]
distinguished_name = dn
prompt = no
x509_extensions = v3_req
[dn]
O = Backup Proxy Manager
CN = ${FQDN}
[v3_req]
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt
[alt]
DNS.1 = ${FQDN}
EOF
    DNS_IDX=2
    if [ "$SHORT" != "$FQDN" ]; then
        echo "DNS.${DNS_IDX} = ${SHORT}" >> "$SAN_FILE"
        DNS_IDX=$((DNS_IDX + 1))
    fi
    echo "DNS.${DNS_IDX} = localhost" >> "$SAN_FILE"
    IP_IDX=1
    echo "IP.${IP_IDX} = 127.0.0.1" >> "$SAN_FILE"; IP_IDX=$((IP_IDX + 1))
    echo "IP.${IP_IDX} = ::1"       >> "$SAN_FILE"; IP_IDX=$((IP_IDX + 1))
    IPS=$(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E '^[0-9a-fA-F:.]+$' | head -8 || true)
    while IFS= read -r ip; do
        [ -n "$ip" ] || continue
        [ "$ip" = "127.0.0.1" ] && continue
        [ "$ip" = "::1" ] && continue
        echo "IP.${IP_IDX} = ${ip}" >> "$SAN_FILE"
        IP_IDX=$((IP_IDX + 1))
    done <<< "$IPS"

    install -d -o root -g root -m 0755 "$TLS_DIR"
    openssl req -x509 -newkey rsa:4096 -nodes -days 3650 \
        -keyout "$TLS_KEY" -out "$TLS_CERT" \
        -config "$SAN_FILE" -extensions v3_req >/dev/null 2>&1
    rm -f "$SAN_FILE"

    chown root:root "$TLS_CERT" "$TLS_KEY"
    chmod 0644 "$TLS_CERT"
    chmod 0600 "$TLS_KEY"

    nginx -t >/dev/null 2>&1 && systemctl reload nginx
    echo "OK — neues selbstsigniertes Cert erzeugt."
    cmd_cert_info
}

# ----- BPM Self-Backup (Sprint 9) ---------------------------------------

cmd_backup() {
    require_root
    cd "$BACKEND"
    artisan_as_www bpm:system-backup
}

cmd_restore() {
    require_root
    local ARCHIVE="${1:-}"
    [ -n "$ARCHIVE" ] || { echo "Usage: bpm-setup restore <datei.tar.gz.enc>"; exit 1; }
    [ -f "$ARCHIVE" ] || { echo "Archiv nicht gefunden: $ARCHIVE"; exit 1; }

    echo "WARNUNG: Restore überschreibt:"
    echo "  - die Datenbank backup_manager"
    echo "  - $ENV_FILE"
    echo "  - /var/lib/backup-manager/ssh-keys/"
    echo
    read -r -p "Mit 'JA' bestätigen, um fortzufahren: " CONFIRM
    [ "$CONFIRM" = "JA" ] || { echo "Abgebrochen."; exit 1; }

    # Passwort interaktiv ohne Echo lesen.
    echo -n "Backup-Passwort: "
    read -rs BPM_BACKUP_PW
    echo
    [ -n "$BPM_BACKUP_PW" ] || { echo "Kein Passwort eingegeben."; exit 1; }
    export BPM_BACKUP_PW

    local TS WORKDIR DECRYPTED
    TS=$(date +%Y%m%d-%H%M%S)
    WORKDIR=$(mktemp -d /tmp/bpm-restore-XXXXXX)
    DECRYPTED="$WORKDIR/bpm-restore.tar.gz"

    echo "→ Entschlüssele Archiv …"
    if ! openssl enc -d -aes-256-cbc -pbkdf2 \
            -in "$ARCHIVE" -out "$DECRYPTED" \
            -pass env:BPM_BACKUP_PW 2>/dev/null; then
        unset BPM_BACKUP_PW
        rm -rf "$WORKDIR"
        echo "Entschlüsseln fehlgeschlagen — falsches Passwort oder beschädigtes Archiv."
        exit 1
    fi
    unset BPM_BACKUP_PW

    echo "→ Entpacke Archiv …"
    if ! tar xzf "$DECRYPTED" -C "$WORKDIR"; then
        rm -rf "$WORKDIR"
        echo "tar fehlgeschlagen."
        exit 1
    fi

    [ -f "$WORKDIR/backup-manager.sql" ] || { rm -rf "$WORKDIR"; echo "backup-manager.sql fehlt im Archiv."; exit 1; }
    [ -f "$WORKDIR/.env" ]                || { rm -rf "$WORKDIR"; echo ".env fehlt im Archiv.";              exit 1; }

    # Backups vor dem Überschreiben — falls jemand zurückrollen will.
    if [ -f "$ENV_FILE" ]; then
        cp -p "$ENV_FILE" "${ENV_FILE}.bak.${TS}"
        echo "→ Aktuelle .env gesichert: ${ENV_FILE}.bak.${TS}"
    fi
    if [ -d /var/lib/backup-manager/ssh-keys ]; then
        tar czf "/var/lib/backup-manager/ssh-keys.bak.${TS}.tar.gz" \
            -C /var/lib/backup-manager ssh-keys 2>/dev/null || true
        echo "→ Aktuelle ssh-keys gesichert: /var/lib/backup-manager/ssh-keys.bak.${TS}.tar.gz"
    fi

    # Wir lesen DB-Credentials aus der ALTEN .env, nicht aus der neuen —
    # die Restore-DB-Verbindung nutzt das aktuelle root/debian.cnf-Setup
    # bzw. die vorhandenen Credentials, weil die restore-Maschine schon
    # einen MySQL-User für BPM hat.
    local DB_NAME DB_USER DB_PASS DB_HOST DB_PORT
    if [ -f "$ENV_FILE" ]; then
        DB_NAME=$(grep -E '^DB_DATABASE=' "$ENV_FILE" | cut -d= -f2- | tr -d '"')
        DB_USER=$(grep -E '^DB_USERNAME=' "$ENV_FILE" | cut -d= -f2- | tr -d '"')
        DB_PASS=$(grep -E '^DB_PASSWORD=' "$ENV_FILE" | cut -d= -f2- | tr -d '"')
        DB_HOST=$(grep -E '^DB_HOST='     "$ENV_FILE" | cut -d= -f2- | tr -d '"')
        DB_PORT=$(grep -E '^DB_PORT='     "$ENV_FILE" | cut -d= -f2- | tr -d '"')
    fi
    DB_NAME="${DB_NAME:-backup_manager}"
    DB_USER="${DB_USER:-bpm_user}"
    DB_HOST="${DB_HOST:-127.0.0.1}"
    DB_PORT="${DB_PORT:-3306}"

    echo "→ Spiele Datenbank ein (${DB_NAME})…"
    local MYSQL_DEFAULTS
    MYSQL_DEFAULTS=$(mktemp)
    chmod 0600 "$MYSQL_DEFAULTS"
    cat > "$MYSQL_DEFAULTS" <<EOF
[client]
host=${DB_HOST}
port=${DB_PORT}
user=${DB_USER}
password=${DB_PASS}
EOF
    if ! mysql --defaults-extra-file="$MYSQL_DEFAULTS" "$DB_NAME" < "$WORKDIR/backup-manager.sql"; then
        rm -f "$MYSQL_DEFAULTS"
        rm -rf "$WORKDIR"
        echo "DB-Restore fehlgeschlagen."
        exit 1
    fi
    rm -f "$MYSQL_DEFAULTS"

    echo "→ Spiele .env ein …"
    install -m 0640 -o root -g www-data "$WORKDIR/.env" "$ENV_FILE"

    # ⚠ Nach dem .env-Restore steht in DB_PASSWORD jetzt das ALTE Passwort
    # vom Quell-System. Der MariaDB-User auf DIESER Maschine hat aber das
    # NEUE Random-Passwort vom postinst dieser Installation. Ohne Sync
    # findet BPM die DB nicht mehr — wir gleichen den DB-User-Passwort an
    # den Wert aus der wiederhergestellten .env an.
    #
    # APP_KEY hingegen MUSS aus der alten .env kommen — die DB enthält
    # mit `Crypt::encryptString` verschlüsselte Felder (smb_password,
    # smtp_password, chap_secret, ldap.bind_password, system_settings.
    # backup_password). Mit fremdem APP_KEY würden alle diese Felder
    # silent zu null entschlüsseln.
    local NEW_DB_USER NEW_DB_PASS
    NEW_DB_USER=$(grep -E '^DB_USERNAME=' "$ENV_FILE" | cut -d= -f2- | tr -d '"')
    NEW_DB_PASS=$(grep -E '^DB_PASSWORD=' "$ENV_FILE" | cut -d= -f2- | tr -d '"')
    NEW_DB_USER="${NEW_DB_USER:-bpm_user}"

    if [ -n "$NEW_DB_PASS" ]; then
        echo "→ Synchronisiere MariaDB-User-Passwort mit wiederhergestellter .env …"
        local MYSQL_ROOT
        MYSQL_ROOT=$(mysql_root_cmd)
        if [ -z "$MYSQL_ROOT" ]; then
            echo "WARNUNG: Kein MariaDB-Root-Zugriff über debian.cnf oder unix_socket."
            echo "         Bitte manuell ausführen:"
            echo "           ALTER USER '${NEW_DB_USER}'@'localhost' IDENTIFIED BY '<DB_PASSWORD aus .env>';"
            echo "           FLUSH PRIVILEGES;"
        else
            $MYSQL_ROOT -e "ALTER USER '${NEW_DB_USER}'@'localhost' IDENTIFIED BY '${NEW_DB_PASS}'; FLUSH PRIVILEGES;" \
                && echo "  OK — '${NEW_DB_USER}'@'localhost' angeglichen." \
                || echo "  WARNUNG: ALTER USER fehlgeschlagen — bitte manuell prüfen."
        fi
    fi

    echo "→ Spiele ssh-keys ein …"
    install -d -o silvester -g www-data -m 0770 /var/lib/backup-manager/ssh-keys 2>/dev/null \
        || install -d -o root -g www-data -m 0770 /var/lib/backup-manager/ssh-keys
    if [ -d "$WORKDIR/ssh-keys" ]; then
        # cp -a erhält Permissions; Trailing /. kopiert Inhalt, nicht das Dir.
        cp -a "$WORKDIR/ssh-keys/." /var/lib/backup-manager/ssh-keys/
    fi

    rm -rf "$WORKDIR"

    echo "→ Räume Caches auf, starte Worker neu …"
    cd "$BACKEND"
    artisan_as_www config:clear >/dev/null 2>&1 || true
    artisan_as_www route:clear >/dev/null 2>&1 || true
    artisan_as_www view:clear >/dev/null 2>&1 || true
    if [ -x /usr/bin/supervisorctl ]; then
        supervisorctl restart 'backup-manager-worker:*' >/dev/null 2>&1 || true
    fi

    echo
    echo "Restore abgeschlossen. BPM ist wiederhergestellt."
}

case "${1:-}" in
    reset-admin-password) cmd_reset_admin_password ;;
    status)               cmd_status ;;
    artisan)              cmd_artisan "$@" ;;
    edit-env)             cmd_edit_env ;;
    show-env)             cmd_show_env ;;
    cert-info)            cmd_cert_info ;;
    install-cert)         shift; cmd_install_cert "$@" ;;
    regenerate-cert)      cmd_regenerate_cert ;;
    backup)               cmd_backup ;;
    restore)              shift; cmd_restore "$@" ;;
    ""|-h|--help|help)    usage ;;
    *)                    echo "Unbekannter Befehl: $1"; usage ;;
esac
