#!/bin/bash
#
# archive-sessions.sh - Archive inactive OpenClaw sessions
#
# Usage: ./archive-sessions.sh [-d DAYS] [--dry-run]
#
# Options:
#   -d DAYS     Archive sessions inactive for more than DAYS days (default: 1)
#   --dry-run   Show what would be archived without making changes
#
# Runs daily at 20:00 UTC via cron.
#

set -euo pipefail

# Configuration
SESSIONS_DIR="/home/aria/.openclaw/agents/main/sessions"
ARCHIVE_DIR="$SESSIONS_DIR/archive"
LOG_FILE="/home/aria/.openclaw/workspace/logs/archive-sessions.log"
INACTIVE_DAYS=1
RETENTION_MONTHS=3
DRY_RUN=false

# Parse arguments
while [[ $# -gt 0 ]]; do
    case $1 in
        -d)
            INACTIVE_DAYS="$2"
            shift 2
            ;;
        --dry-run)
            DRY_RUN=true
            echo "[DRY-RUN MODE] No changes will be made"
            shift
            ;;
        *)
            echo "Unknown option: $1"
            echo "Usage: $0 [-d DAYS] [--dry-run]"
            exit 1
            ;;
    esac
done

# Logging
log() {
    echo "[$(date -u '+%Y-%m-%d %H:%M:%S UTC')] $*" | tee -a "$LOG_FILE"
}

log "=== Starting session archive ==="
log "Inactive threshold: $INACTIVE_DAYS days"
log "Retention: $RETENTION_MONTHS months"

# Acquire lock (zero-downtime safety)
LOCK_FILE="$SESSIONS_DIR/.archive.lock"
exec 200>"$LOCK_FILE"
if ! flock -n 200; then
    log "ERROR: Another archive process is running. Exiting."
    exit 1
fi
log "Lock acquired"

# Calculate cutoff timestamp (3 days ago in milliseconds)
CUTOFF_MS=$(( ($(date +%s) - (INACTIVE_DAYS * 86400)) * 1000 ))
log "Cutoff timestamp: $CUTOFF_MS ($(date -d "@$((CUTOFF_MS / 1000))" '+%Y-%m-%d %H:%M:%S UTC'))"

# Create archive directory structure
MONTH_DIR="$ARCHIVE_DIR/$(date -u '+%Y-%m')"
if [[ "$DRY_RUN" == false ]]; then
    mkdir -p "$MONTH_DIR"
fi

# Initialize archive index if it doesn't exist
ARCHIVE_INDEX="$ARCHIVE_DIR/index.json"
if [[ ! -f "$ARCHIVE_INDEX" ]] && [[ "$DRY_RUN" == false ]]; then
    echo '{}' > "$ARCHIVE_INDEX"
fi

# Backup sessions.json before modifications
if [[ "$DRY_RUN" == false ]]; then
    cp "$SESSIONS_DIR/sessions.json" "$SESSIONS_DIR/sessions.json.bak"
    log "Backup created: sessions.json.bak"
fi

