#!/usr/bin/expect -f
# Assigns Microhard modem identity and network ID over Telnet.
#
# Required usage:
#   mh-assign --config <path to node-ids.txt file> --host <radio-ip> --user <user> --pass <password> --bypass <yes|no>
#
set SCRIPT_NAME "microhard-net-assign"
set TELNET_PORT "23"
set TELNET_FLAG "/var/lib/microhard/.microhard_assigned"
set TIMEOUT_SECONDS "60"
set COMMAND_GAP_MS "8000"
set MWNETWORKID_PREFIX "PMDDL_g35-"
set MSMNAME_PREFIX "microhard-"
set timeout $TIMEOUT_SECONDS
log_user 1

# -----------------------------
# Logging
# -----------------------------
proc now {} {
    return [clock format [clock seconds] -format {%Y-%m-%d %H:%M:%S}]
}

proc log_info {msg} {
    global SCRIPT_NAME
    puts "\[$SCRIPT_NAME\] [now] INFO: $msg"
}

proc log_warn {msg} {
    global SCRIPT_NAME
    puts stderr "\[$SCRIPT_NAME\] [now] WARN: $msg"
}

proc log_error {msg} {
    global SCRIPT_NAME
    puts stderr "\[$SCRIPT_NAME\] [now] ERROR: $msg"
}

proc die {msg {code 1}} {
    log_error $msg
    exit $code
}

