#!/usr/bin/env bash
# policy: dev_mon — CheckMK monitoring agent + local checks.
#   dev_mon_base      install agent, register host, installed-packages check
#   dev_mon_timeshift Timeshift snapshot age check
#   dev_mon_power     CPU package power via RAPL / lm-sensors
#   usr_mon_logins    SSH login audit check (user group, but requires dev_mon_base)
# (ClamAV scan reporting lives in its own policy, dev_mon_clamscan, which runs
#  the scan and writes the Ansipa_ClamScan local check the agent here collects.)
# Revert: deregisters the host and removes the agent socket when dev_mon_base leaves.

MON_LOCAL_DIR="/usr/lib/check_mk_agent/local"
MON_STATE="$STATE_DIR/mon-registered"
MON_CHECKS_STATE="$STATE_DIR/mon-checks"
MON_CMK_CREDS="$STATE_DIR/mon-cmk-creds"
[[ -f "$MON_CHECKS_STATE" ]] || touch "$MON_CHECKS_STATE"

_mon_dev_active() { local n="$1"; for _m in "${ACTIVE_MON_DEV[@]+"${ACTIVE_MON_DEV[@]}"}"; do [[ "$_m" == "$n" ]] && return 0; done; return 1; }
_mon_usr_active() { local n="$1"; for _m in "${ACTIVE_MON_USR[@]+"${ACTIVE_MON_USR[@]}"}"; do [[ "$_m" == "$n" ]] && return 0; done; return 1; }

