#!/usr/bin/env bash
set -euo pipefail

# server-autoclean-installer.sh
# Usage:
#   sudo bash server-autoclean-installer.sh
#
# What it does:
#   1) Installs /usr/local/sbin/server-autoclean.sh
#   2) Creates systemd service + timer
#   3) Runs once on boot and then every 12 hours
#   4) Cleans apt cache, old journal logs, old tmp files, Docker dangling cache/images
#   5) Safely purges old MySQL/MariaDB binary logs through MySQL inside Docker containers
#
# Important:
#   If this MySQL server is a replication master or you rely on binlogs for point-in-time recovery,
#   set MYSQL_PURGE_BINLOGS=0 in /etc/server-autoclean.conf after installation.

INSTALL_PATH="/usr/local/sbin/server-autoclean.sh"
CONFIG_PATH="/etc/server-autoclean.conf"
SERVICE_PATH="/etc/systemd/system/server-autoclean.service"
TIMER_PATH="/etc/systemd/system/server-autoclean.timer"

if [ "$(id -u)" -ne 0 ]; then
  echo "ERROR: run as root"
  echo "Example: sudo bash $0"
  exit 1
fi

cat > "$CONFIG_PATH" <<'CONF'
# server-autoclean config

# Run every 12 hours via systemd timer.
# MySQL/MariaDB binlog retention. 43200 = 12 hours.
MYSQL_BINLOG_RETENTION_SECONDS=43200

# 1 = purge old MySQL/MariaDB binlogs in Docker containers.
# Set to 0 if you use MySQL replication or binlog-based backups/PITR.
MYSQL_PURGE_BINLOGS=1

# Keep systemd journal logs for this duration.
JOURNAL_VACUUM_TIME=7d

# Delete files inside /tmp and /var/tmp older than this many days.
TMP_DELETE_OLDER_THAN_DAYS=2

# Safe Docker cleanup:
# 1 = remove build cache and dangling images only. Does NOT remove volumes.
DOCKER_SAFE_PRUNE=1

# Riskier cleanup:
# 1 = run apt autoremove --purge. Default 0 because some servers need old packages/kernels.
APT_AUTOREMOVE=0
CONF

cat > "$INSTALL_PATH" <<'CLEANER'
#!/usr/bin/env bash
set -u

CONFIG_PATH="/etc/server-autoclean.conf"
LOG_FILE="/var/log/server-autoclean.log"
LOCK_FILE="/run/server-autoclean.lock"

# Defaults if config is missing
MYSQL_BINLOG_RETENTION_SECONDS=43200
MYSQL_PURGE_BINLOGS=1
JOURNAL_VACUUM_TIME="7d"
TMP_DELETE_OLDER_THAN_DAYS=2
DOCKER_SAFE_PRUNE=1
APT_AUTOREMOVE=0

[ -f "$CONFIG_PATH" ] && . "$CONFIG_PATH"

mkdir -p "$(dirname "$LOG_FILE")"

exec 9>"$LOCK_FILE"
if ! flock -n 9; then
  echo "Another server-autoclean run is already active. Exiting."
  exit 0
fi

exec > >(tee -a "$LOG_FILE") 2>&1

echo
echo "============================================================"
echo "server-autoclean started: $(date -Is)"
echo "============================================================"

echo
echo "===== DISK BEFORE ====="
df -h / || true

echo
echo "===== APT CACHE CLEAN ====="
if command -v apt-get >/dev/null 2>&1; then
  apt-get clean || true
  apt-get autoclean -y || true

  if [ "${APT_AUTOREMOVE:-0}" = "1" ]; then
    apt-get autoremove --purge -y || true
  else
    echo "APT_AUTOREMOVE=0, skipping apt autoremove."
  fi
else
  echo "apt-get not found, skipping."
fi

echo
echo "===== JOURNAL CLEAN ====="
if command -v journalctl >/dev/null 2>&1; then
  journalctl --vacuum-time="${JOURNAL_VACUUM_TIME:-7d}" || true
else
  echo "journalctl not found, skipping."
fi

echo
echo "===== TMP CLEAN ====="
TMP_DAYS="${TMP_DELETE_OLDER_THAN_DAYS:-2}"
if [ "$TMP_DAYS" -ge 1 ] 2>/dev/null; then
  find /tmp -xdev -type f -mtime +"$TMP_DAYS" -delete 2>/dev/null || true
  find /var/tmp -xdev -type f -mtime +"$TMP_DAYS" -delete 2>/dev/null || true
else
  echo "TMP_DELETE_OLDER_THAN_DAYS is less than 1, skipping tmp cleanup."
fi

echo
echo "===== DOCKER SAFE CLEAN ====="
if [ "${DOCKER_SAFE_PRUNE:-1}" = "1" ] && command -v docker >/dev/null 2>&1; then
  docker builder prune -af --filter "until=24h" || true
  docker image prune -f || true
else
  echo "Docker safe prune disabled or docker not found."
fi

