#!/bin/sh
# One-click network identity randomizer for ImmortalWrt / OpenWrt.
# Copyright (C) 2026
# SPDX-License-Identifier: MIT

set -u

CONF_DIR="/etc/wifi-identity"
BACKUP_DIR="$CONF_DIR/backups"
LAST_FILE="$CONF_DIR/last"
PENDING_FILE="$CONF_DIR/pending"
SCHEDULE_FILE="$CONF_DIR/schedule"
LICENSE_FILE="$CONF_DIR/license.json"
DEFAULT_DELAY=120

mkdir -p "$BACKUP_DIR" 2>/dev/null || true

json_escape() {
    printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/	/\\t/g'
}

rand_hex() {
    n="${1:-1}"
    want=$((n * 2))
    u=$(cat /proc/sys/kernel/random/uuid 2>/dev/null | tr -d '-' || true)
    if [ "${#u}" -lt "$want" ]; then
        u=$(od -An -N"$n" -tx1 /dev/urandom 2>/dev/null | tr -d ' \n' || true)
    fi
    printf '%s' "$u" | cut -c1-"$want"
}

rand_mac() {
    oui="${1:-02}"
    oui="${oui%:}"
    case "$oui" in
        [0-9A-Fa-f][0-9A-Fa-f]) 
            printf '%s:%s' "$oui" "$(rand_hex 5 | sed 's/../&:/g;s/:$//')"
            ;;
        [0-9A-Fa-f][0-9A-Fa-f]:[0-9A-Fa-f][0-9A-Fa-f]:[0-9A-Fa-f][0-9A-Fa-f])
            printf '%s:%s' "$oui" "$(rand_hex 3 | sed 's/../&:/g;s/:$//')"
            ;;
        *)
            printf '02:%s' "$(rand_hex 5 | sed 's/../&:/g;s/:$//')"
            ;;
    esac
}

valid_mac() {
    printf '%s' "$1" | grep -Eq '^[0-9A-Fa-f]{2}(:[0-9A-Fa-f]{2}){5}$'
}

rand_ssid() {
    printf 'WiFi-%s' "$(rand_hex 2)"
}

rand_hostname() {
    printf 'RT-%s' "$(rand_hex 3 | tr 'a-f' 'A-F')"
}

rand_model() {
    printf 'RT-AX%s' "$(rand_hex 2 | tr 'a-f' 'A-F')"
}

mac_of() {
    [ -n "${1:-}" ] || return 0
    cat "/sys/class/net/$1/address" 2>/dev/null || true
}

lan_ports() {
    for d in /sys/class/net/lan[0-9]*; do
        [ -e "$d" ] || continue
        printf '%s\n' "${d##*/}"
    done
}

wifi_sections() {
    uci show wireless 2>/dev/null |
        sed -n 's/^wireless\.\([^.]*\)=wifi-iface$/\1/p' |
        while read -r s; do
            [ "$(uci -q get "wireless.$s.mode" 2>/dev/null)" = "ap" ] || continue
            printf '%s\n' "$s"
        done
}

netboot_detect() {
    found=""
    if uci show dhcp 2>/dev/null | grep -Eiq 'dhcp_option.*(66|67),|next_server|filename|bootfile|tftp|pxe|dhcp-boot'; then
        found="dhcp"
    fi
    if [ -f /etc/dnsmasq.conf ] && grep -Eiq 'dhcp-boot|pxe-service|tftp-root|next-server|filename' /etc/dnsmasq.conf; then
        found="${found:+$found+}dnsmasq"
    fi
    printf '%s' "$found"
}

get_device_section() {
    dev="$1"
    for s in $(uci show network 2>/dev/null | sed -n 's/^network\.\([^.]*\)=device$/\1/p'); do
        [ "$(uci -q get "network.$s.name" 2>/dev/null)" = "$dev" ] && {
            printf '%s\n' "$s"
            return 0
        }
    done
    return 1
}

set_device_mac() {
    dev="$1"
    mac="$2"
    sec=""

    sec=$(get_device_section "$dev" || true)
    if [ -n "$sec" ]; then
        uci set "network.$sec.macaddr=$mac"
        return 0
    fi

    # Never create a bare br-lan device: that can destroy the LAN bridge.
    if [ "$dev" = "br-lan" ]; then
        printf 'WARN: no device section for br-lan, skipped\n' >&2
        return 1
    fi

    sec=$(uci add network device)
    uci set "network.$sec.name=$dev"
    uci set "network.$sec.macaddr=$mac"
}

