#!/bin/bash
# ==============================================================================================
# ================================= Mesh Monitor ===============================================
# ==============================================================================================
#
# PURPOSE
# ─────────────────────────────────────────────────────────────────────────────
# At-a-glance view of everyone in the mesh — who they are, how to reach them,
# and what services each node has put up for mutual fallback protection.
# Read-only — sources conf data only, no SSH or API calls, no sensitive data.
#
# ==============================================================================================
# OPERATIONAL MODEL
# ==============================================================================================
#
# Reads directly from all three conf files without detect_hosts().
# Iterates all defined HOST* vars — scales automatically as new nodes join.
#
# Sections:
#   MEMBERS       — server name, owner, contact email for each HOST*
#   COVERAGE      — what each host runs for others when they go down, by tier
#   PARTNERSHIP   — enabled/disabled, current owner, sync interval
#
# Safe to share with mesh members — contains no API keys, passwords, or SSH keys.
#
# ==============================================================================================
# DESIGN PRINCIPLES
# ==============================================================================================
#
# Read-Only, No Network Calls
#   All data comes from conf files — no SSH, no API calls, no pings. The output
#   is always instant and never fails due to a node being unreachable. This makes
#   it safe to run at any time without side effects.
#
# Scales Automatically
#   Iterates all defined HOST* vars rather than a hardcoded list. Adding a new
#   node to master.conf/host*.conf makes it appear in the output immediately.
#
# Safe to Share
#   Output contains only identity and coverage configuration — no API keys,
#   no passwords, no SSH keys. The report can be shared with other mesh members
#   without exposing secrets.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
# No External Dependencies
#   Reads only from already-sourced conf files. No curl, no ssh, no docker —
#   nothing that can fail, hang, or require credentials.
#
# Empty Mesh Guard
#   collect_hosts() populates ALL_HOST_IDS — if no HOST* vars are defined the
#   output sections iterate over an empty array and exit cleanly.
#
# No Root, No Lock, No detect_hosts — Deliberate
#   This is the one script in the ecosystem that intentionally omits all three, and
#   they should not be added. It writes nothing, so there is no state for a lock to
#   protect and no privileged operation to justify a root gate. It reports on every
#   node rather than acting as one, so detect_hosts() would narrow it to this host's
#   aliases — the opposite of what it is for. Every HOST* var is read directly instead.
#
# ==============================================================================================
# CONFIGURATION
# ==============================================================================================
#
# host*.conf — per-host identity vars read by this script:
#
#   HOST*_OWNER         human-readable owner name
#   HOST*_OWNER_EMAIL   contact email — also used by NPM for Let's Encrypt
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
#
#   mesh_monitor.sh           — show full mesh overview
#   mesh_monitor.sh --log     — verbose output
#   mesh_monitor.sh --status  — show config and exit
#
# ==============================================================================================

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

source "$SCRIPT_DIR/../load_config.sh"

parse_args "$@"

# ── Helpers ───────────────────────────────────────────────────────────────────────────────────

# Populate ALL_HOST_IDS with every defined HOST* var
collect_hosts() {
    ALL_HOST_IDS=()
    for h in HOST1 HOST2 HOST3 HOST4 HOST5 HOST6 HOST7 HOST8; do
        [[ -n "${!h:-}" ]] && ALL_HOST_IDS+=("$h")
    done
}

# Read a bash array by computed name — returns space-separated elements
get_array() { eval "echo \"\${${1}[*]}\""; }

# Convert minutes to human-readable delay string
format_delay() {
    local m="${1:-0}"
    (( m == 0  )) && { echo "immediate"; return; }
    (( m >= 1440 )) && { echo "$((m / 1440))d";  return; }
    (( m >= 60   )) && { echo "$((m / 60))hr";   return; }
    echo "${m}min"
}

collect_hosts
log "$ICON_HOST Hosts: ${ALL_HOST_IDS[*]} (${#ALL_HOST_IDS[@]} in mesh)"

# ==============================================================================================
# ━━━ Status ━━━
# ==============================================================================================
if [[ "$SHOW_STATUS" == true ]]; then
    echo ""
    echo "━━━━━ $ICON_SUMMARY STATUS ━━━━━"
    echo "  Hosts:  ${ALL_HOST_IDS[*]}"
    for h in "${ALL_HOST_IDS[@]}"; do
        owner_var="${h}_OWNER"; owner="${!owner_var:-unknown}"
        email_var="${h}_OWNER_EMAIL"; email="${!email_var:-(not set)}"
        echo "  $h:     ${!h}  /  $owner  /  $email"
    done
    echo "  Mode:   read-only — conf data only, no SSH or API calls"
    echo "━━━━━━━━━━━━━━━━━━━━━━━"
    exit 0
fi

# ==============================================================================================
# ━━━ Mesh Overview ━━━
# ==============================================================================================

echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "                              MESH OVERVIEW"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