purge_mysql_binlogs_in_container() {
  local container="$1"

  echo
  echo "----- Checking MySQL/MariaDB container: $container -----"

  docker exec -e CLEAN_RETENTION="${MYSQL_BINLOG_RETENTION_SECONDS:-43200}" "$container" sh -lc '
set +e

mysql_exec() {
  if [ -n "${MYSQL_ROOT_PASSWORD:-}" ]; then
    mysql -uroot -p"$MYSQL_ROOT_PASSWORD" "$@"
  elif [ -n "${MARIADB_ROOT_PASSWORD:-}" ]; then
    mysql -uroot -p"$MARIADB_ROOT_PASSWORD" "$@"
  else
    mysql -uroot "$@"
  fi
}

mysql_exec -Nse "SELECT 1;" >/dev/null 2>&1
if [ "$?" -ne 0 ]; then
  echo "Cannot login to MySQL as root using container env password. Skipping this container."
  exit 0
fi

BINLOG_ON="$(mysql_exec -Nse "SHOW VARIABLES LIKE '\''log_bin'\'';" 2>/dev/null | awk "{print \$2}")"
if [ "$BINLOG_ON" != "ON" ] && [ "$BINLOG_ON" != "1" ]; then
  echo "Binary logging is not enabled. Skipping."
  exit 0
fi

CURRENT_LOG="$(mysql_exec -Nse "SHOW BINARY LOGS;" 2>/dev/null | awk "END {print \$1}")"
if [ -z "$CURRENT_LOG" ]; then
  echo "No binary logs found. Skipping."
  exit 0
fi

echo "Current active binlog: $CURRENT_LOG"
echo "Binlogs before:"
mysql_exec -e "SHOW BINARY LOGS;" 2>/dev/null || true

echo "Purging old binlogs before active log..."
mysql_exec -e "PURGE BINARY LOGS TO '\''$CURRENT_LOG'\'';" 2>/dev/null || {
  echo "PURGE BINARY LOGS failed. Skipping."
  exit 0
}

RET="${CLEAN_RETENTION:-43200}"
echo "Setting binlog_expire_logs_seconds=$RET ..."
mysql_exec -e "SET PERSIST binlog_expire_logs_seconds = $RET;" 2>/dev/null \
  || mysql_exec -e "SET GLOBAL binlog_expire_logs_seconds = $RET;" 2>/dev/null \
  || true

echo "Binlog retention:"
mysql_exec -e "SHOW VARIABLES LIKE '\''binlog_expire_logs_seconds'\'';" 2>/dev/null || true

echo "Binlogs after:"
mysql_exec -e "SHOW BINARY LOGS;" 2>/dev/null || true
'
}

echo
echo "===== MYSQL/MARIADB BINLOG CLEAN ====="
if [ "${MYSQL_PURGE_BINLOGS:-1}" = "1" ] && command -v docker >/dev/null 2>&1; then
  MYSQL_CONTAINERS="$(docker ps --format '{{.Names}} {{.Image}}' | awk 'tolower($0) ~ /(mysql|mariadb)/ {print $1}')"

  if [ -z "$MYSQL_CONTAINERS" ]; then
    echo "No running Docker MySQL/MariaDB containers found."
  else
    for c in $MYSQL_CONTAINERS; do
      purge_mysql_binlogs_in_container "$c" || true
    done
  fi
else
  echo "MYSQL_PURGE_BINLOGS=0 or docker not found, skipping MySQL binlog cleanup."
fi

echo
echo "===== DISK AFTER ====="
df -h / || true

echo
echo "===== BIG /var/lib DIRECTORIES ====="
du -xhd1 /var/lib 2>/dev/null | sort -h || true

echo
echo "server-autoclean finished: $(date -Is)"
echo "============================================================"
CLEANER

chmod 0755 "$INSTALL_PATH"

cat > "$SERVICE_PATH" <<SERVICE
[Unit]
Description=Safe server auto cleanup
Documentation=man:systemd.service(5)
Wants=network-online.target docker.service
After=network-online.target docker.service

[Service]
Type=oneshot
ExecStart=$INSTALL_PATH
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=7
SERVICE

cat > "$TIMER_PATH" <<'TIMER'
[Unit]
Description=Run server auto cleanup on boot and every 12 hours

[Timer]
OnBootSec=2min
OnUnitActiveSec=12h
Persistent=true
Unit=server-autoclean.service

[Install]
WantedBy=timers.target
TIMER

systemctl daemon-reload
systemctl enable --now server-autoclean.timer

echo
echo "===== RUNNING CLEANUP NOW ====="
systemctl start server-autoclean.service || true

echo
echo "===== STATUS ====="
systemctl status server-autoclean.timer --no-pager || true

echo
echo "Installed:"
echo "  Cleaner: $INSTALL_PATH"
echo "  Config:  $CONFIG_PATH"
echo "  Service: $SERVICE_PATH"
echo "  Timer:   $TIMER_PATH"
echo
echo "Useful commands:"
echo "  systemctl status server-autoclean.timer --no-pager"
echo "  systemctl status server-autoclean.service --no-pager"
echo "  journalctl -u server-autoclean.service -n 200 --no-pager"
echo "  tail -n 200 /var/log/server-autoclean.log"
echo
echo "DONE"