# Read sessions.json and find inactive sessions
SESSIONS_FILE="$SESSIONS_DIR/sessions.json"
INACTIVE_SESSIONS=$(jq -r --argjson cutoff "$CUTOFF_MS" '
    to_entries[] | 
    select(.value.updatedAt < $cutoff) | 
    @json
' "$SESSIONS_FILE")

ARCHIVED_COUNT=0
ARCHIVED_SIZE=0

# Process each inactive session
while IFS= read -r session_json; do
    [[ -z "$session_json" ]] && continue
    
    SESSION_KEY=$(echo "$session_json" | jq -r '.key')
    SESSION_ID=$(echo "$session_json" | jq -r '.value.sessionId')
    UPDATED_AT=$(echo "$session_json" | jq -r '.value.updatedAt')
    
    # Find the session transcript file
    TRANSCRIPT_FILE=""
    for pattern in "$SESSIONS_DIR/$SESSION_ID.jsonl" "$SESSIONS_DIR/"*"_$SESSION_ID.jsonl"; do
        if [[ -f "$pattern" ]]; then
            TRANSCRIPT_FILE="$pattern"
            break
        fi
    done
    
    if [[ -n "$TRANSCRIPT_FILE" && -f "$TRANSCRIPT_FILE" ]]; then
        FILE_SIZE=$(stat -c%s "$TRANSCRIPT_FILE" 2>/dev/null || echo 0)
        ARCHIVED_SIZE=$((ARCHIVED_SIZE + FILE_SIZE))
        
        log "Archiving: $SESSION_KEY (${FILE_SIZE} bytes)"
        
        if [[ "$DRY_RUN" == false ]]; then
            # Compress and move transcript to archive
            ARCHIVE_FILE="$MONTH_DIR/$(basename "$TRANSCRIPT_FILE").gz"
            gzip -c "$TRANSCRIPT_FILE" > "$ARCHIVE_FILE"
            rm "$TRANSCRIPT_FILE"
            
            # Add to archive index
            jq --arg key "$SESSION_KEY" --argjson value "$(echo "$session_json" | jq '.value')" \
                --arg archivePath "$ARCHIVE_FILE" \
                '.[$key] = ($value + {archivePath: $archivePath, archivedAt: (now * 1000 | floor)})' \
                "$ARCHIVE_INDEX" > "$ARCHIVE_INDEX.tmp" && mv "$ARCHIVE_INDEX.tmp" "$ARCHIVE_INDEX"
        fi
        
        ARCHIVED_COUNT=$((ARCHIVED_COUNT + 1))
    else
        log "  No transcript file found for $SESSION_ID (metadata only)"
    fi
done <<< "$INACTIVE_SESSIONS"

# Remove archived sessions from sessions.json (atomic write)
if [[ "$DRY_RUN" == false ]] && [[ $ARCHIVED_COUNT -gt 0 ]]; then
    jq --argjson cutoff "$CUTOFF_MS" '
        with_entries(select(.value.updatedAt >= $cutoff))
    ' "$SESSIONS_FILE" > "$SESSIONS_FILE.tmp" && mv "$SESSIONS_FILE.tmp" "$SESSIONS_FILE"
    log "Updated sessions.json (removed $ARCHIVED_COUNT entries)"
fi

# Archive .deleted.* files
DELETED_COUNT=0
for deleted_file in "$SESSIONS_DIR"/*.jsonl.deleted.*; do
    [[ -f "$deleted_file" ]] || continue
    
    FILE_SIZE=$(stat -c%s "$deleted_file" 2>/dev/null || echo 0)
    ARCHIVED_SIZE=$((ARCHIVED_SIZE + FILE_SIZE))
    
    log "Archiving deleted: $(basename "$deleted_file")"
    
    if [[ "$DRY_RUN" == false ]]; then
        gzip -c "$deleted_file" > "$MONTH_DIR/$(basename "$deleted_file").gz"
        rm "$deleted_file"
    fi
    
    DELETED_COUNT=$((DELETED_COUNT + 1))
done

# Cleanup old archives (older than 3 months)
CLEANUP_COUNT=0
if [[ -d "$ARCHIVE_DIR" ]]; then
    RETENTION_CUTOFF=$(date -u -d "-$RETENTION_MONTHS months" '+%Y-%m')
    
    for month_folder in "$ARCHIVE_DIR"/20[0-9][0-9]-[0-9][0-9]; do
        [[ -d "$month_folder" ]] || continue
        FOLDER_MONTH=$(basename "$month_folder")
        
        if [[ "$FOLDER_MONTH" < "$RETENTION_CUTOFF" ]]; then
            log "Deleting old archive: $FOLDER_MONTH"
            if [[ "$DRY_RUN" == false ]]; then
                rm -rf "$month_folder"
                # Also remove from index
                jq --arg month "$FOLDER_MONTH" '
                    with_entries(select(.value.archivePath | contains($month) | not))
                ' "$ARCHIVE_INDEX" > "$ARCHIVE_INDEX.tmp" && mv "$ARCHIVE_INDEX.tmp" "$ARCHIVE_INDEX"
            fi
            CLEANUP_COUNT=$((CLEANUP_COUNT + 1))
        fi
    done
fi

# Summary
log "=== Archive Complete ==="
log "Sessions archived: $ARCHIVED_COUNT"
log "Deleted files archived: $DELETED_COUNT"
log "Old archives cleaned: $CLEANUP_COUNT"
log "Total size archived: $(numfmt --to=iec $ARCHIVED_SIZE 2>/dev/null || echo "${ARCHIVED_SIZE} bytes")"

# Release lock (automatic on exit, but explicit for clarity)
flock -u 200

log "Done."
