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

LOG_TAG="harresi-logrotate"
STATE_DIR="/var/lib/harresi/logrotate"
LOCK_FILE="/run/harresi-logrotate.lock"

GROUP10_CONFIGS=(
  "/etc/harresi/logrotate.d/toe-10percent"
)
GROUP50_CONFIGS=(
  "/etc/harresi/logrotate.d/toe-50percent"
)

GROUP10_TOTAL_GLOBS=(
  "/var/log/auth.log"
  "/var/log/auth.log.*"
  "/var/log/harresi/harresi_admin.log"
  "/var/log/harresi/harresi_admin.log.*"
  "/var/log/harresi/harresi_operador.log"
  "/var/log/harresi/harresi_operador.log.*"
  "/var/log/harresi/harresi_auditor.log"
  "/var/log/harresi/harresi_auditor.log.*"
  "/var/log/harresi/audit-security.log"
  "/var/log/harresi/audit-security.log.*"
  "/var/log/tmux-status/*.log"
  "/var/log/tmux-status/*.log.*"
)
GROUP10_ROTATED_GLOBS=(
  "/var/log/auth.log.*"
  "/var/log/harresi/harresi_admin.log.*"
  "/var/log/harresi/harresi_operador.log.*"
  "/var/log/harresi/harresi_auditor.log.*"
  "/var/log/harresi/audit-security.log.*"
  "/var/log/tmux-status/*.log.*"
)

GROUP50_TOTAL_GLOBS=(
  "/var/log/audit/audit.log"
  "/var/log/audit/audit.log.*"
  "/var/log/harresi/audit-product.log"
  "/var/log/harresi/audit-product.log.*"
  "/var/log/suricata/*.log"
  "/var/log/suricata/*.log.*"
  "/var/log/suricata/*.json"
  "/var/log/suricata/*.json.*"
  "/var/log/suricata/*.pcap"
  "/var/log/suricata/*.pcap.*"
  "/var/log/suricata-mitm/*.log"
  "/var/log/suricata-mitm/*.log.*"
  "/var/log/suricata-mitm/*.json"
  "/var/log/suricata-mitm/*.json.*"
  "/var/log/suricata-mitm/*.pcap"
  "/var/log/suricata-mitm/*.pcap.*"
  "/var/log/sslproxy/*.log"
  "/var/log/sslproxy/*.log.*"
  "/var/log/suricata-aggregator.log"
  "/var/log/suricata-aggregator.log.*"
  "/var/log/system_integrity.log"
  "/var/log/system_integrity.log.*"
)
GROUP50_ROTATED_GLOBS=(
  "/var/log/audit/audit.log.*"
  "/var/log/harresi/audit-product.log.*"
  "/var/log/suricata/*.log.*"
  "/var/log/suricata/*.json.*"
  "/var/log/suricata/*.pcap.*"
  "/var/log/suricata-mitm/*.log.*"
  "/var/log/suricata-mitm/*.json.*"
  "/var/log/suricata-mitm/*.pcap.*"
  "/var/log/sslproxy/*.log.*"
  "/var/log/suricata-aggregator.log.*"
  "/var/log/system_integrity.log.*"
)

log_msg() {
  logger -t "$LOG_TAG" -- "$*" 2>/dev/null || true
}

threshold_bytes() {
  local percent="$1"
  local total
  local mem_kb

  if [[ "${HARRESI_RAM_TOTAL_BYTES:-}" =~ ^[0-9]+$ ]] && (( HARRESI_RAM_TOTAL_BYTES > 0 )); then
    total="$HARRESI_RAM_TOTAL_BYTES"
  else
    mem_kb=$(awk '/^MemTotal:/ { print $2 }' /proc/meminfo 2>/dev/null || true)
    if [[ "$mem_kb" =~ ^[0-9]+$ ]] && (( mem_kb > 0 )); then
      total=$(( mem_kb * 1024 ))
    fi
  fi

  if [[ ! "$total" =~ ^[0-9]+$ ]] || (( total <= 0 )); then
    log_msg "unable to determine total RAM size"
    return 1
  fi

  printf '%s\n' $(( total * percent / 100 ))
}

