#!/bin/bash
# backup-app.sh — Strukturiertes Backup-Skript für K3s DMZ Apps
# Verwendung: backup-app.sh <app> <namespace> [--db-dump]
# Beispiel:   backup-app.sh homarr homarr
#             backup-app.sh zammad zammad --db-dump
#
# Mandanten-Mapping:
#   platform: k3s-controlplane (platform-cluster), infisical (platform-infisical)
#   pidoka:   homarr, matomo, matomo-db, mautic, n8n, suitecrm, zammad
#   cattaro:  (reserviert)
# Zielpfad: tenants/<mandant>/prod/<app>/
#             backup-app.sh matomo pidoka-hf-matomo --db-dump
#
# Voraussetzungen:
#   - INFISICAL_CLIENT_ID und INFISICAL_AUTH_KEY als Env-Variablen
#   - age unter /home/node/shared/tools/age
#   - SSH-Key für DMZ Control-Plane unter ~/.ssh/kube-bot-it15-ed25519

set -euo pipefail

APP="${1:-}"
NAMESPACE="${2:-}"
DB_DUMP="${3:-}"

if [ -z "$APP" ] || [ -z "$NAMESPACE" ]; then
  echo "FEHLER: Verwendung: $0 <app> <namespace> [--db-dump]" >&2
  exit 1
fi

TIMESTAMP=$(date -u +%Y%m%dT%H%M%SZ)
WORKDIR="/home/node/.openclaw/workspace/backups/${APP}-${TIMESTAMP}"
LOG="$WORKDIR/backup.log"
STATUS_FILE="$WORKDIR/backup.status"
SSH_KEY="$HOME/.ssh/kube-bot-it15-ed25519"
K3S_NODE="kube-bot@192.168.20.40"
K3S="sudo kubectl --kubeconfig=/etc/rancher/k3s/k3s.yaml"
AGE_BIN="/home/node/shared/tools/age"
INFISICAL_URL="http://192.168.1.121:8088"
PROJECT_ID="ba0a79d9-32fe-474c-be2f-88384a3d9749"

mkdir -p "$WORKDIR/metadata" "$WORKDIR/data"

log() { echo "[$(date -u +%H:%M:%SZ)] $*" | tee -a "$LOG"; }
fail() { log "FEHLER: $*"; echo "FAILED" > "$STATUS_FILE"; exit 1; }

log "=== Backup START: $APP / $NAMESPACE / $TIMESTAMP ==="
echo "RUNNING" > "$STATUS_FILE"

# --- Preflight ---
log "Preflight: SSH-Verbindung prüfen"
ssh -i "$SSH_KEY" -o ConnectTimeout=8 -o StrictHostKeyChecking=no "$K3S_NODE" "echo SSH_OK" >> "$LOG" 2>&1 || fail "SSH zu $K3S_NODE nicht möglich"

# --- Infisical Login ---
log "Infisical Login"
TOKEN=$(curl -s -X POST "$INFISICAL_URL/api/v1/auth/universal-auth/login" \
  -H "Content-Type: application/json" \
  -d "{\"clientId\":\"$INFISICAL_CLIENT_ID\",\"clientSecret\":\"$INFISICAL_AUTH_KEY\"}" \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('accessToken','ERROR'))")
[ "$TOKEN" = "ERROR" ] && fail "Infisical Login fehlgeschlagen"
log "Infisical Login OK"

get_secret() {
  curl -s -X GET "$INFISICAL_URL/api/v3/secrets/raw/$1?workspaceId=$PROJECT_ID&environment=prod&secretPath=/" \
    -H "Authorization: Bearer $TOKEN" \
    | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('secret',{}).get('secretValue',''))"
}

# Age Recipient für diese App
AGE_SECRET_NAME="BACKUP_$(echo "$APP" | tr '[:lower:]' '[:upper:]')_AGE_RECIPIENT"
AGE_RECIPIENT=$(get_secret "$AGE_SECRET_NAME") || fail "Age-Recipient $AGE_SECRET_NAME nicht gefunden"
[ -z "$AGE_RECIPIENT" ] && fail "Age-Recipient leer"
log "Age-Recipient geladen: ${AGE_RECIPIENT:0:20}..."

# --- Metadaten ---
log "Metadaten sammeln"
ssh -i "$SSH_KEY" -o ConnectTimeout=10 -o StrictHostKeyChecking=no "$K3S_NODE" \
  "$K3S get deployment,service,pvc,configmap -n $NAMESPACE -o yaml 2>/dev/null" \
  | python3 -c "
import sys, yaml, json
docs = list(yaml.safe_load_all(sys.stdin))
output = []
for d in docs:
    if not d: continue
    if d.get('kind') == 'Secret':
        d = {'kind':'Secret','metadata':{'name':d.get('metadata',{}).get('name'),'namespace':d.get('metadata',{}).get('namespace')},'type':d.get('type'),'_note':'values_redacted'}
    output.append(d)
