#!/bin/sh
# wan-mwan.sh — mwan3 config template + runtime weight control for busrouter.
#
# Interfaces (from recon §1):
#   cellular -> mwan3 iface 'modem_0001' (rmnet_mhi0, T-Mobile 5G / active SIM)
#   fiber    -> mwan3 iface 'wan'        (eth0, Starlink / WAN2 stand-in)
#
# Public API:
#   mwan_apply_config              write + activate busrouter mwan3 config (replaces stock)
#   mwan_set_weight <mbr> <w>     update member weight if change >=10 (then mwan3 restart)
#   gw_probe_iface <iface>        DNS+ping+HTTP quorum probe; exit 0=alive exit 1=dead

LIB_DIR="${LIB_DIR:-/usr/lib/busrouter}"
. "${LIB_DIR}/lib-decide.sh"

# Write the busrouter mwan3 config and restart mwan3.
# WAN priority: wwan/repeater (metric 1) > modem_0001/cellular (metric 2) > wan/Starlink (metric 3)
# last_resort='default' falls back to the main routing table rather than returning unreachable.
# Idempotent — safe to call on each boot or re-provision.
mwan_apply_config() {
    cat > /etc/config/mwan3.tmp << 'MWAN3CONF'
config globals 'globals'
	option enabled '1'
	option mmx_mask '0x3F00'

config interface 'wwan'
	option enabled '1'
	list track_ip '1.1.1.1'
	list track_ip '8.8.8.8'
	option family 'ipv4'
	option reliability '1'
	option count '1'
	option timeout '2'
	option interval '5'
	option down '3'
	option up '3'

config interface 'modem_0001'
	option enabled '1'
	list track_ip '1.1.1.1'
	list track_ip '8.8.8.8'
	option family 'ipv4'
	option reliability '1'
	option count '1'
	option timeout '2'
	option interval '5'
	option down '3'
	option up '3'

config interface 'wan'
	option enabled '1'
	list track_ip '1.1.1.1'
	list track_ip '8.8.8.8'
	option family 'ipv4'
	option reliability '1'
	option count '1'
	option timeout '2'
	option interval '5'
	option down '3'
	option up '3'

config member 'wwan_mbr'
	option interface 'wwan'
	option metric '1'
	option weight '100'

config member 'cellular'
	option interface 'modem_0001'
	option metric '2'
	option weight '100'

config member 'fiber'
	option interface 'wan'
	option metric '3'
	option weight '100'

config policy 'balanced'
	list use_member 'wwan_mbr'
	list use_member 'cellular'
	list use_member 'fiber'
	option last_resort 'default'

config rule 'default_rule'
	option dest_ip '0.0.0.0/0'
	option use_policy 'balanced'
	option family 'ipv4'
MWAN3CONF

    mv /etc/config/mwan3.tmp /etc/config/mwan3 \
        || { logger -t busrouter "mwan3: config write FAILED"; return 1; }
    mwan3 restart \
        || logger -t busrouter "mwan3: restart FAILED (exit $?)"
    logger -t busrouter "mwan3: config applied (wwan=repeater metric1, cellular=modem_0001 metric2, fiber=wan metric3)"
}

# Update a mwan3 member weight, but only if the change exceeds the hysteresis band (>=10).
# Usage: mwan_set_weight <member> <target_weight>
# <member>: 'cellular' or 'fiber'
# <target_weight>: integer 20..80 (output of wan_weight in lib-decide.sh)
# Calls mwan3 restart only when a weight step is committed — avoids conntrack flush on
# every scoring cycle when both WANs are stable.
mwan_set_weight() {
    local member="$1"
    local target="$2"
    local current

    case "$target" in
        ''|*[!0-9]*) logger -t busrouter "mwan_set_weight: invalid weight '${target}'"; return 1 ;;
    esac

    current=$(uci -q get mwan3."${member}".weight)
    if [ -z "$current" ]; then
        logger -t busrouter "mwan_set_weight: unknown member '${member}'"
        return 1
    fi

    if weight_changed "$current" "$target"; then
        uci set mwan3."${member}".weight="$target" && \
        uci commit mwan3 && \
        mwan3 restart \
            || { logger -t busrouter "mwan3: weight update FAILED for ${member}"; return 1; }
        logger -t busrouter "mwan3: ${member} weight ${current}->${target}"
    fi
}

# Run DNS / ping / HTTP probes on an interface and call gw_verdict (2-of-3 quorum).
# Usage: gw_probe_iface <iface>
# <iface>: real interface name (rmnet_mhi0 or eth0)
# Returns: exit 0 (alive) if >=2 probes pass, exit 1 (dead) otherwise.
# DNS is not interface-bound (BusyBox nslookup lacks source-bind) -- tests system resolver only;
# ping and HTTP are the authoritative per-interface probes.
gw_probe_iface() {
	local iface="$1"
	[ -n "$iface" ] || { logger -t busrouter "gw_probe_iface: missing iface arg"; return 1; }
	local dns_ok=0 ping_ok=0 http_ok=0

	nslookup connectivitycheck.gstatic.com >/dev/null 2>&1 && dns_ok=1
	ping -I "$iface" -c 2 -W 2 8.8.8.8 >/dev/null 2>&1 && ping_ok=1
	curl -sf --interface "$iface" --connect-timeout 5 --max-time 8 -o /dev/null 'http://connectivitycheck.gstatic.com/generate_204' 2>/dev/null && http_ok=1

	logger -t busrouter "gw_probe[${iface}]: dns=${dns_ok} ping=${ping_ok} http=${http_ok}"
	gw_verdict "$dns_ok" "$ping_ok" "$http_ok"
}
