#!/bin/bash
# ==============================================================================================
# ================================= Clear Logs =================================================
# ==============================================================================================
#
# PURPOSE
# ─────────────────────────────────────────────────────────────────────────────
# Clears system and Docker container logs to prevent rootfs fill over time.
# Called weekly via WEEKLY_MAINTENANCE_SCRIPTS. Uses size thresholds — only
# clears logs large enough to be worth clearing. Small logs are left intact,
# preserving recent diagnostic context.
#
# System logs (LOG_FILES): cleared if size exceeds LOG_MIN_SIZE_MB.
# Docker logs (/var/lib/docker/containers/**/*-json.log): cleared only if the
# individual container log exceeds LOG_DOCKER_MAX_MB. Active containers (Emby,
# SABnzbd) grow fastest — inactive containers typically remain small.
#
# ==============================================================================================
# OPERATIONAL MODEL
# ==============================================================================================
#
# Two independent passes, each with its own threshold:
#
#   System logs — every path in LOG_FILES
#     size < LOG_MIN_SIZE_MB    → skip, recent diagnostic history is worth keeping
#     size >= LOG_MIN_SIZE_MB   → truncate in place
#
#   Docker logs — /var/lib/docker/containers/**/*-json.log
#     container name resolved for reporting via docker inspect
#     size < LOG_DOCKER_MAX_MB  → skip
#     size >= LOG_DOCKER_MAX_MB → truncate in place
#     containers directory missing → whole pass skipped, not an error
#
# Truncation is always `: > file`, never rm — see Truncate, Never Delete below.
# Freed bytes are totalled per pass and reported in the summary.
#
# ==============================================================================================
# DESIGN PRINCIPLES
# ==============================================================================================
#
# Truncate, Never Delete
#   Logs are emptied in place, never removed. The writing process keeps its open file
#   handle and keeps logging; deleting the inode would leave a running daemon writing
#   to a file nothing can read, and would consume more tmpfs, not less.
#
# Size Thresholds, Not Blind Truncation
#   A 2MB syslog contains useful recent diagnostic history — not worth clearing.
#   A 500MB Docker log is consuming rootfs and contains mostly noise — clear it.
#   Blind truncation destroys diagnostic context for no benefit.
#
# Truncation, Not Logrotate
#   unRAID writes logs to tmpfs (/var/log). Logrotate's compress + archive approach
#   would consume more tmpfs space, not less. Truncation (`: > file`) keeps the
#   file descriptor open and valid while emptying content — syslogd continues
#   writing to the same fd without interruption.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
# Single Instance Lock
#   acquire_lock prevents concurrent runs from corrupting logs.
#
# Root Required
#   Truncating system logs requires root.
#
# Size Thresholds
#   Each file checked against its threshold before clearing.
#
# Silent When Clean
#   All logs below threshold = no visible output.
#
# ==============================================================================================
# CONFIGURATION
# ==============================================================================================
#
# master.conf
#
#   LOG_FILES
#       System log paths to check and clear. (default: /var/log/syslog /var/log/messages /var/log/dmesg)
#
#   LOG_MIN_SIZE_MB
#       Skip system log if under this size — keep recent history. (default: 10)
#
#   LOG_DOCKER_MAX_MB
#       Clear Docker container log only if over this size. (default: 100)
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
#
# clear_logs.sh
#     Check all configured logs. Clear those above threshold. Silent when all are small.
#
# clear_logs.sh --dry-run
#     Show which logs would be cleared and their current sizes. No clearing.
#
# clear_logs.sh --status
#     Show current log sizes vs thresholds.
#
# clear_logs.sh --log
#     Verbose output — show each file evaluated, its size, and action taken.
#
# ==============================================================================================

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

source "$SCRIPT_DIR/../load_config.sh"

parse_args "$@"

# ==============================================================================================
# ━━━ Setup ━━━
# ==============================================================================================
if [[ "$EUID" -ne 0 ]]; then
    error "Must be run as root — truncating system logs requires root"
    exit 1
fi


acquire_lock

if ! command -v docker &>/dev/null; then
    error "Docker command not found"
    exit 1
fi

detect_hosts

