#!/bin/bash
# =============================================================================
# runtime-health.sh -- System Health Monitor
# =============================================================================
# Category: runtime | Bird: cormorant | Tier: 1 | Node-aware: yes
# Absorbs: health.sh, runtime-agent.sh, runtime-disk.sh, watchdog.sh
#
# Actions:
#   --status     JSON pill (CPU/RAM/disk/load)
#   --heartbeat  Lightweight heartbeat for node_heartbeats table
#   --agent      s3rv1c3agent daemon health
#   --pill       Write pill JSON to /opt/binary/data/pills/
#   --disk       Disk usage detail (inodes, largest, growth)
#   --send       Full health check
#   --digest     Full report with top processes
#   --flush      Truncate logs
# =============================================================================

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

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}"

CPU_WARN=${CPU_WARN:-80}; CPU_CRIT=${CPU_CRIT:-95}
MEM_WARN=${MEM_WARN:-80}; MEM_CRIT=${MEM_CRIT:-95}
DISK_WARN=${DISK_WARN:-80}; DISK_CRIT=${DISK_CRIT:-90}
LOAD_WARN=${LOAD_WARN:-4}

[ -f "$TOOL_CONFIG" ] && source "$TOOL_CONFIG" 2>/dev/null

_get_cpu() {
    local idle; idle=$(top -bn1 2>/dev/null | grep "Cpu(s)" | awk '{print $8}' | cut -d. -f1)
    [ -z "$idle" ] && idle=$(vmstat 1 2 2>/dev/null | tail -1 | awk '{print $15}')
    echo $((100 - ${idle:-0}))
}
_get_mem() {
    local t u; t=$(free -m 2>/dev/null | awk 'NR==2{print $2}'); u=$(free -m 2>/dev/null | awk 'NR==2{print $3}')
    [ -z "$t" ] || [ "${t:-0}" -eq 0 ] 2>/dev/null && { echo "0 0 0"; return; }
    echo "$((u * 100 / t)) $u $t"
}
_get_disk() { df / 2>/dev/null | tail -1 | awk '{print $5}' | tr -d '%'; }
_get_load() { awk '{print $1}' /proc/loadavg 2>/dev/null || echo "0"; }

