#!/usr/bin/env bash
# build-and-deploy.sh — PIR-Server auf Hetzner bauen und deployen
#
# Voraussetzungen (auf dem Server):
#   - /srv/pir-build/pir-service-example/   → geklont
#   - /srv/pir-build/swift-homomorphic-encryption/ → geklont
#   - /srv/pir-server/data/   → existiert
#   - /srv/pir-server/config/ → existiert
#   - /etc/environment enthält INFISICAL_CLIENT_ID / INFISICAL_CLIENT_SECRET
#
# Was dieses Script macht:
#   1. Infisical-Token holen (Universal Auth)
#   2. PIR_AUTH_TOKEN aus Infisical staging lesen
#   3. service-config.json mit realem Token nach /srv/pir-server/config/ schreiben
#   3b. Quell-Patches auf pir-service-example anwenden (patches/*.patch)
#   4. Docker-Image bauen (Multi-Stage, ~20-30 Min wegen Swift-Compile)
#   5. Test-input.txtpb generieren falls keine echte DB vorhanden
#   6. PIRProcessDatabase ausführen (innerhalb Container)
#   7. Container starten / neu starten (inkl. PIR_ISSUER_REQUEST_URI env)

set -euo pipefail

BUILD_DIR="/srv/pir-build"
DATA_DIR="/srv/pir-server/data"
CONFIG_DIR="/srv/pir-server/config"
IMAGE_NAME="pir-service-staging"
CONTAINER_NAME="pir-service-staging"
PORT="8090"
INFISICAL_PROJECT_ID="14b11b35-ef59-4b8a-a16b-398f0cc3ad93"
PROCESS_CONFIG_SRC="$(dirname "$0")/process-config.json"
PATCH_DIR="$(dirname "$0")/patches"
PIR_SERVICE_SRC="${BUILD_DIR}/pir-service-example"
# Public-URL des PIR-Servers. issuer-request-uri MUSS absolut sein (RFC 9578 §6),
# sonst kann der NEURLFilter-Client keinen Privacy-Pass-Token holen.
PIR_PUBLIC_URL="https://pir.staging.rebreak.org"
PIR_ISSUER_REQUEST_URI="${PIR_PUBLIC_URL}/issue"

log() { echo "[pir-deploy] $(date '+%H:%M:%S') $*"; }
log_err() { echo "[pir-deploy:err] $(date '+%H:%M:%S') $*" >&2; }

log "=== PIR-Server Deploy gestartet ==="

# 0. Environment laden
if [[ -f /etc/environment ]]; then
    export $(grep -v "^#" /etc/environment | grep -v "^$" | xargs)
fi

# 1. Infisical-Token holen
log "Step 1: Infisical Universal-Auth Login..."
INFISICAL_TOKEN=$(infisical login \
    --method=universal-auth \
    --client-id="${INFISICAL_CLIENT_ID}" \
    --client-secret="${INFISICAL_CLIENT_SECRET}" \
    --silent --plain 2>/dev/null)

if [[ -z "$INFISICAL_TOKEN" ]]; then
    log_err "Infisical login fehlgeschlagen"
    exit 1
fi
log "Infisical Login ok"

# 2. PIR_AUTH_TOKEN holen
log "Step 2: PIR_AUTH_TOKEN aus Infisical (staging) holen..."
PIR_AUTH_TOKEN=$(infisical secrets get PIR_AUTH_TOKEN \
    --env=staging \
    --projectId="${INFISICAL_PROJECT_ID}" \
    --token="${INFISICAL_TOKEN}" \
    --plain 2>/dev/null)

if [[ -z "$PIR_AUTH_TOKEN" ]]; then
    log_err "PIR_AUTH_TOKEN nicht in Infisical gefunden (env=staging, project=${INFISICAL_PROJECT_ID})"
    log_err "Bitte via Infisical-Dashboard anlegen: Key=PIR_AUTH_TOKEN, Env=staging"
    exit 1
fi
log "PIR_AUTH_TOKEN geladen (${#PIR_AUTH_TOKEN} Zeichen)"

# 3. service-config.json schreiben (Token inline, nicht in Datei committed)
log "Step 3: service-config.json nach ${CONFIG_DIR}/ schreiben..."

# Shard-Count aus vorhandenen url-N.bin-Artifacts ableiten.
# Bugfix: vorher fix `SHARD_COUNT=1` — bei Re-Runs ohne PIRProcessDatabase (Step 6
# wird übersprungen wenn Artifacts existieren) regressierte das einen 4-Shard-Server
# fälschlich auf shardCount=1.
SHARD_COUNT=$(ls "${DATA_DIR}"/url-*.bin 2>/dev/null | wc -l | tr -d ' ')
[[ "$SHARD_COUNT" -lt 1 ]] && SHARD_COUNT=1
log "Shard-Count aus ${DATA_DIR}: ${SHARD_COUNT}"

cat > "${CONFIG_DIR}/service-config.json" << EOF
{
  "users": [
    {
      "tier": "tier1",
      "tokens": ["${PIR_AUTH_TOKEN}"]
    }
  ],
  "usecases": [
    {
      "name": "org.rebreak.app.url.filtering",
      "fileStem": "/data/url",
      "shardCount": ${SHARD_COUNT}
    }
  ]
}
EOF
log "service-config.json geschrieben"