wan_device() {
    dev=$(uci -q get network.wan.device 2>/dev/null || true)
    [ -n "$dev" ] || dev=$(uci -q get network.wan.ifname 2>/dev/null || true)
    [ -n "$dev" ] || dev="wan"
    printf '%s\n' "${dev%% *}"
}

lan_device() {
    dev=$(uci -q get network.lan.device 2>/dev/null || true)
    [ -n "$dev" ] || dev="br-lan"
    printf '%s\n' "${dev%% *}"
}

get_model() {
    m=""
    [ -s /tmp/sysinfo/model ] && m=$(cat /tmp/sysinfo/model 2>/dev/null || true)
    if [ -z "$m" ] && command -v jsonfilter >/dev/null 2>&1; then
        m=$(ubus call system board 2>/dev/null | jsonfilter -e '@.model' 2>/dev/null || true)
    fi
    [ -n "$m" ] || m=$(sed -n 's/.*"model"[^}]*"name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' /etc/board.json 2>/dev/null | head -n 1)
    printf '%s\n' "$m"
}

get_board() {
    b=""
    if command -v jsonfilter >/dev/null 2>&1; then
        b=$(ubus call system board 2>/dev/null | jsonfilter -e '@.board_name' 2>/dev/null || true)
    fi
    printf '%s\n' "$b"
}

device_code() {
    mid=$(cat /etc/machine-id 2>/dev/null || true)
    [ -n "$mid" ] || mid=$(cat /proc/sys/kernel/random/boot_id 2>/dev/null || true)
    serial=$(tr -d '\000' < /proc/device-tree/serial-number 2>/dev/null || true)
    [ -n "$serial" ] || serial=$(cat /sys/devices/soc0/serial_number 2>/dev/null || true)
    board=$(get_board)
    raw="$mid|$serial|$board"
    if command -v sha256sum >/dev/null 2>&1; then
        h=$(printf '%s' "$raw" | sha256sum | cut -c1-16)
    else
        h=$(printf '%s' "$raw" | md5sum | cut -c1-16)
    fi
    h=$(printf '%s' "$h" | tr 'a-f' 'A-F')
    printf 'DEV-%s\n' "$h"
}

license_status() {
    dc=$(device_code)
    if [ -s "$LICENSE_FILE" ]; then
        lic=$(cat "$LICENSE_FILE" 2>/dev/null || true)
        [ -n "$lic" ] || lic="null"
    else
        lic="null"
    fi
    printf '{"device_code":"%s","license":%s}\n' "$(json_escape "$dc")" "$lic"
}

license_save() {
    b64="$1"
    [ -n "$b64" ] || return 1
    tmp="$LICENSE_FILE.tmp"
    printf '%s' "$b64" | base64 -d > "$tmp" 2>/dev/null || return 1
    # Require token and license_id to avoid saving garbage.
    if command -v jsonfilter >/dev/null 2>&1; then
        jsonfilter -i "$tmp" -e '@.token' >/dev/null 2>&1 || {
            rm -f "$tmp"
            return 1
        }
    fi
    mv -f "$tmp" "$LICENSE_FILE"
    chmod 600 "$LICENSE_FILE"
    printf 'OK\n'
}

license_clear() {
    rm -f "$LICENSE_FILE"
    printf 'OK\n'
}

set_model_name() {
    name="$1"
    [ -n "$name" ] || return 1
    [ -f /etc/board.json ] || return 1

    # Keep the value simple so it is safe inside board.json.
    name=$(printf '%s' "$name" | tr -cd 'A-Za-z0-9 ._-')
    [ -n "$name" ] || return 1

    cp -f /etc/board.json "$CONF_DIR/board.json.bak" 2>/dev/null || true
    esc=$(printf '%s' "$name" | sed 's/[\/&]/\\&/g')
    sed -i "/\"model\"[[:space:]]*:/,/^[[:space:]]*}/ s/\"name\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"name\": \"$esc\"/" /etc/board.json
    printf '%s\n' "$name" > /tmp/sysinfo/model 2>/dev/null || true
}

set_lan_subnet() {
    ip="$1"
    netmask="$2"
    start="$3"
    limit="$4"

    uci set network.lan.ipaddr="$ip"
    uci set network.lan.netmask="$netmask"
    uci set dhcp.lan.start="$start"
    uci set dhcp.lan.limit="$limit"
    uci set dhcp.lan.leasetime="12h"
}