print(json.dumps(output, default=str))
" > "$WORKDIR/metadata/k8s-resources.json" 2>/dev/null || log "WARN: Metadaten teilweise nicht abrufbar"

ssh -i "$SSH_KEY" -o ConnectTimeout=10 -o StrictHostKeyChecking=no "$K3S_NODE" \
  "$K3S get pvc -n $NAMESPACE --no-headers" > "$WORKDIR/metadata/pvcs.txt" 2>/dev/null

log "Metadaten OK"

# --- DB-Dump (optional) ---
if [ "$DB_DUMP" = "--db-dump" ]; then
  log "DB-Dump angefordert — muss app-spezifisch implementiert werden"
  # Placeholder: wird pro App überschrieben
  echo "DB-Dump für $APP nicht implementiert in dieser Version" > "$WORKDIR/data/db-dump.txt"
fi

# --- Datei-Dump aus laufenden Pods ---
log "Datei-Dump aus Pods"
POD=$(ssh -i "$SSH_KEY" -o ConnectTimeout=10 -o StrictHostKeyChecking=no "$K3S_NODE" \
  "$K3S get pods -n $NAMESPACE --no-headers -o custom-columns=NAME:.metadata.name | head -1" 2>/dev/null | tr -d '[:space:]')
log "Pod: $POD"

# --- Artefakte packen und verschlüsseln ---
pack_and_encrypt() {
  local KIND="$1"
  local TARFILE="${WORKDIR}/${TIMESTAMP}__dmz-k3s__pidoka__${APP}__${KIND}.tar.gz"
  tar czf "$TARFILE" -C "$WORKDIR" "$KIND"/ 2>/dev/null
  local SHA=$(sha256sum "$TARFILE" | cut -d' ' -f1)
  echo "$SHA  $(basename "$TARFILE")" > "${TARFILE}.sha256"
  "$AGE_BIN" -r "$AGE_RECIPIENT" -o "${TARFILE}.age" "$TARFILE"
  local AGE_SHA=$(sha256sum "${TARFILE}.age" | cut -d' ' -f1)
  echo "$AGE_SHA  $(basename "${TARFILE}.age")" > "${TARFILE}.age.sha256"
  log "$KIND: tar=$SHA age=$AGE_SHA"
}

pack_and_encrypt metadata
log "Metadaten-Artefakt OK"

# --- Push auf dmz-backup ---
log "Push auf dmz-backup"
DMZ_HOST=$(get_secret DMZ_BACKUP_HOST)
DMZ_USER=$(get_secret DMZ_BACKUP_SSH_USER)
DMZ_PRIVKEY=$(get_secret KUBE_BOT_DMZ_BACKUP_SSH_PRIVATE_KEY)
TARGET_BASE="/srv/dmz-backup/k3s-dmz/tenants/pidoka/prod/${APP}"

TMPKEY=$(mktemp)
printf '%s\n' "$DMZ_PRIVKEY" > "$TMPKEY"
chmod 600 "$TMPKEY"

# Preflight: Schreibbarkeit prüfen
ssh -i "$TMPKEY" -o StrictHostKeyChecking=no "$DMZ_USER@$DMZ_HOST" \
  "mkdir -p ${TARGET_BASE}/${TIMESTAMP} && echo DMZ_WRITE_OK" >> "$LOG" 2>&1 || { rm -f "$TMPKEY"; fail "dmz-backup Schreibzugriff fehlgeschlagen"; }

for AGE_FILE in "$WORKDIR"/*.age "$WORKDIR"/*.age.sha256; do
  [ -f "$AGE_FILE" ] || continue
  scp -i "$TMPKEY" -o StrictHostKeyChecking=no "$AGE_FILE" "$DMZ_USER@$DMZ_HOST:${TARGET_BASE}/${TIMESTAMP}/" >> "$LOG" 2>&1 \
    || { rm -f "$TMPKEY"; fail "SCP fehlgeschlagen: $AGE_FILE"; }
done

# Remote SHA256-Check
REMOTE_CHECK=$(ssh -i "$TMPKEY" -o StrictHostKeyChecking=no "$DMZ_USER@$DMZ_HOST" \
  "cd ${TARGET_BASE}/${TIMESTAMP} && sha256sum -c *.sha256 2>&1")
echo "$REMOTE_CHECK" >> "$LOG"
echo "$REMOTE_CHECK" | grep -v ": OK" | grep -v "^$" && { rm -f "$TMPKEY"; fail "Remote SHA256-Check fehlgeschlagen"; }

rm -f "$TMPKEY"
log "Push und Remote-Verify OK"

echo "OK" > "$STATUS_FILE"
log "=== Backup FERTIG: $APP $TIMESTAMP ==="
echo ""
echo "Status: OK"
echo "Timestamp: $TIMESTAMP"
echo "Ziel: ${TARGET_BASE}/${TIMESTAMP}/"