# -----------------------------
# Usage / argument handling
# -----------------------------
proc usage {} {
    puts stderr {Usage:
  mh-assign --config <node-ids.txt> --host <radio-ip> --user <user> --pass <password> --bypass <yes|no>

Required arguments:
  --config <node-ids.txt>       Path to node-ids.txt containing strict SERIAL and NET-ID lines
  --host <radio-ip>        Radio Telnet IP address, example: 192.168.168.1
  --user <user>            Telnet username
  --pass <password>        Telnet password
  --bypass <yes|no>        yes = run even if /var/lib/microhard/.microhard_assigned exists
                           no  = skip if /var/lib/microhard/.microhard_assigned exists

Required format with example (node-ids.txt):
  SERIAL GCS.00000
  NET-ID CDA.00000

Examples:
  sudo mh-assign --config /opt/microavia/config/node-ids.txt --host 192.168.168.1 --user admin --pass microavia --bypass no
}

proc print_missing_usage_and_exit {CONFIG_PATH HOST USER PASS BYPASS} {
    set missing 0

    if {$CONFIG_PATH eq ""} {
        log_warn "missing required argument: --config <path to node-ids.txt>"
        set missing 1
    }

    if {$HOST eq ""} {
        log_warn "missing required argument: --host <radio-ip>"
        set missing 1
    }

    if {$USER eq ""} {
        log_warn "missing required argument: --user <user>"
        set missing 1
    }

    if {$PASS eq "__UNSET__"} {
        log_warn "missing required argument: --pass <password>"
        set missing 1
    }

    if {$BYPASS eq ""} {
        log_warn "missing required argument: --bypass <yes|no>"
        set missing 1
    }

    if {$missing == 1} {
        puts stderr ""
        log_error "missing required arguments"
        puts stderr ""
        usage
        exit 2
    }
}

set CONFIG_PATH ""
set HOST ""
set USER ""
set PASS "__UNSET__"
set BYPASS ""

if {$argc == 0} {
    log_error "missing required arguments"
    puts stderr ""
    usage
    exit 2
}

set i 0
while {$i < $argc} {
    set arg [lindex $argv $i]

    switch -- $arg {
        "--config" {
            incr i
            if {$i >= $argc} {
                log_error "--config requires a value"
                puts stderr ""
                usage
                exit 2
            }
            set CONFIG_PATH [lindex $argv $i]
        }

        "--host" {
            incr i
            if {$i >= $argc} {
                log_error "--host requires a value"
                puts stderr ""
                usage
                exit 2
            }
            set HOST [lindex $argv $i]
        }

        "--user" {
            incr i
            if {$i >= $argc} {
                log_error "--user requires a value"
                puts stderr ""
                usage
                exit 2
            }
            set USER [lindex $argv $i]
        }

        "--pass" {
            incr i
            if {$i >= $argc} {
                log_error "$arg requires a value"
                puts stderr ""
                usage
                exit 2
            }
            set PASS [lindex $argv $i]
        }

        "--bypass" {
            incr i
            if {$i >= $argc} {
                log_error "--bypass requires a value: yes or no"
                puts stderr ""
                usage
                exit 2
            }
            set BYPASS [lindex $argv $i]
        }

        "-h" -
        "--help" {
            usage
            exit 0
        }

        default {
            log_error "unknown argument: $arg"
            puts stderr ""
            usage
            exit 2
        }
    }

    incr i
}

print_missing_usage_and_exit $CONFIG_PATH $HOST $USER $PASS $BYPASS

switch -- $BYPASS {
    "yes" -
    "no" {
        # valid
    }

    default {
        log_error "invalid --bypass value: $BYPASS. Expected: yes or no"
        puts stderr ""
        usage
        exit 2
    }
}

if {![file exists $CONFIG_PATH]} {
    die "config file not found: $CONFIG_PATH" 12
}

if {![file readable $CONFIG_PATH]} {
    die "config file is not readable: $CONFIG_PATH" 13
}

# -----------------------------
# Config parsing
# -----------------------------
proc read_file_lines {path} {
    set fh [open $path r]
    set data [read $fh]
    close $fh
    return [split $data "\n"]
}

proc extract_strict_rc_value {path key} {
    set lines [read_file_lines $path]
    foreach raw_line $lines {
        set line [string trim $raw_line]

        if {$line eq ""} {
            continue
        }

        if {[string match "#*" $line]} {
            continue
        }

        set pattern [format {^%s[ 	]+(.+)$} $key]
        if {[regexp -- $pattern $line -> value]} {
            set value [string trim $value]

            if {$value eq ""} {
                die "empty value for $key in config file: $path" 20
            }
            return $value
        }
    }

    return ""
}

set SERIAL_ID [extract_strict_rc_value $CONFIG_PATH "SERIAL"]
set NET_ID    [extract_strict_rc_value $CONFIG_PATH "NET-ID"]

if {$SERIAL_ID eq ""} {
    die "required key not found in config file: SERIAL. Expected line format: SERIAL <value>" 20
}

if {$NET_ID eq ""} {
    die "required key not found in config file: NET-ID. Expected line format: NET-ID <value>" 21
}

regsub -all {\.} $SERIAL_ID {-} SERIAL_ID_NORMALIZED
set MWNETWORKID "${MWNETWORKID_PREFIX}${NET_ID}"
set MSMNAME     "${MSMNAME_PREFIX}${SERIAL_ID_NORMALIZED}"

# -----------------------------
# Flag / bypass logic
# -----------------------------
if {[file exists $TELNET_FLAG] && $BYPASS eq "no"} {
    log_info "modem already assigned. REASON: configured flag exists: $TELNET_FLAG"
    log_info "bypass=no, skipping assignment. Use --bypass yes to force assignment."
    exit 0
}

if {[file exists $TELNET_FLAG] && $BYPASS eq "yes"} {
    log_warn "assignment flag already exists but bypass=yes, forcing assignment: $TELNET_FLAG"
}

# -----------------------------
# Telnet helpers, logic, and function
# -----------------------------
proc send_and_wait {cmd} {
    global COMMAND_GAP_MS
    send -- "$cmd\r"
    after $COMMAND_GAP_MS

    expect {
        -re {\bOK\b}             { return }
        -re {[\r\n].*[>#]\s?$}   { return }
        timeout                  { die "Timeout waiting after: $cmd" }
        eof                      { die "Remote closed connection after: $cmd" }
    }
}

proc write_telnet_flag {} {
    global TELNET_FLAG CONFIG_PATH HOST NET_ID SERIAL_ID SERIAL_ID_NORMALIZED MWNETWORKID MSMNAME
    catch { file mkdir [file dirname $TELNET_FLAG] }
    set fh [open $TELNET_FLAG w]
    puts $fh "assigned_at=[clock format [clock seconds] -format {%Y-%m-%dT%H:%M:%S%z}]"
    puts $fh "config_path=$CONFIG_PATH"
    puts $fh "host=$HOST"
    puts $fh "net_id=$NET_ID"
    puts $fh "serial_id_raw=$SERIAL_ID"
    puts $fh "serial_id=$SERIAL_ID_NORMALIZED"
    puts $fh "mwnetworkid=$MWNETWORKID"
    puts $fh "msmname=$MSMNAME"
    close $fh
    log_info "assignment succeeded. writing flag: $TELNET_FLAG"
}

# -----------------------------
# Main
# -----------------------------

log_info "========== Microhard Modem Assignment Started =========="
log_info "config file: $CONFIG_PATH"
log_info "target radio: $HOST:$TELNET_PORT"
log_info "flag path: $TELNET_FLAG"
log_info "bypass existing flag: $BYPASS"
log_info "NET_ID=$NET_ID"
log_info "SERIAL_ID=$SERIAL_ID"
log_info "MWNETWORKID=$MWNETWORKID"
log_info "MSMNAME=$MSMNAME"

puts " Starting modem assignment on $HOST ..."
spawn telnet $HOST $TELNET_PORT

expect {
    -re {(?i)login:}       { send -- "$USER\r" }
    -re {(?i)username:}    { send -- "$USER\r" }
    -re {[\r\n].*[>#]\s?$} { }
    timeout                { die "Timeout waiting for login prompt" }
    eof                    { die "Connection closed before login prompt" }
}

expect {
    -re {(?i)Password:}    { send -- "$PASS\r" }
    -re {[\r\n].*[>#]\s?$} { }
    timeout                { die "Timeout waiting for password prompt" }
    eof                    { die "Connection closed before password prompt" }
}

expect {
    -re {[\r\n].*[>#]\s?$} { }
    -re {\bOK\b}           { }
    -re {[\r\n]}           { exp_continue }
    timeout                { die "Timeout waiting for command prompt after login" }
    eof                    { die "Connection closed after login" }
}

send_and_wait "AT+MWNETWORKID=$MWNETWORKID"
send_and_wait "AT+MSMNAME=$MSMNAME"
send_and_wait "AT&W"
send -- "exit\r"
expect eof

write_telnet_flag
log_info "========== Microhard Modem Assignment: SUCCESS =========="

exit 0