backup() {
    ts=$(date +%Y%m%d-%H%M%S)
    d="$BACKUP_DIR/$ts"
    mkdir -p "$d"

    for f in network wireless system dhcp; do
        [ -f "/etc/config/$f" ] && cp "/etc/config/$f" "$d/$f"
    done
    [ -f /etc/board.json ] && cp /etc/board.json "$d/board.json"

    ldev=$(lan_device)
    wdev=$(wan_device)
    cat > "$d/meta" <<EOF
time=$ts
lan=$(mac_of "$ldev")
wan=$(mac_of "$wdev")
hostname=$(uci -q get system.@system[0].hostname 2>/dev/null || true)
subnet=$(uci -q get network.lan.ipaddr 2>/dev/null || true)
EOF

    printf '%s\n' "$d" > "$LAST_FILE"

    n=0
    for old in $(ls -1dt "$BACKUP_DIR"/* 2>/dev/null); do
        n=$((n + 1))
        [ "$n" -gt 10 ] && rm -rf "$old"
    done

    printf '%s\n' "$d"
}

restore_dir() {
    d="$1"
    case "$d" in
        /*) ;;
        *) d="$BACKUP_DIR/$d" ;;
    esac

    [ -n "$d" ] && [ -d "$d" ] || {
        printf 'backup not found\n' >&2
        return 1
    }

    for f in network wireless system dhcp; do
        [ -f "$d/$f" ] && cp "$d/$f" "/etc/config/$f"
    done
    [ -f "$d/board.json" ] && cp "$d/board.json" /etc/board.json

    uci commit network 2>/dev/null || true
    uci commit wireless 2>/dev/null || true
    uci commit system 2>/dev/null || true
    uci commit dhcp 2>/dev/null || true
    rm -f "$PENDING_FILE"

    /etc/init.d/network restart
    sleep 2
    /etc/init.d/dnsmasq restart 2>/dev/null || true
    /etc/init.d/odhcpd restart 2>/dev/null || true
    wifi reload 2>/dev/null || true
    printf 'restored %s\n' "$d"
}

restore_latest() {
    if [ -s "$LAST_FILE" ]; then
        restore_dir "$(cat "$LAST_FILE")"
        return $?
    fi

    d=$(ls -1dt "$BACKUP_DIR"/* 2>/dev/null | head -n 1 || true)
    restore_dir "$d"
}

schedule_status() {
    if [ -f "$SCHEDULE_FILE" ]; then
        . "$SCHEDULE_FILE"
        printf '"schedule":{"enabled":%s,"time":"%s"}' \
            "$([ "${SCHEDULE:-0}" = "1" ] && echo true || echo false)" \
            "$(json_escape "${SCHEDULE_TIME:-00:20}")"
    else
        printf '"schedule":{"enabled":false,"time":"00:20"}'
    fi
}

status() {
    host=$(uci -q get system.@system[0].hostname 2>/dev/null || true)
    [ -n "$host" ] || host=$(cat /proc/sys/kernel/hostname 2>/dev/null || true)

    wdev=$(wan_device)
    ldev=$(lan_device)
    model=$(get_model)
    board=$(get_board)

    printf '{'
    printf '"hostname":"%s",' "$(json_escape "$host")"
    printf '"model":"%s",' "$(json_escape "$model")"
    printf '"board":"%s",' "$(json_escape "$board")"
    printf '"wan":{"device":"%s","mac":"%s"},' \
        "$(json_escape "$wdev")" "$(json_escape "$(mac_of "$wdev")")"
    printf '"lan":{"device":"%s","mac":"%s","ip":"%s","netmask":"%s"},' \
        "$(json_escape "$ldev")" "$(json_escape "$(mac_of "$ldev")")" \
        "$(json_escape "$(uci -q get network.lan.ipaddr 2>/dev/null || true)")" \
        "$(json_escape "$(uci -q get network.lan.netmask 2>/dev/null || true)")"

    printf '"lan_ports":['
    first=1
    for d in $(lan_ports); do
        [ "$first" -eq 1 ] || printf ','
        first=0
        printf '{"name":"%s","mac":"%s"}' \
            "$(json_escape "$d")" "$(json_escape "$(mac_of "$d")")"
    done
    printf '],'

    printf '"wifi":['
    first=1
    for s in $(wifi_sections); do
        [ "$first" -eq 1 ] || printf ','
        first=0
        printf '{"section":"%s","device":"%s","ssid":"%s","mac":"%s"}' \
            "$(json_escape "$s")" \
            "$(json_escape "$(uci -q get "wireless.$s.device" 2>/dev/null || true)")" \
            "$(json_escape "$(uci -q get "wireless.$s.ssid" 2>/dev/null || true)")" \
            "$(json_escape "$(uci -q get "wireless.$s.macaddr" 2>/dev/null || true)")"
    done
    printf '],'

    printf '"backups":['
    first=1
    for d in $(ls -1dt "$BACKUP_DIR"/* 2>/dev/null | head -n 10); do
        [ -d "$d" ] || continue
        id=${d##*/}
        t=$(printf '%s' "$id" | sed -n 's/^\([0-9]\{4\}\)\([0-9]\{2\}\)\([0-9]\{2\}\)-\([0-9]\{2\}\)\([0-9]\{2\}\)\([0-9]\{2\}\)$/\1-\2-\3 \4:\5:\6/p')
        [ -n "$t" ] || t="$id"
        [ "$first" -eq 1 ] || printf ','
        first=0
        printf '{"id":"%s","time":"%s","lan":"%s","wan":"%s","hostname":"%s","subnet":"%s"}' \
            "$(json_escape "$id")" "$(json_escape "$t")" \
            "$(json_escape "$(sed -n 's/^lan=//p' "$d/meta" 2>/dev/null | head -n 1)")" \
            "$(json_escape "$(sed -n 's/^wan=//p' "$d/meta" 2>/dev/null | head -n 1)")" \
            "$(json_escape "$(sed -n 's/^hostname=//p' "$d/meta" 2>/dev/null | head -n 1)")" \
            "$(json_escape "$(sed -n 's/^subnet=//p' "$d/meta" 2>/dev/null | head -n 1)")"
    done
    printf '],'

    if [ -f "$PENDING_FILE" ]; then
        printf '"pending":true,'
    else
        printf '"pending":false,'
    fi
    printf '"last_backup":"%s",' \
        "$(json_escape "$(cat "$LAST_FILE" 2>/dev/null || true)")"

    printf '%s' "$(schedule_status)"
    nb=$(netboot_detect)
    printf ',"netboot":{"detected":%s,"evidence":"%s"}' \
        "$([ -n "$nb" ] && echo true || echo false)" "$(json_escape "$nb")"

    printf ',"checks":['
    first=1
    add_check() {
        [ "$first" -eq 1 ] || printf ','
        first=0
        printf '{"name":"%s","ok":%s,"detail":"%s"}' \
            "$(json_escape "$1")" "$2" "$(json_escape "$3")"
    }
    add_check "br-lan" "$([ -d "/sys/class/net/$ldev" ] && echo true || echo false)" "$ldev"
    add_check "WAN 设备" "$([ -d "/sys/class/net/$wdev" ] && echo true || echo false)" "$wdev"
    add_check "LAN 口" "$([ -n "$(lan_ports)" ] && echo true || echo false)" "$(lan_ports | tr '\n' ' ')"
    add_check "board.json" "$([ -f /etc/board.json ] && echo true || echo false)" "/etc/board.json"
    add_check "jsonfilter" "$(command -v jsonfilter >/dev/null 2>&1 && echo true || echo false)" "用于读取型号"
    add_check "iwinfo" "$(command -v iwinfo >/dev/null 2>&1 && echo true || echo false)" "用于查看 Wi-Fi"
    if [ -n "$nb" ]; then
        add_check "无盘/PXE" false "检测到：$nb"
    else
        add_check "无盘/PXE" true "未检测到"
    fi
    add_check "cron" "$([ -x /etc/init.d/cron ] && echo true || echo false)" "定时任务"
    if command -v opkg >/dev/null 2>&1 || command -v apk >/dev/null 2>&1; then
        add_check "包管理器" true "opkg/apk"
    else
        add_check "包管理器" false "opkg/apk"
    fi
    printf ']'

    printf '}\n'
}