expand_globs() {
  local pattern file

  shopt -s nullglob
  for pattern in "$@"; do
    for file in $pattern; do
      [[ -f "$file" ]] && printf '%s\0' "$file"
    done
  done
  shopt -u nullglob
}

total_size() {
  local total=0
  local file size

  while IFS= read -r -d '' file; do
    size=$(stat -c '%s' -- "$file" 2>/dev/null || printf '0')
    [[ "$size" =~ ^[0-9]+$ ]] || size=0
    total=$(( total + size ))
  done < <(expand_globs "$@")

  printf '%s\n' "$total"
}

oldest_rotated_file() {
  local file mtime
  local oldest_file=""
  local oldest_mtime=""

  while IFS= read -r -d '' file; do
    mtime=$(stat -c '%Y' -- "$file" 2>/dev/null || printf '0')
    [[ "$mtime" =~ ^[0-9]+$ ]] || mtime=0
    if [[ -z "$oldest_mtime" ]] || (( mtime < oldest_mtime )); then
      oldest_mtime="$mtime"
      oldest_file="$file"
    fi
  done < <(expand_globs "$@")

  [[ -n "$oldest_file" ]] && printf '%s\n' "$oldest_file"
}

run_logrotate() {
  local group_name="$1"
  shift

  local config status_file

  for config in "$@"; do
    if [[ ! -f "$config" ]]; then
      log_msg "missing logrotate config for $group_name: $config"
      continue
    fi

    status_file="$STATE_DIR/$(basename "$config").status"
    /usr/sbin/logrotate -s "$status_file" -f "$config" >/dev/null 2>&1 ||
      log_msg "logrotate failed for $group_name using $config"
  done
}

prune_rotated_until_below_threshold() {
  local group_name="$1"
  local threshold="$2"
  local total_globs_name="$3"
  local rotated_globs_name="$4"
  local -n total_globs="$total_globs_name"
  local -n rotated_globs="$rotated_globs_name"
  local total oldest

  while :; do
    total=$(total_size "${total_globs[@]}")
    (( total <= threshold )) && break

    oldest=$(oldest_rotated_file "${rotated_globs[@]}" || true)
    [[ -n "$oldest" ]] || break

    rm -f -- "$oldest"
    log_msg "$group_name over threshold; removed oldest rotated log $oldest"
  done
}

check_group() {
  local group_name="$1"
  local percent="$2"
  local configs_name="$3"
  local total_globs_name="$4"
  local rotated_globs_name="$5"
  local -n configs="$configs_name"
  local -n total_globs="$total_globs_name"
  local threshold total

  threshold=$(threshold_bytes "$percent") || return 0
  total=$(total_size "${total_globs[@]}")

  if (( total >= threshold )); then
    log_msg "$group_name reached ${percent}% threshold (${total}/${threshold} bytes); rotating"
    run_logrotate "$group_name" "${configs[@]}"
    prune_rotated_until_below_threshold "$group_name" "$threshold" "$total_globs_name" "$rotated_globs_name"
  fi
}

check_10percent() {
  check_group "toe-audit-10percent" 10 GROUP10_CONFIGS GROUP10_TOTAL_GLOBS GROUP10_ROTATED_GLOBS
}

check_50percent() {
  check_group "toe-audit-50percent" 50 GROUP50_CONFIGS GROUP50_TOTAL_GLOBS GROUP50_ROTATED_GLOBS
}

with_lock() {
  local group="$1"

  (
    flock -n 9 || exit 0
    mkdir -p "$STATE_DIR"
    case "$group" in
      10) check_10percent ;;
      50) check_50percent ;;
    esac
  ) 9>"$LOCK_FILE"
}

main() {
  local line

  while IFS= read -r line; do
    case "$line" in
      *harresi_logrotate_10percent*) with_lock 10 ;;
      *harresi_logrotate_50percent*) with_lock 50 ;;
    esac
  done
}

main "$@"