# ── Members ───────────────────────────────────────────────────────────────────────────────────
echo ""
echo "━━━ $ICON_HOST Members ━━━"
echo ""

# Dynamic column widths
W_HOST=4; W_SERVER=6; W_OWNER=5
for h in "${ALL_HOST_IDS[@]}"; do
    server="${!h}"
    owner_var="${h}_OWNER"; owner="${!owner_var:-unknown}"
    (( ${#h}      > W_HOST   )) && W_HOST=${#h}
    (( ${#server} > W_SERVER )) && W_SERVER=${#server}
    (( ${#owner}  > W_OWNER  )) && W_OWNER=${#owner}
done

printf "  %-${W_HOST}s  %-${W_SERVER}s  %-${W_OWNER}s  %s\n" \
    "HOST" "SERVER" "OWNER" "EMAIL"
printf "  %-${W_HOST}s  %-${W_SERVER}s  %-${W_OWNER}s  %s\n" \
    "$(printf '─%.0s' $(seq 1 $W_HOST))" \
    "$(printf '─%.0s' $(seq 1 $W_SERVER))" \
    "$(printf '─%.0s' $(seq 1 $W_OWNER))" \
    "─────────────────────────"

for h in "${ALL_HOST_IDS[@]}"; do
    server="${!h}"
    owner_var="${h}_OWNER";       owner="${!owner_var:-unknown}"
    email_var="${h}_OWNER_EMAIL"; email="${!email_var:-(not set)}"
    printf "  %-${W_HOST}s  %-${W_SERVER}s  %-${W_OWNER}s  %s\n" \
        "$h" "$server" "$owner" "$email"
    log "$h: server=$server owner=$owner email=$email"
done

# ── Protected Services ────────────────────────────────────────────────────────────────────────
echo ""
echo "━━━ $ICON_SHIELD Protected Services ━━━"

COVERAGE_FOUND=false

# Iterate from the perspective of each protected host — what do they want covered
for covered in "${ALL_HOST_IDS[@]}"; do
    covered_owner_var="${covered}_OWNER";   covered_owner="${!covered_owner_var:-$covered}"
    covered_email_var="${covered}_OWNER_EMAIL"; covered_email="${!covered_email_var:-}"

    # Each host defines its own recovery profile — read directly from covered host's vars
    any_tiers=false
    declare -A tier_containers=()
    declare -A tier_delays=()
    covered_by=""

    for tier in 1 2 3 4; do
        containers=$(get_array "FALLBACK_${covered}_TIER${tier}")
        [[ -z "$containers" ]] && continue
        any_tiers=true
        tier_containers[$tier]="$containers"
        delay_var="${covered}_TIER${tier}_DELAY"
        tier_delays[$tier]="${!delay_var:-0}"
    done

    if [[ "$any_tiers" == true ]]; then
        for covering in "${ALL_HOST_IDS[@]}"; do
            [[ "$covering" == "$covered" ]] && continue
            covering_owner_var="${covering}_OWNER"
            covering_owner="${!covering_owner_var:-$covering}"
            covered_by="${covered_by:+$covered_by, }$covering ($covering_owner)"
        done
    fi

    [[ "$any_tiers" == false ]] && continue

    COVERAGE_FOUND=true
    echo ""

    header="$covered  $covered_owner"
    [[ -n "$covered_email" ]] && header+="  —  $covered_email"
    echo "  $header"

    for tier in 1 2 3 4; do
        [[ -z "${tier_containers[$tier]:-}" ]] && continue
        if [[ "$tier" -eq 1 ]]; then
            printf "    Tier 1  %-12s  %s\n" "(immediate)" "${tier_containers[$tier]// /, }"
        else
            delay=$(format_delay "${tier_delays[$tier]:-0}")
            printf "    Tier %s  %-12s  %s\n" "$tier" "($delay)" "${tier_containers[$tier]// /, }"
        fi
    done

    [[ -n "$covered_by" ]] && echo "    Covered by:  $covered_by"

    unset tier_containers tier_delays
    declare -A tier_containers=()
    declare -A tier_delays=()
done

if [[ "$COVERAGE_FOUND" == false ]]; then
    echo ""
    echo "  No fallback coverage configured"
fi

# ── Partnership ───────────────────────────────────────────────────────────────────────────────
echo ""
echo "━━━ $ICON_NET Partnership ━━━"
echo ""

if [[ "${PARTNERSHIP_ENABLED:-false}" == true ]]; then
    owner_host="${PARTNERSHIP_OWNER_HOST:-HOST1}"
    owner_server="${!owner_host:-unknown}"
    owner_name_var="${owner_host}_OWNER"; owner_name="${!owner_name_var:-unknown}"
    echo "  Status    enabled"
    echo "  Owner     $owner_host  ($owner_server  /  $owner_name)"
    echo "  Sync      every ${PARTNERSHIP_SYNC_INTERVAL:-15}min"
else
    echo "  Status    disabled"
fi

echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
exit 0