apply() {
    if [ "$LAN_IP" = "1" ]; then
        nb=$(netboot_detect)
        if [ -n "$nb" ] && [ "${FORCE_NETBOOT:-0}" != "1" ]; then
            printf 'ERROR: 检测到无盘/PXE 引导配置（%s），拒绝修改 LAN 网段。\n' "$nb" >&2
            printf '确认现场可恢复后，再加 --force-netboot 1 重试。\n' >&2
            exit 2
        fi
    fi

    backup >/dev/null

    changed_net=0

    if [ "$HOSTNAME" = "1" ]; then
        if [ -n "${HOSTNAME_VALUE:-}" ]; then
            hn="$HOSTNAME_VALUE"
        else
            hn=$(rand_hostname)
        fi
        uci set "system.@system[0].hostname=$hn"
    fi

    if [ "$MODEL" = "1" ]; then
        if [ -n "${MODEL_VALUE:-}" ]; then
            mn="$MODEL_VALUE"
        else
            mn=$(rand_model)
        fi
        set_model_name "$mn" || true
    fi

    if [ "$WAN_MAC" = "1" ]; then
        if [ -n "${WAN_MAC_VALUE:-}" ] && valid_mac "$WAN_MAC_VALUE"; then
            wm="$WAN_MAC_VALUE"
        else
            wm=$(rand_mac "$OUI")
        fi
        set_device_mac "$(wan_device)" "$wm" || true
        changed_net=1
    fi

    if [ "$LAN_MAC" = "1" ]; then
        if [ -n "${LAN_MAC_VALUE:-}" ] && valid_mac "$LAN_MAC_VALUE"; then
            lm="$LAN_MAC_VALUE"
        else
            lm=$(rand_mac "$OUI")
        fi
        set_device_mac "$(lan_device)" "$lm" || true
        changed_net=1
    fi

    if [ "$LAN_PORTS" = "1" ]; then
        for d in $(lan_ports); do
            set_device_mac "$d" "$(rand_mac "$OUI")" || true
        done
        changed_net=1
    fi

    if [ "$WIFI_SSID" = "1" ] || [ "$WIFI_MAC" = "1" ]; then
        for s in $(wifi_sections); do
            [ "$WIFI_SSID" = "1" ] && uci set "wireless.$s.ssid=$(rand_ssid)"
            [ "$WIFI_MAC" = "1" ] && uci set "wireless.$s.macaddr=$(rand_mac "$OUI")"
        done
    fi

    if [ "$LAN_IP" = "1" ]; then
        if [ -n "${LAN_IP_VALUE:-}" ]; then
            lip="$LAN_IP_VALUE"
        else
            x=$(od -An -N1 -tu1 /dev/urandom | tr -d ' ')
            lip="192.168.$((x % 180 + 20)).1"
        fi
        set_lan_subnet "$lip" "255.255.255.0" "${DHCP_START:-100}" "${DHCP_LIMIT:-150}"
        changed_net=1
    fi

    uci commit network 2>/dev/null || true
    uci commit wireless 2>/dev/null || true
    uci commit system 2>/dev/null || true
    uci commit dhcp 2>/dev/null || true

    if [ "$SAFETY" = "1" ]; then
        d=$(cat "$LAST_FILE" 2>/dev/null || true)
        if [ -n "$d" ]; then
            printf '%s\n' "$d" > "$PENDING_FILE"
            (
                sleep "${SAFETY_DELAY:-$DEFAULT_DELAY}"
                if [ -f "$PENDING_FILE" ]; then
                    restore_dir "$(cat "$PENDING_FILE")" >/dev/null 2>&1
                fi
            ) &
        fi
    fi

    if [ "$changed_net" = "1" ]; then
        /etc/init.d/network restart
        sleep 2
        /etc/init.d/dnsmasq restart 2>/dev/null || true
        /etc/init.d/odhcpd restart 2>/dev/null || true
    fi

    if [ "$WIFI_SSID" = "1" ] || [ "$WIFI_MAC" = "1" ]; then
        wifi reload 2>/dev/null || true
    fi

    if [ "$HOSTNAME" = "1" ]; then
        /etc/init.d/system reload 2>/dev/null || true
    fi

    printf 'OK\n'
}