# --send
_do_send_impl() {
    echo "=== Health Check: $(ts_local) ==="
    echo "  Node: ${NODE_ID}"
    echo ""
    local issues=0
    local cpu; cpu=$(_get_cpu)
    if [ "$cpu" -ge "$CPU_CRIT" ]; then echo "  CPU:     ${cpu}% [CRITICAL]"; issues=$((issues+1))
    elif [ "$cpu" -ge "$CPU_WARN" ]; then echo "  CPU:     ${cpu}% [WARNING]"; issues=$((issues+1))
    else echo "  CPU:     ${cpu}% [OK]"; fi

    local mi; mi=$(_get_mem); local mp=${mi%% *}; local mr=${mi#* }; local mu=${mr%% *}; local mt=${mr#* }
    if [ "${mp:-0}" -ge "$MEM_CRIT" ] 2>/dev/null; then echo "  Memory:  ${mp}% (${mu}/${mt}MB) [CRITICAL]"; issues=$((issues+1))
    elif [ "${mp:-0}" -ge "$MEM_WARN" ] 2>/dev/null; then echo "  Memory:  ${mp}% (${mu}/${mt}MB) [WARNING]"; issues=$((issues+1))
    else echo "  Memory:  ${mp}% (${mu}/${mt}MB) [OK]"; fi

    local dp; dp=$(_get_disk)
    if [ "${dp:-0}" -ge "$DISK_CRIT" ] 2>/dev/null; then echo "  Disk:    ${dp}% [CRITICAL]"; issues=$((issues+1))
    elif [ "${dp:-0}" -ge "$DISK_WARN" ] 2>/dev/null; then echo "  Disk:    ${dp}% [WARNING]"; issues=$((issues+1))
    else echo "  Disk:    ${dp}% [OK]"; fi

    local l1; l1=$(_get_load); local li=${l1%.*}; li=${li:-0}
    if [ "${li:-0}" -ge "$LOAD_WARN" ] 2>/dev/null; then echo "  Load:    ${l1} [HIGH]"; issues=$((issues+1))
    else echo "  Load:    ${l1} [OK]"; fi

    echo "  Uptime:  $(uptime -p 2>/dev/null || echo unknown)"
    echo ""
    if [ "$issues" -gt 0 ]; then echo "  RESULT: ${issues} issue(s)"
    else echo "  RESULT: ALL OK"; fi
    tool_audit "send" "issues=${issues} node=${NODE_ID}"
    return $issues
}

# --status
_do_status_impl() {
    local cpu dp l1 mi mp; cpu=$(_get_cpu); dp=$(_get_disk); l1=$(_get_load)
    mi=$(_get_mem); mp=${mi%% *}
    local s="healthy"
    [ "$cpu" -ge "$CPU_CRIT" ] || [ "${mp:-0}" -ge "$MEM_CRIT" ] || [ "${dp:-0}" -ge "$DISK_CRIT" ] 2>/dev/null && s="down"
    [ "$s" = "healthy" ] && { [ "$cpu" -ge "$CPU_WARN" ] || [ "${mp:-0}" -ge "$MEM_WARN" ] || [ "${dp:-0}" -ge "$DISK_WARN" ]; } 2>/dev/null && s="degraded"
    cat <<EOF
{"tool":"${TOOL_NAME}","node":"${NODE_ID}","status":"${s}","cpu_pct":${cpu},"mem_pct":${mp:-0},"disk_pct":${dp:-0},"load":"${l1}","uptime":"$(uptime -p 2>/dev/null | sed 's/up //')","checked_at":"$(date -u +%Y-%m-%dT%H:%M:%SZ)"}
EOF
}

# --heartbeat (lightweight, for node_heartbeats)
_do_heartbeat_impl() {
    cat <<EOF
{"node":"${NODE_ID}","status":"alive","uptime_seconds":$(awk '{print int($1)}' /proc/uptime 2>/dev/null || echo 0),"ts":"$(date -u +%Y-%m-%dT%H:%M:%SZ)"}
EOF
}

# --agent (s3rv1c3agent daemon health)
_do_agent_impl() {
    echo "=== s3rv1c3agent Status: ${NODE_ID} ==="
    if systemctl is-active s3rv1c3agent &>/dev/null; then
        echo "  Status: active"
        echo "  PID: $(systemctl show s3rv1c3agent --property=MainPID --value 2>/dev/null || echo unknown)"
    elif pgrep -f "s3rv1c3agent" &>/dev/null; then
        echo "  Status: running (no systemd)"
        echo "  PID: $(pgrep -f s3rv1c3agent | head -1)"
    else
        echo "  Status: NOT RUNNING"
    fi
    if ss -tlnp 2>/dev/null | grep -q ':8366 '; then
        echo "  Port 8366: listening"
    else
        echo "  Port 8366: NOT listening"
    fi
}

# --pill (write pill to file)
_do_pill_impl() {
    local pill_dir="/opt/binary/data/pills"
    mkdir -p "$pill_dir" 2>/dev/null
    _do_status_impl > "${pill_dir}/${TOOL_NAME}.json"
    echo "Pill written to ${pill_dir}/${TOOL_NAME}.json"
}

# --disk (detailed disk)
_do_disk_impl() {
    echo "=== Disk Status: ${NODE_ID} ==="
    echo "--- Filesystem ---"
    df -h 2>/dev/null | grep -v tmpfs
    echo ""
    echo "--- Inodes ---"
    df -i / 2>/dev/null
    echo ""
    echo "--- Largest (top 10) ---"
    du -ah / 2>/dev/null | sort -rh | head -10
}

# --digest
_do_digest_impl() {
    echo "=== Health Report: ${NODE_ID} ==="
    echo "  Tool: ${TOOL_NAME} v${TOOL_VERSION}"
    echo "  Time: $(ts_local)"
    echo ""
    _do_send_impl
    echo ""
    echo "--- Top CPU ---"
    ps aux --sort=-%cpu 2>/dev/null | head -6
    echo ""
    echo "--- Top Memory ---"
    ps aux --sort=-%mem 2>/dev/null | head -6
    echo ""
    echo "--- s3rv1c3agent ---"
    _do_agent_impl
}

_do_flush_impl() {
    [ -f "$TOOL_LOG" ] && truncate -s 0 "$TOOL_LOG" && echo "  [CHANGED] Truncated log"
    echo "Flush complete on ${NODE_ID}"
}

# --mining (XMRig / mining status)
_do_mining_impl() {
    local action="${1:-status}"
    echo "=== Mining: ${NODE_ID} ==="
    case "$action" in
        status)
            if pgrep -x xmrig &>/dev/null; then
                echo "  XMRig: running (PID $(pgrep -x xmrig))"
                # Try xmrig API if available
                local api; api=$(curl -sf http://127.0.0.1:8080/1/summary 2>/dev/null)
                if [ -n "$api" ]; then
                    echo "$api" | python3 -c "import json,sys;d=json.load(sys.stdin);print(f'  Hashrate: {d.get(\"hashrate\",{}).get(\"total\",[0])[0]:.1f} H/s');print(f'  Pool: {d.get(\"connection\",{}).get(\"pool\",\"unknown\")}');print(f'  Uptime: {d.get(\"uptime\",0)}s')" 2>/dev/null || echo "  API: not available"
                fi
            elif systemctl is-active xmr-mine &>/dev/null; then
                echo "  XMRig: running via xmr-mine.service"
            else
                echo "  XMRig: not running"
            fi
            ;;
        hashrate)
            curl -sf http://127.0.0.1:8080/1/summary 2>/dev/null | python3 -c "import json,sys;d=json.load(sys.stdin);print(json.dumps(d.get('hashrate',{}),indent=2))" 2>/dev/null || echo "API unavailable"
            ;;
        throttle)
            local pct="${2:-50}"
            echo "  Throttle to ${pct}% — requires xmrig config reload"
            tool_audit "mining_throttle" "target=${pct} node=${NODE_ID}"
            ;;
        pool)
            curl -sf http://127.0.0.1:8080/1/summary 2>/dev/null | python3 -c "import json,sys;d=json.load(sys.stdin);c=d.get('connection',{});print(f'Pool: {c.get(\"pool\",\"?\")}\nUptime: {c.get(\"uptime\",0)}s\nAccepted: {c.get(\"accepted\",0)}\nRejected: {c.get(\"rejected\",0)}')" 2>/dev/null || echo "API unavailable"
            ;;
        *)
            echo "  Actions: status|hashrate|throttle <pct>|pool"
            ;;
    esac
}

