#!/bin/sh
# smartwan-adapter.sh — Read SmartWAN state and write dw_weight_ratio.
# NEVER calls loadbalance lb-enable. NEVER calls loadbalance at all.
# Rides entirely on top of SmartWAN — does not own routing.
#
# Public API:
#   sw_read_state              read SmartWAN mode, interfaces, current weights
#   sw_read_wan_state <iface>  read per-WAN state: operstate, gateway, weight
#   sw_write_weights <w1> <w2> write dw_weight_ratio (50=balanced, 20/80=biased)
#   sw_signal_reload           touch SmartWAN to re-read config (light touch, no conntrack flush)
#
# Config file: /usr/syno/etc/smartwan/smartwan.conf
# Weight field: dw_weight_ratio = <0-100> (WAN1 share %, WAN2 = 100-WAN1)
#   - dw_weight_ratio appears twice in the conf (once for each ifname section)
#   - We update BOTH occurrences
#   - 50/50 = balanced, 70 = 70% WAN1 / 30% WAN2

SMARTWAN_CONF="/usr/syno/etc/smartwan/smartwan.conf"
STATE_DIR="${STATE_DIR:-/tmp/syno-balance}"
LOG_TAG="${LOG_TAG:-syno-balance}"

log()  { logger -t "$LOG_TAG" -p local0.warn "$*"; }
warn() { logger -t "$LOG_TAG" -p local0.warn "WARNING: $*"; }

# ── Read SmartWAN global state ──────────────────────────────────────
sw_read_state() {
    [ -f "$SMARTWAN_CONF" ] || { warn "smartwan.conf not found"; return 1; }
    . "$SMARTWAN_CONF" 2>/dev/null

    echo "MODE=${smartwan_mode:-unknown}"
    echo "WAN1_IFNAME=${smartwan_ifname_1:-wan}"
    echo "WAN2_IFNAME=${smartwan_ifname_2:-lan1}"
    echo "WAN1_WEIGHT=${dw_weight_ratio:-50}"
}

# ── Read a single WAN's runtime state ───────────────────────────────
# Returns: STATE=up|down GW=x.x.x.x WEIGHT=N
sw_read_wan_state() {
    local iface="$1"
    [ -n "$iface" ] || return 1

    # Physical link state
    local operstate="down"
    operstate=$(cat "/sys/class/net/${iface}/operstate" 2>/dev/null || echo "unknown")

    # Gateway from routing table
    local gw=""
    gw=$(ip route show dev "$iface" 2>/dev/null | grep default | awk '{print $3}' | head -1)
    [ -z "$gw" ] && gw=$(ip route show dev "$iface" 2>/dev/null | awk '/via/ {print $3}' | head -1)

    # Current weight from smartwan.conf
    local weight=0
    [ -f "$SMARTWAN_CONF" ] && . "$SMARTWAN_CONF" 2>/dev/null
    weight="${dw_weight_ratio:-50}"

    echo "IFACE=${iface} STATE=${operstate} GW=${gw:-none} WEIGHT=${weight}"
}

# ── Write dw_weight_ratio for both WANs ─────────────────────────────
# $1: WAN1 weight (0-100, WAN2 = 100-WAN1)
# Sets dw_weight_ratio in smartwan.conf for both ifname sections.
# Does NOT call loadbalance. SmartWAN re-reads on its own cycle.
sw_write_weights() {
    local w1="$1"
    case "$w1" in
        ''|*[!0-9]*) warn "invalid weight: $w1"; return 1 ;;
    esac
    [ "$w1" -lt 0  ] && w1=0
    [ "$w1" -gt 100 ] && w1=100

    local current=50
    [ -f "$SMARTWAN_CONF" ] && . "$SMARTWAN_CONF" 2>/dev/null
    current="${dw_weight_ratio:-50}"

    # Only write if changed by >= 10 (hysteresis via lib-decide.sh)
    local diff=$(( w1 > current ? w1 - current : current - w1 ))
    if [ "$diff" -lt 10 ]; then
        return 0  # within hysteresis band — no-op
    fi

    # Write to smartwan.conf using sed — safe, atomic-ish
    # SmartWAN has dw_weight_ratio on both ifname sections
    if [ -f "$SMARTWAN_CONF" ]; then
        sed -i "s/^dw_weight_ratio=.*/dw_weight_ratio=${w1}/" "$SMARTWAN_CONF" 2>/dev/null
    fi

    log "smartwan: dw_weight_ratio ${current} -> ${w1} (WAN1=${w1}% WAN2=$((100-w1))%)"
    mkdir -p "$STATE_DIR"
    echo "$w1" > "$STATE_DIR/sw_weight_written"
    echo "$(date +%s)" > "$STATE_DIR/sw_weight_time"
}

# ── Light touch — signal SmartWAN to re-read config ────────────────
# Uses synoservice if available. NEVER calls loadbalance lb-enable.
sw_signal_reload() {
    # SRM 1.3+ has synoservice to restart individual services
    if command -v synoservice >/dev/null 2>&1; then
        synoservice --restart smartwan 2>/dev/null && \
            { log "smartwan: light-reload via synoservice"; return 0; }
    fi

    # Fallback: just touch the config — SmartWAN polls it
    touch "$SMARTWAN_CONF" 2>/dev/null
    log "smartwan: config touched for polling re-read"
    return 0
}

# ── Check if SmartWAN is in load-balance mode ───────────────────────
sw_is_lb_mode() {
    [ -f "$SMARTWAN_CONF" ] || return 1
    local mode=""
    mode=$(grep '^smartwan_mode=' "$SMARTWAN_CONF" 2>/dev/null | cut -d= -f2)
    case "$mode" in
        loadbalance|loadbalancing_failover) return 0 ;;
        *) return 1 ;;
    esac
}