schedule() {
    time="$1"
    shift

    hour=${time%%:*}
    min=${time#*:}
    case "$hour" in ''|*[!0-9]*) hour=0 ;; esac
    case "$min" in ''|*[!0-9]*) min=20 ;; esac

    if [ "$SCHEDULE_ENABLE" != "1" ]; then
        sed -i '/# wifi-identity/d' /etc/crontabs/root 2>/dev/null || true
        rm -f "$SCHEDULE_FILE"
        /etc/init.d/cron restart 2>/dev/null || true
        printf 'disabled\n'
        return 0
    fi

    cat > "$SCHEDULE_FILE" <<EOF
SCHEDULE=1
SCHEDULE_TIME="$time"
LAN_MAC="$LAN_MAC"
LAN_PORTS="$LAN_PORTS"
WAN_MAC="$WAN_MAC"
HOSTNAME="$HOSTNAME"
MODEL="$MODEL"
LAN_IP="$LAN_IP"
WIFI_SSID="$WIFI_SSID"
WIFI_MAC="$WIFI_MAC"
OUI="$OUI"
EOF

    line="$min $hour * * * /usr/bin/wifi-identity apply --lan-mac $LAN_MAC --lan-ports $LAN_PORTS --wan-mac $WAN_MAC --hostname $HOSTNAME --model $MODEL --lan-ip $LAN_IP --wifi-ssid $WIFI_SSID --wifi-mac $WIFI_MAC --oui '$OUI' --safety 0 >/dev/null 2>&1 # wifi-identity"
    sed -i '/# wifi-identity/d' /etc/crontabs/root 2>/dev/null || true
    printf '%s\n' "$line" >> /etc/crontabs/root
    /etc/init.d/cron restart 2>/dev/null || true
    printf 'scheduled %s\n' "$time"
}

