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


##############################################################
# Логирование
##############################################################
LOG_DIR="/var/log"
mkdir -p "$LOG_DIR"
LOGFILE="$LOG_DIR/ds-repl-status.$(date +%F.%H-%M-%S).log"


##############################################################
# Цвета
##############################################################
GREEN=$'\e[32m'
RED=$'\e[31m'
RESET=$'\e[0m'


##############################################################
# DOMAIN → BASE_DN
##############################################################
DOMAIN=$(echo "$HOSTNAME" | sed 's/^[^.]*\.//')
BASE_DN=$(echo "$DOMAIN" | awk -F. '{ for(i=1;i<=NF;i++) printf "dc=%s%s",$i,(i<NF?",":"") }')

echo "DOMAIN: $DOMAIN"
echo "BASE_DN: $BASE_DN"


##############################################################
# Получение SRV записей Домена
##############################################################
mapfile -t SRV_ARRAY < <(
    dig +short _ldap._tcp."$DOMAIN" SRV | awk '{print $4}' | sed 's/\.$//' | sort
)
if (( ${#SRV_ARRAY[@]} == 0 )); then
    echo "Не найдены SRV записи для домена $DOMAIN" | tee -a "$LOGFILE"
    exit 1
fi

echo "Контроллеры:" | tee -a "$LOGFILE"
printf '%s\n' "${SRV_ARRAY[@]}" | tee -a "$LOGFILE"

TMPDIR=$(mktemp -d /tmp/ldap_chk.XXXX)
trap 'rm -rf "$TMPDIR"' EXIT


##############################################################
#Логин
##############################################################
read -p "Введите CN (например Administrator): " BIND_DN


##############################################################
# Ввод пароля (скрытый)
##############################################################
read_password() {
    local PROMPT="$1" PASS=""
    echo -n "$PROMPT"
    stty -echo
    while IFS= read -r -s -n1 char; do
        [[ $char == $'\n' ]] && break
        [[ $char == $'\0' ]] && break
        if [[ $char == $'\177' ]]; then
            PASS="${PASS%?}"
            echo -ne "\b \b"
            continue
        fi
        PASS+="$char"
        echo -n '*'
    done
    stty echo
    echo
    REPLY="$PASS"
}

read_password "Введите пароль: "
PASSWORD="$REPLY"


##############################################################
# Ldap-запрос к контроллерам домена
##############################################################
ldapsearch_base() {
    local HOST="$1" FILTER="$2"
    ldapsearch -LLL -H "ldap://$HOST" \
        -D "cn=$BIND_DN,ou=users,$BASE_DN" \
        -w "$PASSWORD" \
        -E pr=1000/noprompt \
        -b "$BASE_DN" "$FILTER" dn uid 2>/dev/null \
        | awk '
            /^dn: /  { dn=$0; sub(/^dn: /,"",dn) }
            /^uid: / { uid=$0; sub(/^uid: /,"",uid); print dn "|" uid }
        '
}

echo "Опрашиваем контроллеры..."
for HOST in "${SRV_ARRAY[@]}"; do
    SAFE=$(echo "$HOST" | tr '.' '_')
    ldapsearch_base "$HOST" "(objectClass=user)" > "$TMPDIR/u_$SAFE"
    ldapsearch_base "$HOST" "(|(objectClass=group)(objectClass=posixgroup))" > "$TMPDIR/g_$SAFE"
    ldapsearch_base "$HOST" "(objectClass=host)" > "$TMPDIR/h_$SAFE"
done

declare -A DATA_USERS DATA_GROUPS DATA_HOSTS
declare -A COUNT_USERS COUNT_GROUPS COUNT_HOSTS
declare -A STATUS_ARR
declare -A MISMATCH_MAP


##############################################################
# Сортировка полученных данных со всех КД
##############################################################
echo "Идет обработка полученных данных..."
for HOST in "${SRV_ARRAY[@]}"; do
    SAFE=$(echo "$HOST" | tr '.' '_')
    DATA_USERS["$HOST"]=$(cat "$TMPDIR/u_$SAFE")
    DATA_GROUPS["$HOST"]=$(cat "$TMPDIR/g_$SAFE")
    DATA_HOSTS["$HOST"]=$(cat "$TMPDIR/h_$SAFE")

    COUNT_USERS["$HOST"]=$(grep -c . <<< "${DATA_USERS[$HOST]}")
    COUNT_GROUPS["$HOST"]=$(grep -c . <<< "${DATA_GROUPS[$HOST]}")
    COUNT_HOSTS["$HOST"]=$(grep -c . <<< "${DATA_HOSTS[$HOST]}")

done


##############################################################
# Сравнение полученных данных с каждого КД со всеми остальными
##############################################################
for HOST in "${SRV_ARRAY[@]}"; do
    STATUS_ARR["$HOST"]="OK"
    for OTHER in "${SRV_ARRAY[@]}"; do
        [[ "$HOST" == "$OTHER" ]] && continue
        for TYPE in u g h; do
            case $TYPE in
                u) SRC="${DATA_USERS[$OTHER]}"; DST="${DATA_USERS[$HOST]}"; LABEL="Users";;
                g) SRC="${DATA_GROUPS[$OTHER]}"; DST="${DATA_GROUPS[$HOST]}"; LABEL="Groups";;
                h) SRC="${DATA_HOSTS[$OTHER]}"; DST="${DATA_HOSTS[$HOST]}"; LABEL="Hosts";;
            esac
            missing=$(comm -23 <(echo "$SRC" | sort) <(echo "$DST" | sort))
            if [[ -n "$missing" ]]; then
                STATUS_ARR["$HOST"]="BAD"
                while IFS= read -r line; do
                    if [[ -n "${MISMATCH_MAP[$HOST]+x}" ]]; then
                        MISMATCH_MAP["$HOST"]+=$'\n'"$LABEL: $line (не найден на $HOST)"
                    else
                        MISMATCH_MAP["$HOST"]="$LABEL: $line (не найден на $HOST)"
                    fi
                done <<< "$missing"
            fi
        done
    done
