#!/usr/bin/env bash
# Legt die Theme- und Bundle-Dateien im Object Storage komprimiert und mit
# langer Gültigkeit ab. Muss nach jedem `theme:compile` / `assets:install`
# laufen, weil Shopware die Dateien ohne Kopfzeilen hochlädt.
#
# Warum nötig: Hetzner Object Storage komprimiert nicht selbst und setzt kein
# Cache-Control. Ohne diesen Schritt lädt jeder Besucher die 657 KB große
# all.css unkomprimiert und fragt sie bei jedem Aufruf neu an.
# Mit Content-Encoding: gzip sind es rund 100 KB, und mit "immutable" holt der
# Browser sie genau einmal (die Adressen enthalten Theme-Hash und Zeitstempel,
# ändern sich also bei jedem Build).
#
# Aufruf: assets-optimieren.sh [--nur-pruefen|--nur-assets]
#
# --nur-assets laesst die Medien aus. Die Medien-Phase setzt die Kopfzeile
# einzeln je Objekt; bei 29.579 Dateien dauert das Stunden. Nach einem
# Plugin-Rollout sind aber nur Theme- und Bundle-Dateien neu - dort auch die
# Medien anzufassen hat am 25.09.2026 einen Rollout elf Minuten haengen
# lassen, bis jemand nachsah.
set -euo pipefail

# Nur ein Lauf gleichzeitig. Ohne diese Sperre überholen sich zwei Läufe:
# der eine lädt eine Datei noch unkomprimiert, der andere komprimiert sie
# zwischenzeitlich – Ergebnis ist eine DOPPELT komprimierte Datei, die kein
# Browser lesen kann (am 21.09.2026 genau so passiert, 154 Dateien betroffen,
# darunter die halbe Administration).
exec 9>/var/lock/riccardo-assets.lock
if ! flock -n 9; then
    echo "Ein anderer Lauf ist noch aktiv – abgebrochen."
    exit 0
fi

CHECK="${1:-}"
NUR_ASSETS=0
[ "$CHECK" = "--nur-assets" ] && { NUR_ASSETS=1; CHECK=""; }
: "${S3_ENDPOINT:=https://nbg1.your-objectstorage.com}"
[ -f /etc/riccardo-backup.env ] && . /etc/riccardo-backup.env
[ -f /var/www/riccardo/.env.local ] && eval "$(grep -E '^S3_(KEY|SECRET|BUCKET_PUBLIC|ENDPOINT|REGION)=' /var/www/riccardo/.env.local)"

: "${S3_KEY:?S3_KEY fehlt}"; : "${S3_SECRET:?S3_SECRET fehlt}"; : "${S3_BUCKET_PUBLIC:?Bucket fehlt}"
export AWS_ACCESS_KEY_ID="$S3_KEY" AWS_SECRET_ACCESS_KEY="$S3_SECRET" AWS_DEFAULT_REGION="${S3_REGION:-nbg1}"
AWS=(aws --endpoint-url "$S3_ENDPOINT")

CACHE="public, max-age=31536000, immutable"

# Nur Dateien der letzten TAGE Tage anfassen. TAGE=0 bedeutet: alles.
# Der nächtliche Lauf nimmt 2 Tage, ein vollständiger Durchgang TAGE=3650.
TAGE="${TAGE:-2}"
if [ "$TAGE" = "0" ]; then SEIT="0000-00-00"; else SEIT="$(date -u -d "-$TAGE days" +%Y-%m-%d)"; fi
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT

typ() {
    case "$1" in
        *.css) echo "text/css";;
        *.js)  echo "application/javascript";;
        *.json) echo "application/json";;
        *.svg) echo "image/svg+xml";;
        *.map) echo "application/json";;
        *) echo "application/octet-stream";;
    esac
}