usage() {
    cat <<'EOF'
Usage: wifi-identity <command> [options]

Commands:
  status                 Print current identity as JSON
  apply [options]        Apply randomized identity
  backup                 Back up config and current identity
  restore [id|dir]       Restore a backup
  confirm                Keep changes and cancel the safety-net rollback
  schedule [options]     Install/remove the daily cron job
  device-code            Print stable hardware device code
  license-status         Print device code and stored license JSON
  license-save <base64>  Save a license JSON payload
  license-clear          Remove the stored license

Apply options (1 = enable, 0 = skip):
  --lan-mac 1|0          Randomize br-lan MAC
  --lan-ports 1|0        Randomize lan1..lanN MAC
  --wan-mac 1|0          Randomize WAN device MAC
  --hostname 1|0         Randomize system hostname
  --model 1|0            Change display model (experimental)
  --lan-ip 1|0           Randomize LAN subnet
  --wifi-ssid 1|0        Randomize every AP SSID
  --wifi-mac 1|0         Randomize every AP BSSID/MAC
  --safety 1|0           Auto-restore after 120s unless confirmed
  --force-wan 1|0        Explicitly allow WAN MAC change
  --force-netboot 1|0    Allow LAN subnet change even if PXE/diskless is detected
  --oui XX:XX:XX         MAC OUI prefix, default 02
  --lan-mac-value MAC    Use this LAN MAC instead of random
  --wan-mac-value MAC    Use this WAN MAC instead of random
  --hostname-value NAME  Use this hostname
  --model-value NAME     Use this display model
  --lan-ip-value IP      Use this LAN gateway IP
  --dhcp-start N         DHCP pool start, default 100
  --dhcp-limit N         DHCP pool size, default 150

Schedule options:
  --time HH:MM           Daily run time
  --enable 1|0           Enable/disable schedule
  --lan-mac 1|0 ...      Same item flags as apply
EOF
}

LAN_MAC=1
LAN_PORTS=1
WAN_MAC=1
HOSTNAME=1
MODEL=0
LAN_IP=0
WIFI_SSID=1
WIFI_MAC=1
SAFETY=0
SAFETY_DELAY=120
FORCE_WAN=0
FORCE_NETBOOT=0
OUI="02"
LAN_MAC_VALUE=""
WAN_MAC_VALUE=""
HOSTNAME_VALUE=""
MODEL_VALUE=""
LAN_IP_VALUE=""
DHCP_START=100
DHCP_LIMIT=150
SCHEDULE_ENABLE=1
SCHEDULE_TIME="00:20"