done


##############################################################
# Краткий вывод в консоль
##############################################################
for HOST in "${SRV_ARRAY[@]}"; do
    [[ "${STATUS_ARR[$HOST]}" == "BAD" ]] && echo "Обнаружена ошибка репликации на $HOST. См. лог: $LOGFILE"
done


##############################################################
# Лог: детальные различия
##############################################################
{
    echo "======================"
    echo "DIFF LOG"
    echo "======================"
    for HOST in "${SRV_ARRAY[@]}"; do
        echo -e "\nКД: $HOST"
        [[ -n "${MISMATCH_MAP[$HOST]+x}" ]] && echo -e "${MISMATCH_MAP[$HOST]}" || echo "Все записи (users, groups, hosts) совпадают с большенством КД"
    done
} >> "$LOGFILE"


##############################################################
# Вывод таблицы 
##############################################################

# Заголовки
HDR_KD="DC"
HDR_USERS="Users"
HDR_GROUPS="Groups"
HDR_HOSTS="Hosts"
HDR_TOTAL="Total"
HDR_STATUS="Status"

# Массивы строк (без цветов!)
TABLE_KD=()
TABLE_USERS=()
TABLE_GROUPS=()
TABLE_HOSTS=()
TABLE_TOTAL=()
TABLE_STATUS=()

# Добавляем заголовки
TABLE_KD+=("$HDR_KD")
TABLE_USERS+=("$HDR_USERS")
TABLE_GROUPS+=("$HDR_GROUPS")
TABLE_HOSTS+=("$HDR_HOSTS")
TABLE_TOTAL+=("$HDR_TOTAL")
TABLE_STATUS+=("$HDR_STATUS")

# Данные
for HOST in "${SRV_ARRAY[@]}"; do
    TOTAL=$(( COUNT_USERS[$HOST]+COUNT_GROUPS[$HOST]+COUNT_HOSTS[$HOST] ))
    TABLE_KD+=("$HOST")
    TABLE_USERS+=("${COUNT_USERS[$HOST]}")
    TABLE_GROUPS+=("${COUNT_GROUPS[$HOST]}")
    TABLE_HOSTS+=("${COUNT_HOSTS[$HOST]}")
    TABLE_TOTAL+=("$TOTAL")
    TABLE_STATUS+=("${STATUS_ARR[$HOST]}")
done

# Функция поиска максимальной ширины
max_width() {
    local max=0
    for s in "$@"; do
        (( ${#s} > max )) && max=${#s}
    done
    echo "$max"
}

# Вычисляем ширины
W_KD=$(max_width "${TABLE_KD[@]}")
W_USERS=$(max_width "${TABLE_USERS[@]}")
W_GROUPS=$(max_width "${TABLE_GROUPS[@]}")
W_HOSTS=$(max_width "${TABLE_HOSTS[@]}")
W_TOTAL=$(max_width "${TABLE_TOTAL[@]}")
W_STATUS=$(max_width "${TABLE_STATUS[@]}")

# Рисование линий
draw_line() {
    printf "+"
    for w in "$W_KD" "$W_USERS" "$W_GROUPS" "$W_HOSTS" "$W_TOTAL" "$W_STATUS"; do
        printf '%*s+' $((w+2)) '' | tr ' ' '-'
    done
    printf "\n"
}

# Печать строки
print_row() {
    printf "| %-*s | %*s | %*s | %*s | %*s | %-*s |\n" \
        "$W_KD" "$1" \
        "$W_USERS" "$2" \
        "$W_GROUPS" "$3" \
        "$W_HOSTS" "$4" \
        "$W_TOTAL" "$5" \
        "$W_STATUS" "$6"
}

# Заголовок
draw_line
print_row "$HDR_KD" "$HDR_USERS" "$HDR_GROUPS" "$HDR_HOSTS" "$HDR_TOTAL" "$HDR_STATUS"
draw_line

# Данные
for i in "${!SRV_ARRAY[@]}"; do
    HOST="${TABLE_KD[$((i+1))]}"
    U="${TABLE_USERS[$((i+1))]}"
    G="${TABLE_GROUPS[$((i+1))]}"
    H="${TABLE_HOSTS[$((i+1))]}"
    T="${TABLE_TOTAL[$((i+1))]}"
    S="${TABLE_STATUS[$((i+1))]}"

# Цвет ТОЛЬКО при выводе
    if [[ "$S" == "OK" ]]; then
        S="OK    "
        S="${GREEN}${S}${RESET}"
    else
        S="BAD   "
        S="${RED}${S}${RESET}"
    fi


    print_row "$HOST" "$U" "$G" "$H" "$T" "$S"
done

draw_line

exit 0