[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no files will be cleared"

# ==============================================================================================
# ━━━ Status ━━━
# ==============================================================================================
if [[ "$SHOW_STATUS" == true ]]; then
    echo ""
    echo "━━━━━ $ICON_SUMMARY LOG STATUS ━━━━━"
    echo "$ICON_HOST Identity:           $MY_ID ($LOCAL_SERVER_NAME)"
    echo "$ICON_GEAR Min system log:     ${LOG_MIN_SIZE_MB:-10}MB before clearing"
    echo "$ICON_GEAR Max Docker log:     ${LOG_DOCKER_MAX_MB:-100}MB before clearing"
    echo ""

    echo "━━━ System Logs ━━━"
    for f in "${LOG_FILES[@]}"; do
        if [[ -f "$f" ]]; then
            size=$(du -sh "$f" 2>/dev/null | cut -f1)
            size_mb=$(du -sm "$f" 2>/dev/null | cut -f1)
            threshold="${LOG_MIN_SIZE_MB:-10}"
            if [[ "${size_mb:-0}" -ge "$threshold" ]]; then
                echo "  $ICON_WARN $f — $size (above ${threshold}MB threshold — would clear)"
            else
                echo "  $ICON_SUCCESS $f — $size (under threshold)"
            fi
        else
            echo "  $ICON_SKIP $f — not found"
        fi
    done

    echo ""
    echo "━━━ Docker Logs (top 10 by size) ━━━"
    if [[ -d /var/lib/docker/containers ]]; then
        find /var/lib/docker/containers/ -name "*-json.log" 2>/dev/null | \
            while IFS= read -r logfile; do
                size_mb=$(du -sm "$logfile" 2>/dev/null | cut -f1)
                container_id=$(basename "$(dirname "$logfile")" | cut -c1-12)
                container_name=$(docker inspect --format '{{.Name}}' "$container_id" \
                    2>/dev/null | tr -d '/' || echo "$container_id")
                echo "${size_mb:-0} $container_name $logfile"
            done | sort -rn | head -10 | \
            while read -r size_mb name logfile; do
                threshold="${LOG_DOCKER_MAX_MB:-100}"
                if [[ "$size_mb" -ge "$threshold" ]]; then
                    echo "  $ICON_WARN ${size_mb}MB — $name (above ${threshold}MB — would clear)"
                else
                    echo "  $ICON_SUCCESS ${size_mb}MB — $name"
                fi
            done
    else
        echo "  Docker directory not found"
    fi
    echo "━━━━━━━━━━━━━━━━━━━━━━━"
    exit 0
fi

# ==============================================================================================
# ━━━ Clear Logs ━━━
# ==============================================================================================
log "$ICON_GEAR Config: system-threshold=${LOG_MIN_SIZE_MB:-10}MB docker-threshold=${LOG_DOCKER_MAX_MB:-100}MB"
log "$ICON_GEAR System logs: ${LOG_FILES[*]}"

START=$(date +%s)
SYS_CLEARED=0
SYS_SKIPPED=0
SYS_BYTES=0
DOCKER_CLEARED=0
DOCKER_SKIPPED=0
DOCKER_BYTES=0
FAILED=()

# ── System Logs ───────────────────────────────────────────────────────────────────────────────
for logfile in "${LOG_FILES[@]}"; do
    if [[ ! -f "$logfile" ]]; then
        log "$logfile — not found, skipping"
        continue
    fi

    size_bytes=$(stat -c%s "$logfile" 2>/dev/null || echo 0)
    size_mb=$(( size_bytes / 1048576 ))
    size_h=$(du -sh "$logfile" 2>/dev/null | cut -f1)
    threshold="${LOG_MIN_SIZE_MB:-10}"

    if [[ "$size_mb" -lt "$threshold" ]]; then
        log "$logfile — ${size_h} (under ${threshold}MB — skipping)"
        (( SYS_SKIPPED++ ))
        continue
    fi

    if [[ "$DRY_RUN" == true ]]; then
        warn "DRY RUN — would clear: $logfile (${size_h})"
        (( SYS_CLEARED++ ))
        SYS_BYTES=$(( SYS_BYTES + size_bytes ))
        continue
    fi

    if : > "$logfile" 2>/dev/null; then
        log "Cleared: $logfile (freed ${size_h})"
        (( SYS_CLEARED++ ))
        SYS_BYTES=$(( SYS_BYTES + size_bytes ))
    else
        error "Failed to clear: $logfile"
        FAILED+=("$logfile")
    fi
done

# ── Docker Logs ───────────────────────────────────────────────────────────────────────────────
if [[ ! -d /var/lib/docker/containers ]]; then
    log "Docker containers directory not found — skipping Docker log clear"
else
    while IFS= read -r logfile; do
        [[ -z "$logfile" ]] && continue

        size_bytes=$(stat -c%s "$logfile" 2>/dev/null || echo 0)
        size_mb=$(( size_bytes / 1048576 ))
        size_h=$(du -sh "$logfile" 2>/dev/null | cut -f1)
        threshold="${LOG_DOCKER_MAX_MB:-100}"

        # Get container name for display
        container_id=$(basename "$(dirname "$logfile")" | cut -c1-12)
        container_name=$(docker inspect --format '{{.Name}}' "$container_id" \
            2>/dev/null | tr -d '/' || echo "$container_id")

        if [[ "$size_mb" -lt "$threshold" ]]; then
            log "Docker $container_name — ${size_h} (under ${threshold}MB — skipping)"
            (( DOCKER_SKIPPED++ ))
            continue
        fi

        if [[ "$DRY_RUN" == true ]]; then
            warn "DRY RUN — would clear Docker log: $container_name (${size_h})"
            (( DOCKER_CLEARED++ ))
            DOCKER_BYTES=$(( DOCKER_BYTES + size_bytes ))
            continue
        fi

        if : > "$logfile" 2>/dev/null; then
            log "Cleared Docker log: $container_name (freed ${size_h})"
            (( DOCKER_CLEARED++ ))
            DOCKER_BYTES=$(( DOCKER_BYTES + size_bytes ))
        else
            error "Failed to clear Docker log: $container_name"
            FAILED+=("docker:$container_name")
        fi
    done < <(find /var/lib/docker/containers/ -name "*-json.log" 2>/dev/null)
fi

END=$(date +%s)
TOTAL_BYTES=$(( SYS_BYTES + DOCKER_BYTES ))
TOTAL_FREED_H=$(awk "BEGIN {printf \"%.1fMB\", $TOTAL_BYTES / 1048576}")
TOTAL_CLEARED=$(( SYS_CLEARED + DOCKER_CLEARED ))

# ==============================================================================================
# ━━━ Summary ━━━
# ==============================================================================================
if [[ "$TOTAL_CLEARED" -gt 0 || ${#FAILED[@]} -gt 0 ]]; then
    echo ""
    echo "━━━━━ $ICON_SUMMARY LOG CLEANER SUMMARY ━━━━━"
    echo "$ICON_HOST   Identity:       $MY_ID ($LOCAL_SERVER_NAME)"
    echo "$ICON_HEALTH System cleared: $SYS_CLEARED file(s)"
    echo "$ICON_CONTAINERS Docker cleared: $DOCKER_CLEARED file(s)"
    echo "$ICON_HEALTH Total freed:    $TOTAL_FREED_H"
    echo "$ICON_TIME   Duration:       $(format_duration $(( END - START )))"
    [[ "$SYS_SKIPPED" -gt 0 || "$DOCKER_SKIPPED" -gt 0 ]] && \
        log "Skipped: ${SYS_SKIPPED} system + ${DOCKER_SKIPPED} Docker (under threshold)"
    echo ""

    if [[ "$DRY_RUN" == true ]]; then
        warn "DRY RUN — no files cleared"
    elif [[ ${#FAILED[@]} -gt 0 ]]; then
        echo "$ICON_ERROR Status: SOME FILES FAILED — ${FAILED[*]}"
        notify "Log clear failed on $(hostname) ($MY_ID) — ${FAILED[*]}" \
            "Clear Logs" "warning"
    else
        echo "$ICON_DONE Status: done — ${TOTAL_FREED_H} freed"
    fi
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
else
    # All logs under threshold — completely silent
    echo "All logs under threshold — nothing to clear"
fi

[[ ${#FAILED[@]} -gt 0 ]] && exit 1
exit 0