# 3b. Quell-Patches auf pir-service-example anwenden (idempotent)
log "Step 3b: Quell-Patches anwenden..."
if [[ -d "$PATCH_DIR" ]] && compgen -G "${PATCH_DIR}/*.patch" > /dev/null; then
    # Clone auf sauberen Stand bringen, dann alle Patches anwenden
    git -C "$PIR_SERVICE_SRC" checkout -- . 2>/dev/null || true
    for p in "${PATCH_DIR}"/*.patch; do
        log "  apply $(basename "$p")"
        git -C "$PIR_SERVICE_SRC" apply "$p"
    done
else
    log "  keine Patches gefunden — übersprungen"
fi

# 4. Docker-Image bauen
log "Step 4: Docker-Image bauen (Kontext: ${BUILD_DIR})..."
log "WARNUNG: Swift-Build dauert ~20-30 Min, RAM-Nutzung hoch (~2-3 GB). Swap wird benötigt."

docker build \
    -t "${IMAGE_NAME}:latest" \
    -f "$(dirname "$0")/Dockerfile" \
    "${BUILD_DIR}"

log "Docker-Image gebaut: ${IMAGE_NAME}:latest"

# 5. Test-input.txtpb generieren falls keine echte DB
log "Step 5: Input-DB prüfen..."
bash "$(dirname "$0")/gen-test-input.sh" "${DATA_DIR}/input.txtpb"

# 6. PIRProcessDatabase ausführen (im Container, mounted /data)
log "Step 6: PIRProcessDatabase ausführen..."

# Prüfe ob DB-Artifacts schon vorhanden (von vorherigem Run)
if [[ -f "${DATA_DIR}/url-0.bin" ]] && [[ -f "${DATA_DIR}/url-0.params.txtpb" ]]; then
    log "DB-Artifacts bereits vorhanden (url-0.bin + url-0.params.txtpb) — überspringe PIRProcessDatabase"
    log "Für Rebuild: rm ${DATA_DIR}/url-0.bin ${DATA_DIR}/url-0.params.txtpb && re-run"
else
    # process-config.json in data-dir kopieren (PIRProcessDatabase braucht absolute Pfade)
    cp "${PROCESS_CONFIG_SRC}" "${DATA_DIR}/process-config.json"

    docker run --rm \
        -v "${DATA_DIR}:/data" \
        "${IMAGE_NAME}:latest" \
        PIRProcessDatabase /data/process-config.json

    log "PIRProcessDatabase abgeschlossen"

    # Shard-Count aus Output ermitteln
    SHARD_COUNT=$(ls "${DATA_DIR}"/url-*.bin 2>/dev/null | wc -l)
    log "Erkannte Shards: ${SHARD_COUNT}"

    # service-config.json mit korrektem shardCount neu schreiben
    if [[ "$SHARD_COUNT" -gt 1 ]]; then
        log "Mehr als 1 Shard — service-config.json updaten..."
        cat > "${CONFIG_DIR}/service-config.json" << EOF
{
  "users": [
    {
      "tier": "tier1",
      "tokens": ["${PIR_AUTH_TOKEN}"]
    }
  ],
  "usecases": [
    {
      "name": "org.rebreak.app.url.filtering",
      "fileStem": "/data/url",
      "shardCount": ${SHARD_COUNT}
    }
  ]
}
EOF
        log "service-config.json mit shardCount=${SHARD_COUNT} aktualisiert"
    fi
fi

# 7. Container starten / neu starten
log "Step 7: Container starten..."

# Alten Container stoppen falls vorhanden
if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
    log "Stoppe alten Container ${CONTAINER_NAME}..."
    docker stop "${CONTAINER_NAME}" 2>/dev/null || true
    docker rm "${CONTAINER_NAME}" 2>/dev/null || true
fi

docker run -d \
    --name "${CONTAINER_NAME}" \
    --restart unless-stopped \
    -p "127.0.0.1:${PORT}:8090" \
    -v "${DATA_DIR}:/data:ro" \
    -v "${CONFIG_DIR}:/config:ro" \
    -e "PIR_ISSUER_REQUEST_URI=${PIR_ISSUER_REQUEST_URI}" \
    "${IMAGE_NAME}:latest"

log "Container ${CONTAINER_NAME} gestartet auf 127.0.0.1:${PORT}"

# 8. Health-Check
log "Step 8: Health-Check (warte 5s auf Start)..."
sleep 5

# Issuer-Directory muss erreichbar sein UND eine ABSOLUTE issuer-request-uri liefern.
# (PrivateToken-Auth lässt sich ohne echten Privacy-Pass-Token nicht testen — der
#  PIR_AUTH_TOKEN ist nur der User-Token für /issue, nicht der /config-Token.)
DIRECTORY=$(curl -s "http://127.0.0.1:${PORT}/.well-known/private-token-issuer-directory" 2>/dev/null || echo "")

if echo "$DIRECTORY" | grep -q '"issuer-request-uri":"https://'; then
    log "Health-Check OK — Issuer-Directory liefert absolute issuer-request-uri"
    log "=== Deploy erfolgreich abgeschlossen ==="
else
    log_err "Health-Check fehlgeschlagen — Issuer-Directory unerwartet:"
    log_err "$DIRECTORY"
    log_err "Container-Logs:"
    docker logs "${CONTAINER_NAME}" --tail 30
    log_err "Deploy FEHLGESCHLAGEN — manueller Check erforderlich"
    exit 1
fi