# --uptime (detailed uptime)
_do_uptime_impl() {
    echo "=== Uptime: ${NODE_ID} ==="
    echo "  System: $(uptime -p 2>/dev/null || echo unknown)"
    echo "  Since:  $(uptime -s 2>/dev/null || echo unknown)"
    echo "  Load:   $(cat /proc/loadavg 2>/dev/null || echo unknown)"
    echo "  Users:  $(who 2>/dev/null | wc -l) active"
}

# --verify (cross-check health assertions)
_do_verify_impl() {
    echo "=== Health Verification: ${NODE_ID} ==="
    local pass=0 fail=0
    # Verify critical paths
    for p in /opt/binary /etc/binary /opt/binary/logs /opt/binary/data; do
        if [ -d "$p" ]; then echo "  [OK] ${p} exists"; pass=$((pass+1))
        else echo "  [FAIL] ${p} missing"; fail=$((fail+1)); fi
    done
    # Verify time sync
    if command -v chronyc &>/dev/null; then
        local sync; sync=$(chronyc tracking 2>/dev/null | grep "Leap status" | grep -c "Normal")
        if [ "$sync" -gt 0 ]; then echo "  [OK] Time synchronized"; pass=$((pass+1))
        else echo "  [WARN] Time may be drifting"; fi
    fi
    # Verify disk
    local dp; dp=$(_get_disk)
    if [ "${dp:-0}" -lt "$DISK_CRIT" ] 2>/dev/null; then echo "  [OK] Disk: ${dp}%"; pass=$((pass+1))
    else echo "  [FAIL] Disk critical: ${dp}%"; fail=$((fail+1)); fi
    # Verify RAM
    local mi; mi=$(_get_mem); local mp=${mi%% *}
    if [ "${mp:-0}" -lt "$MEM_CRIT" ] 2>/dev/null; then echo "  [OK] Memory: ${mp}%"; pass=$((pass+1))
    else echo "  [FAIL] Memory critical: ${mp}%"; fail=$((fail+1)); fi
    echo ""
    echo "  Passed: ${pass}  Failed: ${fail}"
    tool_audit "verify" "pass=${pass} fail=${fail} node=${NODE_ID}"
    return $fail
}

