#!/bin/bash
# Connectivity + host metrics reporter for macOS.
# Posts to the same API as report.sh (Linux). Install via cron:
#   */5 * * * * bash /path/to/report-mac.sh >/dev/null 2>&1
# Downloads are served from the CDN mirror (lo01.g77k.com); metrics still
# POST to f.g77k.com. On first run from the old URL the script rewrites the
# caller's crontab to fetch from the mirror, and every cron run sleeps a
# per-host offset + random jitter so POSTs don't spike on the minute.
#
# macOS cron has a minimal PATH — ensure sysctl, diskutil, route,
# netstat, sw_vers, vm_stat etc. are reachable.
export PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin:$PATH"
set -u
A=https://f.g77k.com/j/api/connectivity.php
I=https://w.g77k.com/ip.php

# Lock via mkdir (flock not available on macOS)
LOCK=/tmp/report-mac.sh.lock
mkdir "$LOCK" 2>/dev/null || exit 0
trap 'rmdir "$LOCK" 2>/dev/null' EXIT

H=$(hostname 2>/dev/null)

# One-time crontab migration: fetch this script from the mirror instead of
# f.g77k.com (curl|bash every few minutes was eating origin bandwidth).
# Only the download URL in cron lines changes; $A above still posts here.
# The standard `curl -fsSL <url> | bash` line becomes a mirror-first line
# with f.g77k.com as fallback, so a dead mirror can't strand the fleet.
# Gated on the mirror actually serving the script (HEAD 200): migrating
# before the mirror is populated would break variant cron lines.
# Migrated lines contain lo01, so the /lo01/! guards make this idempotent
# and keep the fallback URL from being rewritten on later runs.
_mir=https://lo01.g77k.com/j/report-mac.sh
_old=https://f.g77k.com/j/report-mac.sh
if command -v crontab >/dev/null 2>&1; then
  _ct=$(crontab -l 2>/dev/null) || _ct=
  if printf '%s\n' "$_ct" | grep 'f\.g77k\.com/j/report-mac\.sh' | grep -qv 'lo01\.g77k\.com' \
     && curl -fsI --connect-timeout 5 --max-time 10 "$_mir" >/dev/null 2>&1; then
    printf '%s\n' "$_ct" \
      | sed -e "/lo01\.g77k\.com/!s#curl -fsSL $_old | bash#(curl -fsSL $_mir || curl -fsSL $_old) | bash#" \
            -e "/lo01\.g77k\.com/!s#https://f\.g77k\.com/j/report-mac\.sh#$_mir#g" \
            -e "/lo01\.g77k\.com/!s#http://f\.g77k\.com/j/report-mac\.sh#$_mir#g" \
      | crontab - 2>/dev/null || true
  fi
fi

# Cron fires every host at the same wall-clock minute; spread the POSTs with
# a stable per-host offset (0-104s, hostname hash) + 0-14s random jitter.
# Skipped when interactive (stdout is a tty) or invoked with --now.
if [ "${1:-}" != "--now" ] && [ ! -t 1 ]; then
  _o=$(printf '%s' "$H" | cksum 2>/dev/null | awk '{print $1 % 105}')
  _j=$(od -An -N1 -tu1 /dev/urandom 2>/dev/null | tr -cd '0-9')
  sleep $(( ${_o:-0} + ${_j:-0} % 15 )) 2>/dev/null || true
fi

host google.com >/dev/null 2>&1 || true