CMK_URL="" CMK_SITE="" CMK_USER="" CMK_SECRET="" CMK_API="" CMK_HOST_PORT=""
_cmk_parse_creds() {
    local _desc
    _desc=$(ipa hostgroup-show dev_mon_base --all 2>/dev/null \
        | awk -F': ' '/Description:/{print $2; exit}' || true)
    if [[ "$_desc" =~ ^cmk://([^/]+)(/[^:]+):([^:]+):(.+)$ ]]; then
        CMK_HOST_PORT="${BASH_REMATCH[1]}"
        CMK_SITE="${BASH_REMATCH[2]#/}"
        CMK_USER="${BASH_REMATCH[3]}"
        CMK_SECRET="${BASH_REMATCH[4]}"
        CMK_URL="http://${CMK_HOST_PORT}"
        CMK_API="${CMK_URL}/${CMK_SITE}/check_mk/api/1.0"
    fi
}

_WANT_CHECKS=()
_cmk_write_check() {
    local _name="$1" _body="$2"
    mkdir -p "$MON_LOCAL_DIR"
    printf '%s\n' "$_body" > "$MON_LOCAL_DIR/$_name"
    chmod 755 "$MON_LOCAL_DIR/$_name"
    _WANT_CHECKS+=("$_name")
}

if _mon_dev_active "base"; then
    _cmk_parse_creds

    if [[ -z "$CMK_SECRET" ]]; then
        warn "dev_mon_base: no CheckMK credentials in IPA dev_mon_base description"
        warn "  — run ansipa-checkmk-setup on the FreeIPA container first"
    else
        # ── Install CheckMK agent ─────────────────────────────────────────────
        _AGENT_INSTALLED=false
        if rpm -q check-mk-agent &>/dev/null || command -v check_mk_agent &>/dev/null; then
            _AGENT_INSTALLED=true
        else
            log "dev_mon_base: downloading CheckMK agent from ${CMK_URL}"
            _RPM=$(curl -sf -u "${CMK_USER}:${CMK_SECRET}" \
                    "${CMK_URL}/${CMK_SITE}/check_mk/agents/" 2>/dev/null \
                | grep -oE 'check-mk-agent-[0-9][^"]*\.noarch\.rpm' | head -1 || true)
            if [[ -n "$_RPM" ]]; then
                curl -sf -u "${CMK_USER}:${CMK_SECRET}" \
                    "${CMK_URL}/${CMK_SITE}/check_mk/agents/${_RPM}" \
                    -o /tmp/cmk-agent.rpm 2>/dev/null \
                && (rpm -ivh --nodeps /tmp/cmk-agent.rpm 2>/dev/null \
                    || dnf install -y /tmp/cmk-agent.rpm 2>/dev/null) \
                && _AGENT_INSTALLED=true \
                || warn "dev_mon_base: CheckMK RPM install failed"
                rm -f /tmp/cmk-agent.rpm
            fi
            if [[ "$_AGENT_INSTALLED" == false ]]; then
                curl -sf -u "${CMK_USER}:${CMK_SECRET}" \
                    "${CMK_URL}/${CMK_SITE}/check_mk/agents/check_mk_agent.linux" \
                    -o /usr/local/bin/check_mk_agent 2>/dev/null \
                && chmod 755 /usr/local/bin/check_mk_agent \
                && _AGENT_INSTALLED=true \
                || warn "dev_mon_base: shell agent download failed"
            fi
        fi

        if [[ "$_AGENT_INSTALLED" == true ]] && [[ ! -f /etc/systemd/system/check_mk.socket ]]; then
            cat > /etc/systemd/system/check_mk.socket <<'UNIT'
[Unit]
Description=Check_MK agent socket (ansipa-managed)

[Socket]
ListenStream=6556
Accept=yes

[Install]
WantedBy=sockets.target
UNIT
            cat > /etc/systemd/system/check_mk@.service <<'UNIT'
[Unit]
Description=Check_MK per-connection agent (ansipa-managed)

[Service]
ExecStart=/usr/local/bin/check_mk_agent
StandardInput=socket
StandardOutput=socket
StandardError=null
UNIT
            systemctl daemon-reload
        fi
        if [[ "$_AGENT_INSTALLED" == true ]]; then
            systemctl enable --now check_mk.socket 2>/dev/null \
                && log "dev_mon_base: check_mk.socket enabled (port 6556)" \
                || warn "dev_mon_base: could not enable check_mk.socket"
        fi

        # ── Register host in CheckMK ──────────────────────────────────────────
        _HOST_IP=$(hostname -I 2>/dev/null | awk '{print $1}' || echo "")
        _CMK_REG_URL="${CMK_API}/objects/host_config/${HOST_FQDN}"
        _ALREADY_REG=false
        curl -sf -o /dev/null \
            -H "Authorization: Bearer ${CMK_USER} ${CMK_SECRET}" \
            "$_CMK_REG_URL" 2>/dev/null && _ALREADY_REG=true || true

        if [[ "$_ALREADY_REG" == false ]]; then
            _reg_body="{\"host_name\":\"${HOST_FQDN}\",\"folder\":\"/ansipa\""
            [[ -n "$_HOST_IP" ]] && _reg_body+=",\"attributes\":{\"ipaddress\":\"${_HOST_IP}\"}"
            _reg_body+="}"
            _reg_http=$(curl -sf -o /dev/null -w '%{http_code}' \
                -X POST "${CMK_API}/domain-types/host_config/collections/all" \
                -H "Authorization: Bearer ${CMK_USER} ${CMK_SECRET}" \
                -H "Content-Type: application/json" \
                -H "Accept: application/json" \
                -d "$_reg_body" 2>/dev/null || echo "000")
            if [[ "$_reg_http" =~ ^2 ]]; then
                log "dev_mon_base: host ${HOST_FQDN} registered in CheckMK"
                curl -sf \
                    -X POST "${CMK_API}/domain-types/service_discovery_run/actions/start/invoke" \
                    -H "Authorization: Bearer ${CMK_USER} ${CMK_SECRET}" \
                    -H "Content-Type: application/json" \
                    -H "Accept: application/json" \
                    -d "{\"host_name\":\"${HOST_FQDN}\",\"mode\":\"refresh\"}" \
                    >/dev/null 2>&1 || true
                curl -sf \
                    -X POST "${CMK_API}/domain-types/activation_run/actions/activate-changes/invoke" \
                    -H "Authorization: Bearer ${CMK_USER} ${CMK_SECRET}" \
                    -H "Content-Type: application/json" \
                    -H "Accept: application/json" \
                    -d '{"redirect":false,"sites":[],"force_foreign_changes":true}' \
                    >/dev/null 2>&1 || true
                printf '%s\n' "${CMK_API}" > "$MON_STATE"
            else
                warn "dev_mon_base: host registration failed (HTTP ${_reg_http})"
            fi
        else
            log "dev_mon_base: ${HOST_FQDN} already registered in CheckMK"
            printf '%s\n' "${CMK_API}" > "$MON_STATE"
        fi
        unset _HOST_IP _CMK_REG_URL _ALREADY_REG _reg_body _reg_http

        # ── cmk-agent-ctl: register for TLS/push transport ───────────────────
        if command -v cmk-agent-ctl &>/dev/null; then
            _CMK_RECV_HOST=$(echo "$CMK_URL" | sed 's|http://||;s|:.*||')
            _CMK_CTL_STATUS_FILE="$STATE_DIR/mon-cmk-ctl-mode"
            _ALREADY_CTL=$(cmk-agent-ctl status 2>/dev/null | grep -c "${_CMK_RECV_HOST}/${CMK_SITE}" || true)
            if [[ "${_ALREADY_CTL:-0}" -eq 0 ]]; then
                cmk-agent-ctl register \
                    --server "${_CMK_RECV_HOST}:8000" \
                    --site "${CMK_SITE}" \
                    --user "${CMK_USER}" \
                    --password "${CMK_SECRET}" \
                    --hostname "${HOST_FQDN}" \
                    --trust-cert &>/dev/null \
                    && log "dev_mon_base: cmk-agent-ctl registered with ${_CMK_RECV_HOST}:8000" \
                    || warn "dev_mon_base: cmk-agent-ctl registration failed (non-fatal)"
            fi
            _CTL_MODE=$(cmk-agent-ctl status 2>/dev/null | awk '/Connection mode:/{print $NF; exit}')
            echo "$_CTL_MODE" > "$_CMK_CTL_STATUS_FILE" 2>/dev/null || true
            if [[ "$_CTL_MODE" == "push-agent" ]]; then
                log "dev_mon_base: push mode active — setting up push timer"
                cmk-agent-ctl push 2>/dev/null || true
                if [[ ! -f /etc/systemd/system/cmk-agent-push.timer ]]; then
                    cat > /etc/systemd/system/cmk-agent-push.service <<'UNIT'
[Unit]
Description=CheckMK agent push (ansipa-managed)
[Service]
Type=oneshot
ExecStart=/usr/bin/cmk-agent-ctl push
UNIT
                    cat > /etc/systemd/system/cmk-agent-push.timer <<'UNIT'
[Unit]
Description=CheckMK agent push timer (ansipa-managed)
[Timer]
OnBootSec=30s
OnUnitActiveSec=60s
[Install]
WantedBy=timers.target
UNIT
                    systemctl daemon-reload
                    systemctl enable --now cmk-agent-push.timer 2>/dev/null \
                        && log "dev_mon_base: cmk-agent-push.timer enabled (push every 60s)" \
                        || warn "dev_mon_base: could not enable push timer"
                fi
            fi
            unset _CMK_RECV_HOST _ALREADY_CTL _CTL_MODE _CMK_CTL_STATUS_FILE
        fi

        # ── Persist CMK credentials for push scripts ──────────────────────────
        printf 'CMK_API=%q\nCMK_USER=%q\nCMK_SECRET=%q\n' \
            "$CMK_API" "$CMK_USER" "$CMK_SECRET" > "$MON_CMK_CREDS"
        chmod 600 "$MON_CMK_CREDS"

        # ── dev_mon_base: installed-packages local check ──────────────────────
        _cmk_write_check "ansipa_packages" '#!/bin/bash
PKG_COUNT=0
RECENT="n/a"
if command -v rpm &>/dev/null; then
    PKG_COUNT=$(rpm -qa --qf "%{NAME}\n" 2>/dev/null | wc -l)
    RECENT=$(rpm -qa --qf "%{INSTALLTIME} %{NAME}\n" 2>/dev/null | sort -rn | head -5 | awk "{print \$2}" | tr "\n" "," | sed "s/,\$//")
elif command -v dpkg &>/dev/null; then
    PKG_COUNT=$(dpkg --list 2>/dev/null | grep -c "^ii")
    RECENT=$(grep " install " /var/log/dpkg.log 2>/dev/null | tail -5 | awk "{print \$4}" | tr "\n" "," | sed "s/,\$//")
elif command -v pacman &>/dev/null; then
    PKG_COUNT=$(pacman -Qq 2>/dev/null | wc -l)
    RECENT=$(grep "\[ALPM\] installed" /var/log/pacman.log 2>/dev/null | tail -5 | awk "{print \$4}" | tr "\n" "," | sed "s/,\$//")
fi
echo "0 Ansipa_Packages packages=${PKG_COUNT} ${PKG_COUNT} packages installed. Recent: ${RECENT}"'

        # NOTE: ClamAV scan reporting is handled by the dev_mon_clamscan policy,
        # which runs the scan and writes its own CheckMK local check
        # (ansipa_clamscan) directly — so the old dev_mon_malware group and its
        # ansipa_clamav check are gone. The stale ansipa_clamav file is cleaned
        # up automatically by the local-check cleanup below (it is no longer in
        # _WANT_CHECKS).

        # ── dev_mon_timeshift: snapshot age check ─────────────────────────────
        if _mon_dev_active "timeshift"; then
            _cmk_write_check "ansipa_timeshift" '#!/bin/bash
if ! command -v timeshift &>/dev/null; then
    echo "3 Ansipa_Timeshift - Timeshift not installed on this host"
    exit 0
fi
LAST=$(timeshift --list 2>/dev/null | grep -E "^\s*[0-9]+" \
    | awk "{print \$3, \$4}" | tail -1 || echo "")
if [[ -z "$LAST" ]]; then
    echo "2 Ansipa_Timeshift last_backup_days=9999;5;10 No Timeshift snapshots found"
    exit 0
fi
LAST_TS=$(date -d "$LAST" +%s 2>/dev/null || echo 0)
DAYS=$(( ( $(date +%s) - LAST_TS ) / 86400 ))
PERF="last_backup_days=${DAYS};5;10"
if [[ "$DAYS" -ge 10 ]]; then
    echo "2 Ansipa_Timeshift ${PERF} Last backup: ${DAYS}d ago (${LAST}) — CRIT"
elif [[ "$DAYS" -ge 5 ]]; then
    echo "1 Ansipa_Timeshift ${PERF} Last backup: ${DAYS}d ago (${LAST}) — WARN"
else
    echo "0 Ansipa_Timeshift ${PERF} Last backup: ${DAYS}d ago (${LAST})"
fi'
        fi

        # ── dev_mon_power: CPU package power via RAPL / lm-sensors ──────────
        if _mon_dev_active "power"; then
            _cmk_write_check "ansipa_power" '#!/bin/bash
RAPL=/sys/class/powercap/intel-rapl/intel-rapl:0/energy_uj
POWER_W=0; SOURCE=""
if [[ -r "$RAPL" ]]; then
    E1=$(cat "$RAPL" 2>/dev/null || echo 0)
    sleep 1
    E2=$(cat "$RAPL" 2>/dev/null || echo 0)
    if [[ "$E2" -gt "$E1" ]]; then
        POWER_W=$(( (E2 - E1) / 1000000 ))
        SOURCE="RAPL"
    fi
fi
if [[ -z "$SOURCE" ]] && command -v sensors &>/dev/null; then
    W=$(sensors 2>/dev/null | grep -iE "package.*power|cpu.*power" \
        | grep -oP "[0-9]+\.[0-9]+" | head -1 | cut -d. -f1 || echo 0)
    [[ "${W:-0}" -gt 0 ]] && POWER_W="$W" && SOURCE="lm-sensors"
fi
if [[ -z "$SOURCE" ]]; then
    echo "3 Ansipa_Power power_w=0 Power monitoring unavailable (needs Intel RAPL or lm-sensors)"
    exit 0
fi
PERF="power_w=${POWER_W};65;95"
if [[ "$POWER_W" -ge 95 ]]; then
    echo "2 Ansipa_Power ${PERF} CPU power: ${POWER_W}W via ${SOURCE} — CRIT"
elif [[ "$POWER_W" -ge 65 ]]; then
    echo "1 Ansipa_Power ${PERF} CPU power: ${POWER_W}W via ${SOURCE} — WARN"
else
    echo "0 Ansipa_Power ${PERF} CPU power: ${POWER_W}W via ${SOURCE}"
fi'
        fi

        # ── usr_mon_logins: SSH login audit (requires dev_mon_base agent) ─────
        if _mon_usr_active "logins"; then
            _cmk_write_check "ansipa_logins" '#!/bin/bash
count_journal() {
    local pat="$1"
    journalctl -u sshd --since "24 hours ago" --no-pager -q 2>/dev/null \
        | grep -c "$pat" 2>/dev/null || true
}
count_secure() {
    local pat="$1"
    { grep "$(date "+%b %e")" /var/log/secure 2>/dev/null
      grep "$(date -d yesterday "+%b %e" 2>/dev/null || true)" /var/log/secure 2>/dev/null; } \
      | grep -c "$pat" 2>/dev/null || true
}
if command -v journalctl &>/dev/null && journalctl -u sshd --since "24 hours ago" -q &>/dev/null; then
    ACCEPTED=$(count_journal "Accepted ")
    FAILED=$(count_journal "Failed ")
    INVALID=$(count_journal "Invalid user ")
else
    ACCEPTED=$(count_secure "sshd.*Accepted")
    FAILED=$(count_secure "sshd.*Failed")
    INVALID=$(count_secure "sshd.*Invalid user")
fi
PERF="successful=${ACCEPTED};; failed=${FAILED};10;50 invalid=${INVALID};5;20"
MSG="Logins 24h: ${ACCEPTED} ok / ${FAILED} failed / ${INVALID} invalid user"
if [[ "$FAILED" -ge 50 ]] || [[ "$INVALID" -ge 20 ]]; then
    echo "2 Ansipa_Logins ${PERF} HIGH ACTIVITY — ${MSG}"
elif [[ "$FAILED" -ge 10 ]] || [[ "$INVALID" -ge 5 ]]; then
    echo "1 Ansipa_Logins ${PERF} ELEVATED — ${MSG}"
else
    echo "0 Ansipa_Logins ${PERF} ${MSG}"
fi'
        fi

    fi  # CMK_SECRET non-empty

else
    # ── Revert: host left dev_mon_base → deregister and clean up ─────────────
    if [[ -f "$MON_STATE" ]]; then
        _OLD_CMK_API=$(cat "$MON_STATE" 2>/dev/null || true)
        if [[ -n "$_OLD_CMK_API" ]] && [[ -f "$MON_CMK_CREDS" ]]; then
            # shellcheck source=/dev/null
            source "$MON_CMK_CREDS" 2>/dev/null || true
            _del_http=$(curl -sf -o /dev/null -w '%{http_code}' \
                -X DELETE "${_OLD_CMK_API}/objects/host_config/${HOST_FQDN}" \
                -H "Authorization: Bearer ${CMK_USER:-} ${CMK_SECRET:-}" \
                -H "Accept: application/json" 2>/dev/null || echo "000")
            if [[ "$_del_http" =~ ^(2|404) ]]; then
                log "Deregistered ${HOST_FQDN} from CheckMK (HTTP ${_del_http})"
                curl -sf \
                    -X POST "${_OLD_CMK_API}/domain-types/activation_run/actions/activate-changes/invoke" \
                    -H "Authorization: Bearer ${CMK_USER:-} ${CMK_SECRET:-}" \
                    -H "Content-Type: application/json" \
                    -H "Accept: application/json" \
                    -d '{"redirect":false,"sites":[],"force_foreign_changes":true}' \
                    >/dev/null 2>&1 || true
            else
                warn "CheckMK deregistration returned HTTP ${_del_http} — may need manual cleanup"
            fi
        fi
        rm -f "$MON_STATE" "$MON_CMK_CREDS"
        systemctl disable --now check_mk.socket 2>/dev/null || true
        systemctl disable --now cmk-agent-push.timer 2>/dev/null || true
        command -v cmk-agent-ctl &>/dev/null && cmk-agent-ctl delete-all 2>/dev/null || true
        log "dev_mon_base reverted: check_mk.socket disabled, host deregistered"
        unset _OLD_CMK_API _del_http
    fi
fi

# ── Cleanup: remove local check scripts for groups no longer active ───────────
while IFS= read -r _old_check; do
    [[ -z "$_old_check" ]] && continue
    _still_wanted=false
    for _wc in "${_WANT_CHECKS[@]+"${_WANT_CHECKS[@]}"}"; do
        [[ "$_wc" == "$_old_check" ]] && _still_wanted=true && break
    done
    if [[ "$_still_wanted" == false ]] && [[ -f "$MON_LOCAL_DIR/$_old_check" ]]; then
        rm -f "$MON_LOCAL_DIR/$_old_check"
        log "Removed CheckMK local check: $_old_check (group left)"
    fi
done < "$MON_CHECKS_STATE"
unset _old_check _still_wanted _wc

if [[ ${#_WANT_CHECKS[@]} -gt 0 ]]; then
    printf '%s\n' "${_WANT_CHECKS[@]+"${_WANT_CHECKS[@]}"}" | sort -u > "$MON_CHECKS_STATE"
else
    > "$MON_CHECKS_STATE"
fi
unset _WANT_CHECKS _AGENT_INSTALLED
