#!/bin/bash
# =============================================================================
# runtime-update.sh -- Patch Orchestration & System Update
# =============================================================================
# Category: runtime | Bird: cormorant | Tier: 1 | Node-aware: yes
# AlmaLinux 9 across all nodes. Idempotent, safe to run on any node.
#
# Actions:
#   --status           JSON pill (pending updates count, last update age)
#   --check            Check for available updates (read-only)
#   --security         Apply security patches only (write/veil-gated)
#   --full             Full dnf update (write/veil-gated)
#   --kernel           Show kernel version + available upgrades
#   --reboot-required  Check if reboot is needed
#   --history          Show recent dnf history
#   --rollback <id>    Rollback a dnf transaction (break-glass)
#   --verify           Verify package integrity (rpm -Va)
#   --send             Check + report
#   --digest           Full update report
#   --flush            Truncate logs
# =============================================================================

TOOL_NAME="runtime-update"
TOOL_VERSION="3.0.0"
TOOL_TIER=1
TOOL_BIRD="cormorant"
TOOL_LOG="/opt/binary/logs/tools/${TOOL_NAME}.log"

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
if [[ -f "${SCRIPT_DIR}/common.sh" ]]; then source "${SCRIPT_DIR}/common.sh"
elif [[ -f "${SCRIPT_DIR}/../common.sh" ]]; then source "${SCRIPT_DIR}/../common.sh"
else echo "FATAL: common.sh not found" >&2; exit 1; fi
export BIRD_ID="${TOOL_BIRD}"

_do_check_impl() {
    echo "=== Update Check: ${NODE_ID} ==="
    echo "  OS: $(cat /etc/redhat-release 2>/dev/null || echo 'unknown')"
    echo "  Kernel: $(uname -r 2>/dev/null)"
    echo ""
    echo "--- Available Updates ---"
    local count; count=$(dnf check-update --quiet 2>/dev/null | grep -c '^\S' || echo 0)
    echo "  Pending: ${count} package(s)"
    if [ "$count" -gt 0 ]; then
        dnf check-update --quiet 2>/dev/null | head -20 | sed 's/^/  /'
    fi
    echo ""
    echo "--- Security Updates ---"
    local sec_count; sec_count=$(dnf updateinfo list --security --quiet 2>/dev/null | grep -c '^\S' || echo 0)
    echo "  Security: ${sec_count} advisory(ies)"
    tool_audit "check" "pending=${count} security=${sec_count} node=${NODE_ID}"
}

_do_security_impl() {
    require_root || return 1
    echo "=== Security Patch: ${NODE_ID} ==="
    dnf update --security -y 2>&1 | tail -20 | sed 's/^/  /'
    local rc=$?
    tool_audit "security_update" "exit_code=${rc} node=${NODE_ID}"
    echo ""
    [ "$rc" -eq 0 ] && echo "  Security patches applied" || echo "  [WARN] Some patches may have failed"
    _do_reboot_required_impl
    return $rc
}

_do_full_impl() {
    require_root || return 1
    echo "=== Full Update: ${NODE_ID} ==="
    dnf update -y 2>&1 | tail -30 | sed 's/^/  /'
    local rc=$?
    tool_audit "full_update" "exit_code=${rc} node=${NODE_ID}"
    echo ""
    [ "$rc" -eq 0 ] && echo "  Full update complete" || echo "  [WARN] Some updates may have failed"
    _do_reboot_required_impl
    return $rc
}

_do_kernel_impl() {
    echo "=== Kernel: ${NODE_ID} ==="
    echo "  Running: $(uname -r)"
    echo "  Available:"
    dnf list --available kernel 2>/dev/null | grep kernel | sed 's/^/    /' || echo "    (none)"
    echo "  Installed:"
    rpm -qa kernel 2>/dev/null | sort -V | sed 's/^/    /'
}

_do_reboot_required_impl() {
    echo "--- Reboot Required ---"
    if needs-restarting -r 2>/dev/null; then
        echo "  Reboot: NOT required"
    else
        echo "  Reboot: REQUIRED (kernel or core library updated)"
    fi
    local svc_restart; svc_restart=$(needs-restarting -s 2>/dev/null | wc -l)
    [ "${svc_restart:-0}" -gt 0 ] && echo "  Services needing restart: ${svc_restart}"
}

