#!/usr/bin/env bash

trap 'killm f; stty sane 2>/dev/null' EXIT INT TERM  # restore echo (if sudo fail)

# Function to search a log file for a string after a specified line number
# Arguments:
#   $1: log_file - Path to the log file
#   $2: search_string - Exact string to match
#   $3: start_line - Line number after which to start searching
# Returns:
#   -1: No matches or error (file missing, unreadable, invalid start_line)
#   Positive integer: Line number of the first match found
search_log_after_line() {
    local log_file="$1"
    local search_string="$2"
    local start_line="$3"
    local line_number

    # Check if log_file is provided and exists
    if [ -z "$log_file" ] || [ ! -f "$log_file" ]; then
        echo "Error: Log file '$log_file' does not exist or is not specified" >&2
        return 1
    fi

    # Check if log_file is readable
    if [ ! -r "$log_file" ]; then
        echo "Error: Log file '$log_file' is not readable" >&2
        return 1
    fi

    # Check if search_string is provided
    if [ -z "$search_string" ]; then
        echo "Error: Search string is not specified" >&2
        return 1
    fi

    # Check if start_line is a positive integer
    if ! [[ "$start_line" =~ ^[0-9]+$ ]] || [ "$start_line" -lt 0 ]; then
        echo "Error: Start line '$start_line' is not a valid positive integer" >&2
        return 1
    fi

    # Use tail to skip lines up to start_line, then grep for the string
    # tail -n +N starts from line N, so use start_line + 1
    local tail_start=$((start_line + 1))
    local match
    match=$(tail -n +"$tail_start" "$log_file" | grep -Fn "$search_string" | head -n 1)

    # Check if a match was found
    if [ -z "$match" ]; then
        echo "No matches found for '$search_string' in '$log_file' after line $start_line" >&2
        return 1
    fi

    # Extract the line number from grep output (format: line_number:line_content)
    line_number=$(echo "$match" | cut -d: -f1)
    # Adjust line number to account for tail offset
    line_number=$((line_number + start_line))

    echo "Match found at line $line_number: $search_string" >&2
    echo "$line_number"
    return 0
}

# documentation boilerplate
CONFIGFILES=" \
./2.bench.ltp/bench.bprc \
./2.bench.ltp/bench.ionconfig \
./2.bench.ltp/bench.ionrc \
./2.bench.ltp/bench.ionsecrc \
./2.bench.ltp/bench.ipnrc \
./3.bench.ltp/bench.bprc \
./3.bench.ltp/bench.ionconfig \
./3.bench.ltp/bench.ionrc \
./3.bench.ltp/bench.ionsecrc \
./3.bench.ltp/bench.ipnrc \
"

echo "########################################"
echo
pwd | sed "s/\/.*\///" | xargs echo "NAME: "
echo
echo "PURPOSE: Test ION's IP address caching behavior in udplso for LTP. ION will periodically
               re-resolve IP address from hostname in order to detect DNS changes, and
               periodically provide ion.log report for IP address look up failure."

echo -e "\n\tNOTE: This test requires sudo privilege to manually change /etc/hosts file
                   to simulate the effect of changes in DNS database."

sleep 3

echo
echo "CONFIG: 2 node custom:"
echo
for N in $CONFIGFILES
do
    echo "$N:"
    cat "$N"
    echo "# EOF"
    echo
done
echo "OUTPUT: Terminal messages will relay results."
echo
echo "########################################"
echo

# Configurations
HOSTS_FILE="/etc/hosts"
BACKUP_FILE="$PWD/hosts.bak"
ENTRY="127.0.1.3    test3.invalid"
LOG_FILE="ion.log"
SEARCH_STRING1="udplso: DNS resolution failed for test3.invalid:3113"
SEARCH_STRING2="parseSocketSpec: Parsed: test3.invalid:3113"
SEARCH_STRING3="(0) 0 0 (1) 6 42 (2) 0 0 (+) 6 42"
LINE_NUM=1

# --- sudo-availability check (non-interactive, max 15 s) -------------
if [ "$(id -u)" -eq 0 ]; then
    printf '%s\n\n' "Running as root — skipping sudo availability check."
    snpwd() { "$@"; }          # run command directly, no sudo needed
else
    printf '%s\n' "This test requires sudo privileges: checking now…"
    printf '%s\n\n' \
      "Hint: use  sudo -E  if you need to preserve ION_RUN_EXPERT."

    sudo -n -v 2>/dev/null & SUDO_PID=$!

    # start watchdog
    ( sleep 15 && kill -s TERM "$SUDO_PID" 2>/dev/null && \
    sleep 2 && kill -s KILL "$SUDO_PID" 2>/dev/null ) & WATCH_PID=$!

    wait "$SUDO_PID";  SUDO_RESULT=$? # stop watchdog quietly

    kill "$WATCH_PID" 2>/dev/null && wait "$WATCH_PID" 2>/dev/null
    stty sane 2>/dev/null || : # restore echo just in case

    if [ "$SUDO_RESULT" -ne 0 ]; then
        printf '%s\n' \
          "Password-less sudo required or lookup timed-out." >&2
        exit 2
    fi
    printf '%s\n\n' "Sudo privileges confirmed: continuing test."
    snpwd() { sudo -n "$@"; }             # carry -n to every sudo call
