#!/bin/bash
# Aria Arsenal Sync (KAR-61) — versioniert .claude-Customization nach
# KADiCon/aria-arsenal (privates GitHub-Repo).
#
# Scope:
#   /root/.claude/skills/         -> arsenal/skills/
#   /root/.claude/hooks/          -> arsenal/hooks/
#   /root/.claude/agents/         -> arsenal/agents/
#   /root/.claude/statusline-command.sh -> arsenal/statusline-command.sh
#   /root/.claude/settings.json   -> arsenal/settings.json   (jq-scrubbed)
#   /root/.claude/mcp.json        -> arsenal/mcp.json        (jq-scrubbed)
#
# Out-of-scope (lokale State und Caches, nie ins Arsenal):
#   backups/ cache/ channels/ file-history/ history.jsonl
#   mcp-needs-auth-cache.json plugins/ projects/ sessions/
#   session-env/ shell-snapshots/ tasks/ security_warnings_*
#   settings.json.bak-* current_session_id
#
# Token-Scrubbing: jq filtert env-Werte und String-Werte die wie Tokens
# aussehen. Fallback: vor dem Commit grep-scan nach bekannten Prefixes;
# wenn was matcht, abort.
#
# Aufruf: bash aria-arsenal-sync.sh [reason]
#   reason: Commit-Nachricht-Suffix, default "auto-sync"
#
# Exit-Codes:
#   0  OK (commit-leer oder erfolgreich gepusht)
#   2  Token-Scan hat Match gefunden, commit abgebrochen
#   3  rsync / git Fehler

set -euo pipefail

ARSENAL="${ARIA_ARSENAL:-/root/aria-arsenal}"
CLAUDE_DIR="${CLAUDE_DIR:-/root/.claude}"
REASON="${1:-auto-sync}"
LOG="/var/log/aria-arsenal-sync.log"

log() { printf '[%s] %s\n' "$(date -Is)" "$*" | tee -a "$LOG" >&2; }

if [ ! -d "$ARSENAL/.git" ]; then
    log "ERROR: $ARSENAL is not a git repo"
    exit 3
fi

cd "$ARSENAL"

# --- 1. rsync skills/hooks/agents -----------------------------------------
RSYNC_OPTS=(-a --delete
    --exclude='__pycache__'
    --exclude='*.pyc'
    --exclude='.DS_Store'
    --exclude='*.log'
    --exclude='*.tmp'
    --exclude='.git/'
    --exclude='.git'
    --exclude='node_modules/'
    --exclude='venv/'
    # Test-Fixtures enthalten oft Fake-Tokens (ghp_abc..., sk-abc...) die
    # vom Layer-2 Token-Scan korrekt erfasst werden. Tests gehoeren ins
    # Upstream-Skill-Repo, nicht ins Arsenal.
    --exclude='*.test.*'
    --exclude='test/'
    --exclude='tests/'
    --exclude='__tests__/'
    --exclude='*.spec.*'
    # Eigenstaendige Skill-Repos (haben eigenes .git, Setup-Beispiele
    # mit echten Token-Strings in Docs). Diese sind upstream-klonbar
    # und werden im README mit Install-Anleitung dokumentiert.
    # Pattern ohne fuehrendes skills/ — rsync arbeitet relativ zum source-dir.
    --exclude='gstack/'
)

for sub in skills hooks agents; do
    src="$CLAUDE_DIR/$sub"
    if [ -d "$src" ]; then
        mkdir -p "$ARSENAL/$sub"
        rsync "${RSYNC_OPTS[@]}" "$src/" "$ARSENAL/$sub/" 2>>"$LOG"
    fi
done

# --- 2. single files -------------------------------------------------------
if [ -f "$CLAUDE_DIR/statusline-command.sh" ]; then
    cp "$CLAUDE_DIR/statusline-command.sh" "$ARSENAL/statusline-command.sh"
fi

# --- 3. settings.json + mcp.json with jq-scrubber --------------------------
scrub_json() {
    # Walks the JSON tree, replacing any string that looks token-shaped
    # with "REDACTED". Patterns: ghp_*, github_pat_*, sk_*, sb_*, xoxb-*,
    # bearer tokens, JWTs (3 base64-segments).
    jq '
        def is_secret(s):
            (s | type) == "string"
            and (
                (s | test("^ghp_[A-Za-z0-9_-]{20,}$"))
                or (s | test("^github_pat_[A-Za-z0-9_-]{30,}$"))
                or (s | test("^gho_[A-Za-z0-9_-]{20,}$"))
                or (s | test("^sk_[A-Za-z0-9_-]{20,}$"))
                or (s | test("^sk-[A-Za-z0-9_-]{20,}$"))
                or (s | test("^sb_(secret|publishable)_[A-Za-z0-9_-]{20,}$"))
                or (s | test("^xox[bp]-[A-Za-z0-9-]{10,}$"))
                or (s | test("^eyJ[A-Za-z0-9_-]+\\.eyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$"))
                or (s | test("(?i)^(bearer\\s|token\\s)"))
            );
        walk(if is_secret(.) then "REDACTED" else . end)
    '
}

if [ -f "$CLAUDE_DIR/settings.json" ]; then
    scrub_json < "$CLAUDE_DIR/settings.json" > "$ARSENAL/settings.json"
fi

if [ -f "$CLAUDE_DIR/mcp.json" ]; then
    scrub_json < "$CLAUDE_DIR/mcp.json" > "$ARSENAL/mcp.json"
fi

# --- 4. defense-in-depth: scan staged content for known token prefixes ----
# Wenn der jq-Scrubber etwas verpasst hat, hier abbrechen.
git add -A
STAGED_LEAK=$(
    git diff --cached --no-color | \
        grep -E '^\+' | \
        grep -E 'ghp_[A-Za-z0-9_-]{20,}|github_pat_[A-Za-z0-9_-]{30,}|sk-[A-Za-z0-9_-]{30,}|sb_(secret|publishable)_[A-Za-z0-9_-]{20,}|xox[bp]-[A-Za-z0-9-]{20,}' \
        || true
)
if [ -n "$STAGED_LEAK" ]; then
    log "ABORT: token-shaped strings found in staged diff:"
    log "$STAGED_LEAK"
    git reset >/dev/null
    exit 2
fi

# --- 5. commit + push ------------------------------------------------------
if git diff --cached --quiet; then
    log "no changes to commit"
    exit 0
fi

TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
git -c user.email=aseckzai@gmail.com -c user.name="Kais Aseckzai" \
    commit -q -m "arsenal sync: $TIMESTAMP ($REASON)"
git push -q origin main
log "synced: $REASON"