tool_help() {
    echo "${TOOL_NAME} v${TOOL_VERSION} -- System Health Monitor"
    echo ""
    echo "Status:"
    echo "  --status       JSON pill for dashboard"
    echo "  --heartbeat    Lightweight heartbeat JSON"
    echo "  --agent        s3rv1c3agent daemon status"
    echo "  --pill         Write pill JSON to data/pills/"
    echo "  --uptime       Detailed uptime info"
    echo ""
    echo "Diagnostics:"
    echo "  --send         Full health check (CPU, RAM, disk, load)"
    echo "  --disk         Detailed disk usage"
    echo "  --digest       Full report with top processes"
    echo "  --verify       Cross-check health assertions"
    echo ""
    echo "Mining:"
    echo "  --mining <act> Mining status (status|hashrate|throttle|pool)"
    echo ""
    echo "Maintenance:"
    echo "  --flush        Truncate log"
    echo ""
    echo "DB: bird=${TOOL_BIRD} | tier=${TOOL_TIER} | node=${NODE_ID}"
}

do_send()      { tool_wrap "send"      _do_send_impl "$@"; }
do_status()    { tool_wrap "status"    _do_status_impl "$@"; }
do_heartbeat() { tool_wrap "heartbeat" _do_heartbeat_impl "$@"; }
do_agent()     { tool_wrap "agent"     _do_agent_impl "$@"; }
do_pill()      { tool_wrap "pill"      _do_pill_impl "$@"; }
do_disk()      { tool_wrap "disk"      _do_disk_impl "$@"; }
do_digest()    { tool_wrap "digest"    _do_digest_impl "$@"; }
do_flush()     { tool_wrap "flush"     _do_flush_impl "$@"; }
do_mining()    { tool_wrap "mining"    _do_mining_impl "$@"; }
do_uptime()    { tool_wrap "uptime"    _do_uptime_impl "$@"; }
do_verify()    { tool_wrap "verify"    _do_verify_impl "$@"; }

tool_init
trap 'tool_complete' EXIT
case "${1:---help}" in
    --send)      shift; do_send "$@" ;;
    --status)    shift; do_status "$@" ;;
    --heartbeat) shift; do_heartbeat "$@" ;;
    --agent)     shift; do_agent "$@" ;;
    --pill)      shift; do_pill "$@" ;;
    --disk)      shift; do_disk "$@" ;;
    --digest)    shift; do_digest "$@" ;;
    --flush)     shift; do_flush "$@" ;;
    --mining)    shift; do_mining "$@" ;;
    --uptime)    do_uptime ;;
    --verify)    do_verify ;;
    --help)      tool_help ;;
    *)           handle_standard_command "$@" ;;
esac

