#!/usr/bin/bash
#
# Copyright(C) 2024 @ ZENETYS
# This script is licensed under MIT License (http://opensource.org/licenses/MIT)
#

set -f
export LC_ALL=C
PROGNAME=${0##*/}
QUIET=
RULES_FILE=
DRY_RUN=
YES_DO_IT=
ZLC_ENV=${ZLC_ENV:-/etc/logcenter/logcenter.conf}

function exit_usage() {
    local status=${1:-0}
    [[ "$status" != "0" ]] && exec >&2
    echo "\
Usage: $PROGNAME -r RULES -n|-y [-q]
Delete old logs according to retention rules

Available options:
  -q, --quiet           Display errors only.
  -r, --rules           Path to retention rules file.
  -n, --dry-run         Dry-run mode, do not change anything.
  -y, --yes-do-it       Yes, do it. Either -n or -y are required.
  -h, --help            Display this help."
    exit "$status"
}

function _log() {
    local now=$(date +%Y-%m-%dT%H:%M:%S.%3N%:z)
    local LOGGER_SEVERITY=${LOGGER_SEVERITY:-info}
    local STDOUT_LABEL=${STDOUT_LABEL:-INFO}
    [[ -t 1 ]] || logger -t "$PROGNAME[$$]" -p "$LOGGER_SEVERITY" -- "${DRY_RUN:+DRY-RUN ** }$*"
    [[ -n $QUIET && $STDOUT_LABEL == INFO ]] && return 0
    echo "$now $STDOUT_LABEL $PROGNAME: ${DRY_RUN:+DRY-RUN ** }$*"
}

function info() { LOGGER_SEVERITY=info STDOUT_LABEL=INFO _log "$@"; }
function warning() { LOGGER_SEVERITY=warning STDOUT_LABEL=WARNING _log "$@"; }
function error() { LOGGER_SEVERITY=err STDOUT_LABEL=ERROR _log "$@"; }
function fatal() { LOGGER_SEVERITY=crit STDOUT_LABEL=FATAL _log "$@"; exit 2; }

function load_env_or_fatal() {
    source "$ZLC_ENV" || fatal "Failed to source $ZLC_ENV"
    [[ -z $ZLC_ARCHIVES_DIR ]] && fatal 'Bad environment, ZLC_ARCHIVES_DIR not set'
    return 0
}

[[ $UID == 0 ]] ||
    fatal 'This script must be run as root'

load_env_or_fatal

while (( $# > 0 )); do
    case "$1" in
        -q|--quiet) QUIET=1 ;;
        -r|--rules) RULES_FILE=$2; shift ;;
        -n|--dry-run) DRY_RUN=1 ;;
        -y|--yes-do-it) YES_DO_IT=1 ;;
        -h|--help) exit_usage ;;
        *) exit_usage 1 ;;
    esac
    shift
done

[[ "${DRY_RUN}${YES_DO_IT}" ==  "1" ]] || fatal 'Either --yes-do-it or --dry-run are required'
[[ -z $RULES_FILE ]] && { info 'No retention rules file, skip'; exit 0; }
[[ -f $RULES_FILE && -r $RULES_FILE ]] || fatal 'Invalid retention rules file'

rules_pattern=()
rules_days=()
line=0

while read -r pattern days; do
    (( line++ ))
    [[ -z $pattern ]] && continue # empty line
    [[ ${pattern:0:1} == \# ]] && continue # comment

    if [[ -z $days || ( -n ${days//[0-9]} && $days != - ) ]]; then
        error "Invalid retention rule at line $line"
        continue
    fi

    rules_pattern+=( "$pattern" )
    rules_days+=( "$days" )
done < "$RULES_FILE"

total=0
success=0
error=0

time_main_start=$(date +%s)

info "BEGIN TASK -- ZLC_ARCHIVES_DIR=$ZLC_ARCHIVES_DIR, RULES_FILE=$RULES_FILE"

while read -r dir; do
    match=
    for (( i=0; i < ${#rules_pattern[@]}; i++ )) ; do
        [[ $dir == ${rules_pattern[i]} ]] && match=1 && break
    done
    if [[ -z $match ]]; then
        info "Directory $dir, no match"
        continue
    fi
    pattern=${rules_pattern[i]}
    days=${rules_days[i]}
    info "Directory $dir, match rule $((i+1)), pattern $pattern, days $days"
    [[ $days == - ]] && continue

    # BEGIN $dir files removal
    while read -r; do
        info "Delete $REPLY"
        (( total++ ))
        [[ -n $DRY_RUN ]] && continue

        rm "$REPLY"
        if (( $? == 0 )); then
            (( success++ ))
        else
            (( error++ ))
            error "Call to rm failed, error deleting $REPLY"
        fi
    done < <(
        find -L "$ZLC_ARCHIVES_DIR/$dir/" \
            -mindepth 2 \
            -type f \
            -name "????-??-??*.log*" \
            -mtime "+$days" |
        sort -V
    )
    # END $dir files removal

done < <(
    find -L "$ZLC_ARCHIVES_DIR/" \
        -mindepth 1 \
        -maxdepth 1 \
        -name '[a-zA-Z0-9]*' \
        -not -name lost+found \
        -printf '%P\n' |
    sort -V
)

time_main_stop=$(date +%s)
message="END TASK -- elapsed: $(( time_main_stop - time_main_start ))s, \
files: $total, success: $success, error: $error"

if (( error > 0 )); then
    error "$message"
    exit 1
else
    info "$message"
    exit 0
fi