_do_history_impl() {
    echo "=== DNF History: ${NODE_ID} ==="
    dnf history list --setopt=history_list_view=commands 2>/dev/null | head -15 | sed 's/^/  /'
}

_do_rollback_impl() {
    local txn_id="$1"
    [ -z "$txn_id" ] && { echo "Usage: --rollback <transaction-id>"; return 1; }
    require_root || return 1
    echo "=== Rollback: ${NODE_ID} ==="
    dnf history undo "$txn_id" -y 2>&1 | tail -15 | sed 's/^/  /'
    tool_audit "rollback" "txn_id=${txn_id} node=${NODE_ID}"
}

_do_verify_impl() {
    echo "=== Package Integrity: ${NODE_ID} ==="
    local modified; modified=$(rpm -Va 2>/dev/null | grep -c '^\.' || echo 0)
    echo "  Modified packages: ${modified}"
    [ "$modified" -gt 0 ] && rpm -Va 2>/dev/null | head -20 | sed 's/^/  /'
    tool_audit "verify" "modified=${modified} node=${NODE_ID}"
}

_do_send_impl() { _do_check_impl; }

_do_status_impl() {
    local pending=0 sec=0 s="healthy"
    pending=$(dnf check-update --quiet 2>/dev/null | grep -c '^\S' || echo 0)
    sec=$(dnf updateinfo list --security --quiet 2>/dev/null | grep -c '^\S' || echo 0)
    [ "$sec" -gt 0 ] && s="degraded"
    [ "$sec" -gt 10 ] && s="down"
    echo "{\"tool\":\"${TOOL_NAME}\",\"node\":\"${NODE_ID}\",\"status\":\"${s}\",\"pending\":${pending},\"security\":${sec},\"kernel\":\"$(uname -r)\",\"checked_at\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}"
}

_do_digest_impl() {
    echo "=== Update Report: ${NODE_ID} ==="
    _do_check_impl; echo ""
    _do_kernel_impl; echo ""
    _do_reboot_required_impl; echo ""
    _do_history_impl
}

_do_flush_impl() { [ -f "$TOOL_LOG" ] && truncate -s 0 "$TOOL_LOG"; echo "Flush complete"; }

tool_help() {
    echo "${TOOL_NAME} v${TOOL_VERSION} -- Patch Orchestration (AlmaLinux 9)"
    echo ""
    echo "Read:"
    echo "  --check             Available updates"
    echo "  --kernel            Kernel versions"
    echo "  --reboot-required   Reboot check"
    echo "  --history           DNF history"
    echo "  --verify            RPM integrity check"
    echo "  --status            JSON pill"
    echo ""
    echo "Write (veil-gated):"
    echo "  --security          Security patches only"
    echo "  --full              Full dnf update"
    echo "  --rollback <id>     Undo transaction (break-glass)"
    echo ""
    echo "Standard: --send|--digest|--flush"
}

do_send()   { tool_wrap "send"   _do_send_impl "$@"; }
do_status() { tool_wrap "status" _do_status_impl "$@"; }
do_digest() { tool_wrap "digest" _do_digest_impl "$@"; }
do_flush()  { tool_wrap "flush"  _do_flush_impl "$@"; }

tool_init; trap 'tool_complete' EXIT
case "${1:---help}" in
    --send) shift; do_send "$@" ;; --status) shift; do_status "$@" ;;
    --digest) shift; do_digest "$@" ;; --flush) shift; do_flush "$@" ;;
    --check) tool_wrap "check" _do_check_impl ;;
    --security) tool_wrap "security" _do_security_impl ;;
    --full) tool_wrap "full" _do_full_impl ;;
    --kernel) tool_wrap "kernel" _do_kernel_impl ;;
    --reboot-required) tool_wrap "reboot_required" _do_reboot_required_impl ;;
    --history) tool_wrap "history" _do_history_impl ;;
    --rollback) shift; tool_wrap "rollback" _do_rollback_impl "$@" ;;
    --verify) tool_wrap "verify" _do_verify_impl ;;
    --help) tool_help ;;
    *) handle_standard_command "$@" ;;
esac