B=
for _ in 1 2 3 4 5; do
  P=$(curl -s -o /dev/null -w "%{time_total}" --connect-timeout 3 --max-time 5 http://google.com 2>/dev/null) || continue
  [ -n "$P" ] || continue
  M=$(awk "BEGIN{printf \"%.0f\",$P*1000}")
  [ -n "$M" ] && [ "$M" != 0 ] || continue
  { [ -z "$B" ] || [ "$M" -lt "$B" ]; } && B=$M
done
G=${B:--1}

X=$(curl -s --connect-timeout 3 --max-time 10 "$I" 2>/dev/null); X=${X:-unknown}

# CPU
CC=$(sysctl -n hw.ncpu 2>/dev/null)
CM=$(sysctl -n machdep.cpu.brand_string 2>/dev/null)
CH=$(sysctl -n hw.cpufrequency 2>/dev/null)
[ -n "$CH" ] && CH=$((CH / 1000000))

# Load avg
L1=; L5=; L15=
_load=$(sysctl -n vm.loadavg 2>/dev/null | tr -d '{}' | xargs)
if [ -n "$_load" ]; then
  L1=$(echo "$_load" | awk '{print $1}')
  L5=$(echo "$_load" | awk '{print $2}')
  L15=$(echo "$_load" | awk '{print $3}')
fi

# Temperature (requires osx-cpu-temp; null if absent)
T=
command -v osx-cpu-temp >/dev/null 2>&1 && T=$(osx-cpu-temp 2>/dev/null | awk '{printf "%.1f",$1}')

# Memory: hw.memsize (bytes) + vm_stat pages
MT=; MA=
_memb=$(sysctl -n hw.memsize 2>/dev/null)
if [ -n "$_memb" ]; then
  MT=$((_memb / 1024))
  _vmstat=$(vm_stat 2>/dev/null)
  _pgsz=$(echo "$_vmstat" | awk '/page size of/{for(i=1;i<=NF;i++){if($i+0>0){print $i;exit}}}')
  [ -z "$_pgsz" ] && _pgsz=16384
  _free=$(echo "$_vmstat" | awk '/Pages free:/{gsub(/\./,"",$NF);print $NF+0}')
  _inactive=$(echo "$_vmstat" | awk '/Pages inactive:/{gsub(/\./,"",$NF);print $NF+0}')
  _speculative=$(echo "$_vmstat" | awk '/Pages speculative:/{gsub(/\./,"",$NF);print $NF+0}')
  _avail=$(( (${_free:-0} + ${_inactive:-0} + ${_speculative:-0}) * ${_pgsz:-16384} / 1024 ))
  MA=$_avail
fi

# Swap
ST=; SU=
_swap=$(sysctl vm.swapusage 2>/dev/null)
if [ -n "$_swap" ]; then
  ST=$(echo "$_swap" | awk -F'[= ]+' '{for(i=1;i<=NF;i++){if($i=="total"){gsub(/M/,"",$(i+1));printf "%.0f",$(i+1)*1024}}}')
  _sused=$(echo "$_swap" | awk -F'[= ]+' '{for(i=1;i<=NF;i++){if($i=="used"){gsub(/M/,"",$(i+1));printf "%.0f",$(i+1)*1024}}}')
  SU=${_sused:-0}
fi

# Disk: use diskutil if available (accurate for APFS), fall back to df
DT=; DU=
if command -v diskutil >/dev/null 2>&1; then
  _dinfo=$(diskutil info / 2>/dev/null)
  _dtotal=$(echo "$_dinfo" | awk -F'[()]' '/Container Total Space|Volume Total Space/{gsub(/[^0-9]/,"",$2);print $2;exit}')
  _davail=$(echo "$_dinfo" | awk -F'[()]' '/Volume Free Space|Volume Available Space|Container Free Space/{gsub(/[^0-9]/,"",$2);print $2;exit}')
  if [ -n "$_dtotal" ] && [ -n "$_davail" ]; then
    DT=$_dtotal
    DU=$((_dtotal - _davail))
  fi
fi
if [ -z "$DT" ]; then
  _df=$(df -Pk / 2>/dev/null | awk 'NR==2{print $2*1024" "$3*1024}')
  read -r DT DU <<<"${_df:-}"
fi

# Uptime
UP=
_boot=$(sysctl -n kern.boottime 2>/dev/null | awk -F'[= ,]+' '{for(i=1;i<=NF;i++){if($i=="sec")print $(i+1)}}')
[ -n "$_boot" ] && UP=$(( $(date +%s) - _boot ))

# TCP connections + process count
TC=$(netstat -an -p tcp 2>/dev/null | grep -c ESTABLISHED)
PC=$(ps -ax 2>/dev/null | tail -n +2 | wc -l | tr -d ' ')

# Default network interface + rx/tx
NI=; NR=; NX=
NI=$(route -n get default 2>/dev/null | awk '/interface:/{print $2}')
if [ -n "$NI" ]; then
  _netstats=$(netstat -ib -I "$NI" 2>/dev/null | awk 'NR==2{print $7" "$10}')
  NR=$(echo "$_netstats" | awk '{print $1}')
  NX=$(echo "$_netstats" | awk '{print $2}')
fi

KE=$(uname -r 2>/dev/null)
DI=$(sw_vers -productName 2>/dev/null)
_ver=$(sw_vers -productVersion 2>/dev/null)
[ -n "$_ver" ] && DI="$DI $_ver"

VT=none
ARCH=$(uname -m 2>/dev/null)

js(){ local s=${1:-}; s=${s//\\/\\\\}; s=${s//\"/\\\"}; printf '%s' "$s"|tr -d '\000-\037'; }
jn(){ if [ -z "${1:-}" ]; then printf null; else printf '%s' "$1"; fi; }

J="{\"hostname\":\"$(js "$H")\",\"proxy\":\"$(js "$X")\",\"google_ping\":$(jn "$G"),\"cpu_cores\":$(jn "$CC"),\"cpu_model\":\"$(js "$CM")\",\"cpu_mhz\":$(jn "$CH"),\"load1\":$(jn "$L1"),\"load5\":$(jn "$L5"),\"load15\":$(jn "$L15"),\"cpu_temp_c\":$(jn "$T"),\"mem_total_kb\":$(jn "$MT"),\"mem_available_kb\":$(jn "$MA"),\"swap_total_kb\":$(jn "$ST"),\"swap_used_kb\":$(jn "$SU"),\"disk_total_b\":$(jn "$DT"),\"disk_used_b\":$(jn "$DU"),\"uptime_sec\":$(jn "$UP"),\"tcp_conns\":$(jn "$TC"),\"proc_count\":$(jn "$PC"),\"net_iface\":\"$(js "$NI")\",\"net_rx_bytes\":$(jn "$NR"),\"net_tx_bytes\":$(jn "$NX"),\"kernel\":\"$(js "$KE")\",\"distro\":\"$(js "$DI")\",\"virt_type\":\"$(js "$VT")\",\"arch\":\"$(js "$ARCH")\",\"gpu_info\":null}"

p(){
  if command -v gzip >/dev/null 2>&1; then
    printf '%s' "$J"|gzip -c|curl -s -X POST "$A" --connect-timeout 5 --max-time 15 -H "Content-Type: application/json" -H "Content-Encoding: gzip" --data-binary @- -w "\n%{http_code}"
  else
    curl -s -X POST "$A" --connect-timeout 5 --max-time 15 -H "Content-Type: application/json" -d "$J" -w "\n%{http_code}"
  fi
}

O=$(p) || true
HC=$(printf '%s\n' "$O"|tail -n1)
[[ "$HC" =~ ^2[0-9][0-9]$ ]] || { sleep 1; O=$(p) || true; HC=$(printf '%s\n' "$O"|tail -n1); }
[[ "$HC" =~ ^2[0-9][0-9]$ ]] || { echo "report-mac.sh HTTP ${HC:-?}" >&2; exit 1; }
exit 0
