#!/usr/bin/env bash
set -u
set -o pipefail

AIDE_CONFIG_DIR="${HARRESI_AIDE_CONFIG_DIR:-/etc/aide}"
AIDE_STATE_DIR="${HARRESI_AIDE_STATE_DIR:-/var/lib/aide}"
LOG_DIR="${HARRESI_AIDE_LOG_DIR:-/var/log/harresi/audit/aide}"
LAST_LOG="$LOG_DIR/aide.log"
HIST_LOG="$LOG_DIR/aide_history.log"
RUN_SOURCE="${HARRESI_AIDE_RUN_SOURCE:-startup}"
EXTRA_RULES_FILE="${HARRESI_AIDE_EXTRA_RULES_FILE:-}"

CATEGORIES=(system security network services)

category_config() {
  printf '%s/aide-%s.conf\n' "$AIDE_CONFIG_DIR" "$1"
}

category_db() {
  printf '%s/aide-%s.db\n' "$AIDE_STATE_DIR" "$1"
}

category_new_db() {
  printf '%s/aide-%s.db.new\n' "$AIDE_STATE_DIR" "$1"
}

prepare_paths() {
  install -d -m 0700 -o root -g root "$AIDE_STATE_DIR"
  install -d -m 0755 -o root -g root "$LOG_DIR"

  touch "$LAST_LOG" "$HIST_LOG"
  chown root:root "$LAST_LOG" "$HIST_LOG"
  chmod 0644 "$LAST_LOG" "$HIST_LOG"
}

clear_db_attributes() {
  local target="$1" new_db="$2"
  if command -v chattr >/dev/null 2>&1; then
    chattr -i "$target" "$target.gz" "$new_db" "$new_db.gz" \
      "$new_db.db" "$new_db.db.gz" 2>/dev/null || true
  fi
}

remove_generated_databases() {
  local new_db="$1"
  rm -f -- "$new_db" "$new_db.gz" "$new_db.db" "$new_db.db.gz" \
    "${new_db}"* 2>/dev/null || true
}

find_generated_database() {
  local new_db="$1" candidate
  for candidate in \
    "$new_db" \
    "$new_db.gz" \
    "$new_db.db" \
    "$new_db.db.gz" \
    "${new_db}"*; do
    if [ -s "$candidate" ]; then
      printf '%s\n' "$candidate"
      return 0
    fi
  done
  return 1
}

install_generated_database() {
  local target="$1" new_db="$2" generated="$3"

  clear_db_attributes "$target" "$new_db"
  rm -f -- "$target" "$target.gz"
  if [[ "$generated" == *.gz ]]; then
    gzip -dc "$generated" > "$target"
    rm -f -- "$generated"
  else
    mv -f -- "$generated" "$target"
  fi

  if [ ! -s "$target" ]; then
    echo "La base de datos AIDE generada esta vacia o no existe: $target"
    return 1
  fi

  chown root:root "$target"
  chmod 0600 "$target"
  command -v chattr >/dev/null 2>&1 && chattr +i "$target" 2>/dev/null || true
}

run_with_config() {
  local category="$1" operation="$2" config target new_db temp_config rc generated

  config="$(category_config "$category")"
  target="$(category_db "$category")"
  new_db="$(category_new_db "$category")"

  if [ ! -f "$config" ]; then
    echo "No existe la configuracion AIDE de categoria: $config"
    return 2
  fi

  temp_config=""
  if [ -n "$EXTRA_RULES_FILE" ] && [ -f "$EXTRA_RULES_FILE" ]; then
    temp_config="$(mktemp /tmp/harresi-aide.XXXXXX.conf)"
    cat "$config" > "$temp_config"
    cat "$EXTRA_RULES_FILE" >> "$temp_config"
    config="$temp_config"
  fi

  case "$operation" in
    init)
      clear_db_attributes "$target" "$new_db"
      rm -f -- "$target" "$target.gz"
      remove_generated_databases "$new_db"
      echo "Inicializando base AIDE: $category"
      aide --config="$config" --init
      rc=$?
      generated="$(find_generated_database "$new_db" || true)"
      if [ -z "$generated" ]; then
        echo "AIDE no genero una base para la categoria: $category"
        [ -z "$temp_config" ] || rm -f -- "$temp_config"
        return 2
      fi
      if ! install_generated_database "$target" "$new_db" "$generated"; then
        [ -z "$temp_config" ] || rm -f -- "$temp_config"
        return 2
      fi
      echo "Base AIDE de $category generada correctamente."
      rc=0
      ;;
    check)
      if [ ! -s "$target" ]; then
        run_with_config "$category" init
        rc=$?
      else
        echo "Verificando base AIDE: $category"
        aide --config="$config" --check
        rc=$?
      fi
      ;;
    update)
      if [ ! -s "$target" ]; then
        run_with_config "$category" init
        rc=$?
      else
        clear_db_attributes "$target" "$new_db"
        remove_generated_databases "$new_db"
        echo "Actualizando base AIDE: $category"
        aide --config="$config" --update
        rc=$?
        generated="$(find_generated_database "$new_db" || true)"
        if [ -z "$generated" ]; then
          echo "AIDE no genero una base actualizada para la categoria: $category"
          rc=2
        elif ! install_generated_database "$target" "$new_db" "$generated"; then
          rc=2
        else
          echo "Base AIDE de $category actualizada correctamente."
        fi
      fi
      ;;
    *)
      echo "Operacion AIDE desconocida: $operation"
      rc=2
      ;;
  esac

  [ -z "$temp_config" ] || rm -f -- "$temp_config"
  return "$rc"
}

run_all() {
  local operation="$1" category rc overall=0 work_dir
  declare -A pids

  work_dir="$(mktemp -d /tmp/harresi-aide-parallel.XXXXXX)" || {
    echo "No se pudo crear el directorio temporal para las comprobaciones AIDE"
    return 2
  }

  for category in "${CATEGORIES[@]}"; do
    (
      run_with_config "$category" "$operation"
    ) >"$work_dir/$category.log" 2>&1 &
    pids["$category"]=$!
  done

  for category in "${CATEGORIES[@]}"; do
    if wait "${pids[$category]}"; then
      rc=0
    else
      rc=$?
    fi
    cat "$work_dir/$category.log"
    [ "$rc" -le "$overall" ] || overall="$rc"
  done

  rm -rf -- "$work_dir"
  return "$overall"
}

run_requested_operation() {
  case "${1:-check}" in
    check|--check) run_all check ;;
    init|--init-all) run_all init ;;
    update|--update-all) run_all update ;;
    --help|-h)
      echo "Uso: $0 [--check|--init-all|--update-all]"
      return 0
      ;;
    *)
      echo "Uso: $0 [--check|--init-all|--update-all]"
      return 2
      ;;
  esac
}

prepare_paths

{
  echo "=== AIDE RUN START $(date '+%Y-%m-%d %H:%M:%S') source=$RUN_SOURCE mode=${1:-check} ==="

  if ! command -v aide >/dev/null 2>&1; then
    echo "AIDE no esta instalado; se omite la comprobacion de integridad."
    rc=0
  else
    run_requested_operation "${1:-check}"
    rc=$?
  fi

  echo "=== AIDE RUN END $(date '+%Y-%m-%d %H:%M:%S') source=$RUN_SOURCE mode=${1:-check} rc=$rc ==="
  exit "$rc"
} 2>&1 | tee "$LAST_LOG" | tee -a "$HIST_LOG" >/dev/null

exit "${PIPESTATUS[0]}"