fi

# Clean up
./cleanup

# Remove any leftover test3.invalid entry from a previous interrupted run
if grep -q "test3.invalid" "$HOSTS_FILE" 2>/dev/null; then
    echo "Removing leftover test3.invalid entry from $HOSTS_FILE..."
    TMP_HOSTS=$(mktemp)
    grep -v 'test3\.invalid' "$HOSTS_FILE" > "$TMP_HOSTS" \
        && snpwd cp -- "$TMP_HOSTS" "$HOSTS_FILE"
    rm -f "$TMP_HOSTS"
fi

# Backup /etc/hosts
printf 'Backing up %s → %s …\n' "$HOSTS_FILE" "$BACKUP_FILE"
cp -- "$HOSTS_FILE" "$BACKUP_FILE" ||
    { printf '%s\n' 'Lost sudo privileges; aborting.' >&2; exit 2; }

# Start ION nodes
echo "Starting ION..."
export ION_NODE_LIST_DIR=$PWD
rm -f ./ion_nodes
RETVAL=0

# Start nodes.
cd 2.bench.ltp
./ionstart
cd ../3.bench.ltp
./ionstart

# Setup receiver
echo ""
echo "Starting bpsink application..."
echo ""
cd ../3.bench.ltp
bpsink ipn:3.1 &
BPCOUNTER_PID=$!
sleep 1

# Step 1: Destination hostname not in DNS database or hosts file
echo ""
echo "Step 1: Destination host not in DNS database or hosts file."
echo ""

cd ../2.bench.ltp
sleep 10

# Check ion.log for DNS lookup report
LINE_NUM=$(search_log_after_line "$LOG_FILE" "$SEARCH_STRING1" "$LINE_NUM")
return_code=$?
if [ $return_code -eq 0 ]; then
    echo "SUCCESS: ion.log reported DSN lookup failure at line $LINE_NUM"
else
    echo "FAIL: No match or error (return code: -1)"
    RETVAL=1
fi

# Step 2: Destination hostname added back to DNS or hosts file
echo ""
echo "Step 2: Destination hostname added back to DNS or hosts file."
echo ""

# Append test3.invalid to hosts file
echo "Appending entry '$ENTRY' to $HOSTS_FILE..."
echo "$ENTRY" | snpwd tee -a "$HOSTS_FILE" >/dev/null

# Verify the entry was added
if grep -Fx "$ENTRY" "$HOSTS_FILE" >/dev/null; then
    echo "Entry '$ENTRY' added successfully"
else
    echo "Error: Failed to add entry '$ENTRY' to $HOSTS_FILE"
    exit 1
fi
echo "Waiting for ION to re-resolve hostname..."
sleep 65

# Check ion.log for DNS lookup report
#LINE_NUM=$(search_log_after_line "$LOG_FILE" "$SEARCH_STRING2" "$LINE_NUM")
#return_code=$?
#if [ $return_code -eq 0 ]; then
#    echo "SUCCESS: ion.log reported DNS lookup success at line $LINE_NUM"
#else
#    echo "FAIL: No match or error (return code: -1)"
#    RETVAL=1
#fi

# Step 3: Sending LTP data
echo ""
echo "Step 3: Sending LTP data"
echo ""

# Sending several messages
echo ""
echo "Sending messages to verify hostname is resolved..."
bpsource ipn:3.1 "Hello1!"
bpsource ipn:3.1 "Hello2!"
bpsource ipn:3.1 "Hello3!"
bpsource ipn:3.1 "Hello4!"
bpsource ipn:3.1 "Hello5!"
bpsource ipn:3.1 "Hello6!"
sleep 10

bpstats
sleep 3
echo ""
echo "Confirm the transmission..."
echo ""
cat ion.log | grep xmt

echo ""
echo "Now check the receiver to confirm reception..."
echo ""

cd ../3.bench.ltp
bpstats
sleep 3

cat ion.log | grep rcv

# Check ion.log for DNS lookup report
LINE_NUM=$(search_log_after_line "$LOG_FILE" "$SEARCH_STRING3" "$LINE_NUM")
return_code=$?
if [ $return_code -eq 0 ]; then
    echo "SUCCESS: ion.log confirm data reception at line: $LINE_NUM"
else
    echo "FAIL: No match or error (return code: -1)"
    RETVAL=1
fi


if [ "$RETVAL" -eq 1 ]
then
    echo "Test did not complete."
fi

echo -e "\nStopping ION..."
cd ..
# End-of-test cleanup removed so logs survive failures; runtests cleans on
# pass and the initial ./cleanup above gives a clean slate for manual runs.
# ./cleanup

exit $RETVAL