if [ $# -eq 0 ]; then
    usage
    exit 1
fi

cmd="$1"
shift

case "$cmd" in
    status)
        status
        ;;
    device-code)
        device_code
        ;;
    license-status)
        license_status
        ;;
    license-save)
        [ $# -ge 1 ] || {
            printf 'missing license payload\n' >&2
            exit 1
        }
        license_save "$1"
        ;;
    license-clear)
        license_clear
        ;;
    apply)
        while [ $# -gt 0 ]; do
            case "$1" in
                --lan-mac) [ $# -ge 2 ] && LAN_MAC="$2" && shift 2 || shift ;;
                --lan-bridge) [ $# -ge 2 ] && LAN_MAC="$2" && shift 2 || shift ;;
                --lan-ports) [ $# -ge 2 ] && LAN_PORTS="$2" && shift 2 || shift ;;
                --wan) [ $# -ge 2 ] && WAN_MAC="$2" && shift 2 || shift ;;
                --wan-mac) [ $# -ge 2 ] && WAN_MAC="$2" && shift 2 || shift ;;
                --hostname) [ $# -ge 2 ] && HOSTNAME="$2" && shift 2 || shift ;;
                --model) [ $# -ge 2 ] && MODEL="$2" && shift 2 || shift ;;
                --lan-ip) [ $# -ge 2 ] && LAN_IP="$2" && shift 2 || shift ;;
                --wifi-ssid) [ $# -ge 2 ] && WIFI_SSID="$2" && shift 2 || shift ;;
                --wifi-mac) [ $# -ge 2 ] && WIFI_MAC="$2" && shift 2 || shift ;;
                --safety) [ $# -ge 2 ] && SAFETY="$2" && shift 2 || shift ;;
                --safety-delay) [ $# -ge 2 ] && SAFETY_DELAY="$2" && shift 2 || shift ;;
                --force-wan) [ $# -ge 2 ] && FORCE_WAN="$2" && shift 2 || shift ;;
                --force-netboot) [ $# -ge 2 ] && FORCE_NETBOOT="$2" && shift 2 || shift ;;
                --oui) [ $# -ge 2 ] && OUI="$2" && shift 2 || shift ;;
                --lan-mac-value) [ $# -ge 2 ] && LAN_MAC_VALUE="$2" && shift 2 || shift ;;
                --wan-mac-value) [ $# -ge 2 ] && WAN_MAC_VALUE="$2" && shift 2 || shift ;;
                --hostname-value) [ $# -ge 2 ] && HOSTNAME_VALUE="$2" && shift 2 || shift ;;
                --model-value) [ $# -ge 2 ] && MODEL_VALUE="$2" && shift 2 || shift ;;
                --lan-ip-value) [ $# -ge 2 ] && LAN_IP_VALUE="$2" && shift 2 || shift ;;
                --dhcp-start) [ $# -ge 2 ] && DHCP_START="$2" && shift 2 || shift ;;
                --dhcp-limit) [ $# -ge 2 ] && DHCP_LIMIT="$2" && shift 2 || shift ;;
                *) shift ;;
            esac
        done
        apply
        ;;
    backup)
        backup
        ;;
    restore)
        if [ $# -ge 1 ] && [ -n "$1" ]; then
            restore_dir "$1"
        else
            restore_latest
        fi
        ;;
    confirm)
        rm -f "$PENDING_FILE"
        printf 'confirmed\n'
        ;;
    schedule)
        while [ $# -gt 0 ]; do
            case "$1" in
                --time) [ $# -ge 2 ] && SCHEDULE_TIME="$2" && shift 2 || shift ;;
                --enable) [ $# -ge 2 ] && SCHEDULE_ENABLE="$2" && shift 2 || shift ;;
                --lan-mac) [ $# -ge 2 ] && LAN_MAC="$2" && shift 2 || shift ;;
                --lan-ports) [ $# -ge 2 ] && LAN_PORTS="$2" && shift 2 || shift ;;
                --wan-mac) [ $# -ge 2 ] && WAN_MAC="$2" && shift 2 || shift ;;
                --hostname) [ $# -ge 2 ] && HOSTNAME="$2" && shift 2 || shift ;;
                --model) [ $# -ge 2 ] && MODEL="$2" && shift 2 || shift ;;
                --lan-ip) [ $# -ge 2 ] && LAN_IP="$2" && shift 2 || shift ;;
                --wifi-ssid) [ $# -ge 2 ] && WIFI_SSID="$2" && shift 2 || shift ;;
                --wifi-mac) [ $# -ge 2 ] && WIFI_MAC="$2" && shift 2 || shift ;;
                --oui) [ $# -ge 2 ] && OUI="$2" && shift 2 || shift ;;
                *) shift ;;
            esac
        done
        schedule "$SCHEDULE_TIME"
        ;;
    -h|--help|help)
        usage
        ;;
    *)
        usage
        exit 1
        ;;
esac