gesamt=0; behandelt=0; uebersprungen=0
for prefix in theme asset; do
    while read -r schluessel; do
        [ -n "$schluessel" ] || continue
        gesamt=$((gesamt+1))

        # Schon komprimiert? Zwei Prüfungen, weil der Kopf allein nicht reicht.
        vorhanden=$("${AWS[@]}" s3api head-object --bucket "$S3_BUCKET_PUBLIC" --key "$schluessel" \
                    --query 'ContentEncoding' --output text 2>/dev/null || echo None)
        if [ "$vorhanden" = "gzip" ]; then
            uebersprungen=$((uebersprungen+1))
            continue
        fi

        if [ "$CHECK" = "--nur-pruefen" ]; then
            echo "  würde komprimieren: $schluessel"
            behandelt=$((behandelt+1))
            continue
        fi

        datei="$TMP/datei"
        "${AWS[@]}" s3 cp "s3://$S3_BUCKET_PUBLIC/$schluessel" "$datei" --only-show-errors

        # Zweite Sicherung: fängt der Inhalt mit der gzip-Kennung 1f8b an, ist
        # die Datei schon komprimiert – dann NICHT erneut komprimieren, egal
        # was der Kopf sagt.
        if [ "$(head -c 2 "$datei" | xxd -p)" = "1f8b" ]; then
            rm -f "$datei"
            uebersprungen=$((uebersprungen+1))
            continue
        fi

        gzip -9 -c "$datei" > "$datei.gz"
        "${AWS[@]}" s3 cp "$datei.gz" "s3://$S3_BUCKET_PUBLIC/$schluessel" \
            --content-encoding gzip --content-type "$(typ "$schluessel")" \
            --cache-control "$CACHE" --only-show-errors
        rm -f "$datei" "$datei.gz"
        behandelt=$((behandelt+1))
    done < <("${AWS[@]}" s3 ls --recursive "s3://$S3_BUCKET_PUBLIC/$prefix/" 2>/dev/null \
             | awk -v seit="$SEIT" '$1 >= seit { $1=""; $2=""; $3=""; sub(/^   /, ""); print }' \
             | grep -E '\.(css|js|json|svg|map)$')
done

if [ "$NUR_ASSETS" = 1 ]; then
    echo "$(date '+%Y-%m-%d %H:%M') Theme/Assets geprüft: $gesamt, behandelt: $behandelt; Medien ausgelassen (--nur-assets)"
    exit 0
fi

# ---------------------------------------------------------------- Medien
# Bilder werden NICHT komprimiert (sind schon komprimiert), brauchen aber
# Cache-Control. Shopware kann das beim Hochladen nicht mitgeben: der
# S3-Adapter wird ohne Schreiboptionen erzeugt.
#
# Kein head-object je Datei: bei über 25.000 Objekten dauert das Stunden
# (der Lauf vom 22.09. hing deshalb nach fast fünf Stunden noch). Stattdessen
# filtert awk über das Änderungsdatum aus der Liste, und copy-object setzt die
# Kopfzeile ohne Rückfrage – das ist ohnehin wiederholbar.
medien=0

for prefix in public/media public/thumbnail; do
    while read -r schluessel; do
        [ -n "$schluessel" ] || continue

        if [ "$CHECK" = "--nur-pruefen" ]; then
            echo "  würde Cache-Control setzen: $schluessel"
        else
            "${AWS[@]}" s3api copy-object --bucket "$S3_BUCKET_PUBLIC" --key "$schluessel" \
                --copy-source "$S3_BUCKET_PUBLIC/$schluessel" --metadata-directive REPLACE \
                --cache-control "$CACHE" --acl public-read >/dev/null 2>&1 || true
        fi
        medien=$((medien+1))
    done < <("${AWS[@]}" s3 ls --recursive "s3://$S3_BUCKET_PUBLIC/$prefix/" 2>/dev/null \
             | awk -v seit="$SEIT" '$1 >= seit { $1=""; $2=""; $3=""; sub(/^   /, ""); print }')
done

echo "$(date '+%Y-%m-%d %H:%M') Theme/Assets geprüft: $gesamt, behandelt: $behandelt, schon fertig: $uebersprungen; Medien mit neuer Gültigkeit: $medien (letzte $TAGE Tage)"
