#!/bin/bash
#
# Dune Weaver Autohotspot
#
# Two modes:
#   Boot mode (no args):  Scans for known WiFi with 30s retry timeout.
#   Check mode (--check): Quick single-pass check, runs every 60s via timer.
#
# Logic:
#   - If connected to known WiFi → do nothing
#   - If in hotspot mode and known WiFi appears → switch to client, reboot
#   - If disconnected → scan once → connect or activate hotspot
#
# DNS redirect for captive portal is handled by NetworkManager's built-in
# dnsmasq via config in /etc/NetworkManager/dnsmasq-shared.d/.
#
# Requires: NetworkManager (Pi Trixie)
#

set -euo pipefail

HOTSPOT_CON_NAME="DuneWeaver-Hotspot"
MODE_FILE="/tmp/dw-wifi-mode"
SCAN_TIMEOUT=30
SCAN_INTERVAL=5
IFACE="wlan0"

log() {
    echo "[autohotspot] $*"
    logger -t autohotspot "$*"
}

# Wait for NetworkManager to be ready
wait_for_nm() {
    local waited=0
    while [ $waited -lt 10 ]; do
        if nmcli general status &>/dev/null; then
            return 0
        fi
        sleep 1
        waited=$((waited + 1))
    done
    log "ERROR: NetworkManager not ready after 10s"
    return 1
}

# Get list of saved WiFi connection names
get_saved_wifi() {
    nmcli -t -f NAME,TYPE con show | grep ':.*wireless' | cut -d: -f1 | grep -v "^${HOTSPOT_CON_NAME}$" || true
}

# Get current wlan0 connection state: "client", "hotspot", or "disconnected"
get_current_state() {
    local active
    active=$(nmcli -t -f NAME,TYPE,DEVICE con show --active 2>/dev/null || true)

    while IFS= read -r line; do
        local name device
        name=$(echo "$line" | cut -d: -f1)
        device=$(echo "$line" | rev | cut -d: -f1 | rev)
        if [ "$device" = "$IFACE" ]; then
            if [ "$name" = "$HOTSPOT_CON_NAME" ]; then
                echo "hotspot"
                return
            fi
            echo "client"
            return
        fi
    done <<< "$active"

    echo "disconnected"
}

# Scan for available networks and check if any match saved connections
check_for_known_wifi() {
    # Trigger a fresh scan
    nmcli dev wifi rescan ifname "$IFACE" 2>/dev/null || true
    sleep 2

    # Get available SSIDs
    local available
    available=$(nmcli -t -f SSID dev wifi list ifname "$IFACE" 2>/dev/null | sort -u | grep -v '^$' || true)

    if [ -z "$available" ]; then
        return 1
    fi

    # Get saved connections
    local saved
    saved=$(get_saved_wifi)

    if [ -z "$saved" ]; then
        return 1
    fi

    # Check if any saved connection matches an available network
    while IFS= read -r saved_name; do
        # Get the SSID associated with this saved connection
        local saved_ssid
        saved_ssid=$(nmcli -t -f connection.id,802-11-wireless.ssid con show "$saved_name" 2>/dev/null | grep '802-11-wireless.ssid' | cut -d: -f2 || true)

        # If we couldn't get the SSID from connection details, use the connection name as SSID
        if [ -z "$saved_ssid" ]; then
            saved_ssid="$saved_name"
        fi

        if echo "$available" | grep -qxF "$saved_ssid"; then
            echo "$saved_name"
            return 0
        fi
    done <<< "$saved"

    return 1
}

# Activate client mode with a known connection
activate_client_mode() {
    local con_name="$1"
    log "Found known network, activating connection: $con_name"

    # Deactivate hotspot if it's active
    nmcli con down "$HOTSPOT_CON_NAME" 2>/dev/null || true

    # Connect
    if nmcli con up "$con_name" 2>/dev/null; then
        local ip
        ip=$(nmcli -t -f IP4.ADDRESS dev show "$IFACE" 2>/dev/null | head -1 | cut -d: -f2 | cut -d/ -f1 || echo "unknown")
        log "Client mode active. Connected to '$con_name', IP: $ip"
        echo "client" > "$MODE_FILE"
        return 0
    else
        log "Failed to connect to '$con_name'"
        return 1
    fi
}

# Activate hotspot mode
activate_hotspot_mode() {
    log "No known WiFi found. Activating hotspot mode..."

    # Make sure we're not connected to anything
    nmcli dev disconnect "$IFACE" 2>/dev/null || true

    # Activate the hotspot profile
    # NM's shared mode starts built-in dnsmasq for DHCP+DNS.
    # Our config in /etc/NetworkManager/dnsmasq-shared.d/ makes it
    # redirect all DNS queries to 10.42.0.1 for captive portal detection.
    if nmcli con up "$HOTSPOT_CON_NAME" 2>/dev/null; then
        log "Hotspot active. SSID: $(nmcli -t -f 802-11-wireless.ssid con show "$HOTSPOT_CON_NAME" 2>/dev/null | cut -d: -f2)"
        log "DNS redirect active via NM dnsmasq (captive portal mode)"
        echo "hotspot" > "$MODE_FILE"
        return 0
    else
        log "ERROR: Failed to activate hotspot"
        return 1
    fi
}

# Boot mode: scan with retry loop, then fallback to hotspot
boot_mode() {
    log "Boot mode: scanning for known WiFi (${SCAN_TIMEOUT}s timeout)..."

    # Wait for NetworkManager
    if ! wait_for_nm; then
        log "NetworkManager not available, exiting"
        exit 1
    fi

    # Check if hotspot profile exists
    if ! nmcli con show "$HOTSPOT_CON_NAME" &>/dev/null; then
        log "Hotspot profile '$HOTSPOT_CON_NAME' not found. Run 'dw wifi setup' first."
        echo "client" > "$MODE_FILE"
        exit 0
    fi

    # Scan for known WiFi with timeout
    local elapsed=0
    while [ $elapsed -lt $SCAN_TIMEOUT ]; do
        local match
        if match=$(check_for_known_wifi); then
            activate_client_mode "$match"
            exit 0
        fi

        log "No known WiFi found yet, retrying... (${elapsed}s/${SCAN_TIMEOUT}s)"
        sleep $SCAN_INTERVAL
        elapsed=$((elapsed + SCAN_INTERVAL))
    done

    # No known WiFi found after timeout — go to hotspot mode
    activate_hotspot_mode
}

# Check mode: quick single-pass check for periodic timer
check_mode() {
    # Check if hotspot profile exists
    if ! nmcli con show "$HOTSPOT_CON_NAME" &>/dev/null; then
        exit 0
    fi

    local state
    state=$(get_current_state)

    case "$state" in
        client)
            # Already connected — nothing to do
            ;;
        hotspot)
            # In hotspot mode — check if a known network has appeared
            local match
            if match=$(check_for_known_wifi); then
                log "Known WiFi found while in hotspot mode, switching to client..."
                activate_client_mode "$match"
            fi
            ;;
        disconnected)
            # WiFi dropped — try to reconnect or fall back to hotspot
            log "WiFi disconnected, attempting recovery..."
            local match
            if match=$(check_for_known_wifi); then
                activate_client_mode "$match"
            else
                activate_hotspot_mode
            fi
            ;;
    esac
}

# Main
case "${1:-}" in
    --check)
        check_mode
        ;;
    *)
        boot_mode
        ;;
esac
