#!/bin/sh

# Export ICUBE_RESOLVE_ID immediately so it's inherited by all child processes
# and can be verified by safe_kill_installer via /proc/<pid>/environ.
# This works regardless of how the script is invoked (direct, heredoc, pipe).
[ -n "$ICUBE_RESOLVE_ID" ] && export ICUBE_RESOLVE_ID

echo "${SCRIPT_ID}: [step] local_install_start"

# Initialize retry count environment variables
export DOWNLOAD_RETRY_COUNT=0
export EXTRACT_RETRY_COUNT=0
TRAE_TMP="${XDG_RUNTIME_DIR:-/tmp}"
# Print installation results and exit
print_install_results_and_exit() {
    echo "${SCRIPT_ID}: start"
    echo "exitCode==$1=="
    echo "listeningOn==$LISTENING_ON=="
    echo "connectionToken==$SERVER_CONNECTION_TOKEN=="
    echo "logFile==$SERVER_LOGFILE=="
    echo "osReleaseId==$OS_RELEASE_ID=="
    echo "arch==$ARCH=="
    echo "platform==$PLATFORM=="
    echo "tmpDir==$TRAE_TMP=="
    echo "regionDetectJson==$TRAE_REGION_DETECT_JSON=="
    echo "downloadRetryCount==$DOWNLOAD_RETRY_COUNT=="
    echo "extractRetryCount==$EXTRACT_RETRY_COUNT=="
    echo "${SCRIPT_ID}: end"
    exit $1
}

# Create install directory
if [ ! -d "$SERVER_DIR" ]; then
    mkdir -p "$SERVER_DIR"
    if [ $? -gt 0 ]; then
        echo "Error creating server install directory"
        print_install_results_and_exit 1001
    fi
fi
echo "${SCRIPT_ID}: [step] server_dir_created"

# Skip region detection (not needed for local install)
export TRAE_DETECT_REGION="LOCAL"
echo "${SCRIPT_ID}: [step] skip_region_check"

# Set local version (skip remote version fetching)
REMOTE_VERSION="${LOCAL_PACKAGE_VERSION}"
LOCAL_VERSION_FILE="$SERVER_DIR/version"
echo "${SCRIPT_ID}: [step] local_version_set"

# Set package name and compression format
if [ "$ICUBE_REMOTE_USE_XZ_COMPRESSION" = "true" ]; then
    COMPRESSION_EXT="tar.xz"
else
    COMPRESSION_EXT="tar.gz"
fi
SERVER_PACKAGE_NAME="${LOCAL_PACKAGE_NAME}"

# Create manager directory
if [ -z "$MANAGER_DATA_DIR" ]; then
    MANAGER_DATA_DIR="${SERVER_DATA_DIR}"
fi
if [ ! -d "$MANAGER_DATA_DIR" ]; then
    mkdir -p "$MANAGER_DATA_DIR"
    if [ $? -gt 0 ]; then
        echo "Error creating MANAGER_DATA_DIR directory"
        print_install_results_and_exit 1002
    fi
fi

# Check whether reinstall is needed
# REMOTE_VERSION was pre-fetched and validated by CheckServerRequirementStep
NEED_INSTALL=true
if [ -f "$SERVER_SCRIPT" ]; then
    if [ -f "$LOCAL_VERSION_FILE" ]; then
        LOCAL_VERSION=$(cat "$LOCAL_VERSION_FILE" 2>/dev/null)
        if [ -n "$REMOTE_VERSION" ] && [ -n "$LOCAL_VERSION" ] && [ "$LOCAL_VERSION" = "$REMOTE_VERSION" ]; then
            NEED_INSTALL=false
            echo "Server already installed with correct version: $LOCAL_VERSION"
        else
            echo "Version mismatch, will reinstall: local='$LOCAL_VERSION', expected='$REMOTE_VERSION'"
            rm -rf "$SERVER_DIR"/*
        fi
    else
        echo "Version file missing, will reinstall"
        rm -rf "$SERVER_DIR"/*
    fi
else
    echo "Server script not found, will install"
fi

# Local installation logic
if [ "$NEED_INSTALL" = true ]; then
    echo "${SCRIPT_ID}: [step] local_install_begin"
    cd "$SERVER_DIR" > /dev/null
    OLDPWD_SAVED=$PWD

    # Copy local cached package
    LOCAL_PACKAGE_PATH="${LOCAL_CACHED_PACKAGE_PATH}"
    TAR_NAME="vscode-server.${COMPRESSION_EXT}"

    echo "Debug: LOCAL_CACHED_PACKAGE_PATH = $LOCAL_CACHED_PACKAGE_PATH"
    echo "Debug: SERVER_DIR = $SERVER_DIR"
    echo "Debug: TAR_NAME = $TAR_NAME"
    echo "Debug: Current working directory = $(pwd)"

    if [ ! -f "$LOCAL_PACKAGE_PATH" ]; then
        echo "Error: Local package not found: $LOCAL_PACKAGE_PATH"
        echo "Debug: Listing $TRAE_TMP directory:"
        ls -la $TRAE_TMP/ | grep -E "\\.tar\\.(gz|xz)$" || echo "No tar files found in $TRAE_TMP"
        print_install_results_and_exit 2001
    fi

    # Copy file
    echo "Copying local package: $LOCAL_PACKAGE_PATH -> $TAR_NAME"
    rm -rf "$TAR_NAME"
    mv "$LOCAL_PACKAGE_PATH" "$TAR_NAME"
    if [ $? -ne 0 ]; then
        echo "Error: Failed to copy local package"
        print_install_results_and_exit 2001
    fi

    # Verify copy result
    if [ -f "$TAR_NAME" ]; then
        echo "Package copied successfully"
        ls -la "$TAR_NAME"
    else
        echo "Error: Package file not found after copy"
        print_install_results_and_exit 2001
    fi

    echo "${SCRIPT_ID}: [step] local_package_copied"

    # Extraction process
    extract_retry_count=0
    max_extract_retries=3
    extract_success=false

    echo "${SCRIPT_ID}: [step] local_package_extraction_begin"
    while [ $extract_retry_count -lt $max_extract_retries ]; do
        echo "Extracting server from $TAR_NAME..."
        if [ "$ICUBE_REMOTE_USE_XZ_COMPRESSION" = "true" ] && case "$TAR_NAME" in *.xz) true;; *) false;; esac; then
            tar -xJf "$TAR_NAME" --strip-components=1
        else
            tar -xzf "$TAR_NAME" --strip-components=1
        fi

        if [ $? -eq 0 ]; then
            extract_success=true
            break
        fi

        extract_retry_count=$((extract_retry_count+1))
        export EXTRACT_RETRY_COUNT=$extract_retry_count
        echo "Extraction failed, retry attempt $extract_retry_count..."
        sleep 1
    done

    if [ "$extract_success" != "true" ]; then
        echo "Error: Failed to extract server contents after $extract_retry_count attempts"
        print_install_results_and_exit 2002
    fi
    echo "${SCRIPT_ID}: [step] local_package_extracted"

    # Verify key files exist
    if [ -f "$SERVER_SCRIPT" ]; then
        echo "✓ Server script extracted successfully: $SERVER_SCRIPT"
    else
        echo "✗ Server script not found after extraction: $SERVER_SCRIPT"
        echo "Directory contents:"
        ls -la "$SERVER_DIR/" | head -10
        print_install_results_and_exit 2003
    fi

    if [ -f "$SERVER_DIR/node" ]; then
        echo "✓ Node binary extracted successfully"
    else
        echo "✗ Node binary not found after extraction: $SERVER_DIR/node"
        print_install_results_and_exit 2004
    fi

    # Write version info
    if [ -n "$REMOTE_VERSION" ]; then
        echo "$REMOTE_VERSION" > "$LOCAL_VERSION_FILE"
        echo "Version file written: $REMOTE_VERSION"
    fi

    # Clean temporary files
    rm -f "$TAR_NAME"
    cd "$OLDPWD_SAVED" > /dev/null

    echo "${SCRIPT_ID}: [step] local_install_completed"
else
    echo "${SCRIPT_ID}: [step] local_install_skipped"
fi

# --- Server startup lock mechanism ---
# Prevents race conditions when multiple IDE windows try to start the server simultaneously.
# Uses mkdir-based lock (atomic on all POSIX filesystems).
# Lock is scoped by BOTH commitId and machineId so that different versions can install concurrently.
COMMIT_SUFFIX_FOR_LOCK="${DISTRO_COMMIT:+-${DISTRO_COMMIT%${DISTRO_COMMIT#????????}}}"
MACHINE_ID_SUFFIX_FOR_LOCK="${ICUBE_MACHINE_ID:+-${ICUBE_MACHINE_ID%${ICUBE_MACHINE_ID#????????}}}"
SERVER_LOCK_DIR="$SERVER_DATA_DIR/.resolve-lock${COMMIT_SUFFIX_FOR_LOCK}${MACHINE_ID_SUFFIX_FOR_LOCK}"
SERVER_LOCK_TIMEOUT=1800
SERVER_LOCK_HEARTBEAT_STALE=30
SERVER_LOCK_ACQUIRED=false
SERVER_LOCK_HEARTBEAT_PID=""

# ============================================================
# Safely kill a process only if it's our installer (verified via ICUBE_RESOLVE_ID
# environment variable). Prevents killing an unrelated process if the PID was
# recycled, and correctly identifies installers executed via heredoc/pipe
# (e.g. 'curl | bash' or 'bash -s -- < <(EOF)') whose cmdline doesn't contain
# the script name.
# ============================================================
safe_kill_installer() {
    local target_pid="$1"
    [ -z "$target_pid" ] && return 1
    [[ ! "$target_pid" =~ ^[0-9]+$ ]] && return 1

    # First check: if PID is already dead, return success — the process is gone,
    # so the lock is definitely stale and we can safely delete it.
    if ! kill -0 "$target_pid" 2>/dev/null; then
        return 0
    fi

    # Second check: ICUBE_RESOLVE_ID in /proc/<pid>/environ must match.
    # This works for all execution modes (direct script, heredoc, pipe)
    # because the environment is inherited regardless of how bash is invoked.
    if [ -n "$ICUBE_RESOLVE_ID" ]; then
        local env_match
        env_match=$(cat "/proc/$target_pid/environ" 2>/dev/null | tr '\0' '\n' | grep "^ICUBE_RESOLVE_ID=$ICUBE_RESOLVE_ID$")
        if [ -n "$env_match" ]; then
            kill "$target_pid" 2>/dev/null
            return 0
        fi
    fi
    return 1
}

# Check if the server is already running with a valid port.
# Returns 0 (true) if ready, 1 (false) if not.
is_server_running_with_port() {
    if [ -f "$SERVER_PIDFILE" ] && [ -f "$SERVER_TOKENFILE" ] && [ -f "$SERVER_LOGFILE" ]; then
        local check_pid=$(cat "$SERVER_PIDFILE" 2>/dev/null)
        if [ -n "$check_pid" ] && kill -0 "$check_pid" 2>/dev/null; then
            local check_port=$(grep -E 'Extension host agent listening on .+' "$SERVER_LOGFILE" 2>/dev/null | tail -1 | sed 's/.*Extension host agent listening on //' | sed 's/.*://' | tr -d '[:space:]')
            if [ -n "$check_port" ]; then
                echo "Server running (pid=$check_pid, port=$check_port)"
                return 0
            fi
        fi
    fi
    return 1
}

acquire_server_lock() {
    local start_time=$(date +%s)

    # ============================================================
    # Pre-check: If the lock already exists and is our own stale lock
    # from a previous attempt in the same resolve session.
    # Break it immediately without waiting for the loop.
    # We must verify via /proc/<pid>/environ that the process is
    # truly our installer before killing, to handle edge cases
    # where the script is executed via heredoc/pipe and cmdline
    # doesn't contain the script name.
    # ============================================================
    if [ -d "$SERVER_LOCK_DIR" ]; then
        if [ -n "$ICUBE_RESOLVE_ID" ] && [ -f "$SERVER_LOCK_DIR/resolveId" ]; then
            local pre_resolve_id=$(cat "$SERVER_LOCK_DIR/resolveId" 2>/dev/null)
            if [ "$pre_resolve_id" = "$ICUBE_RESOLVE_ID" ]; then
                echo "Pre-check: Lock is our own stale lock (resolve ID $ICUBE_RESOLVE_ID)"
                local killed=false
                if [ -f "$SERVER_LOCK_DIR/pid" ]; then
                    local pre_pid=$(cat "$SERVER_LOCK_DIR/pid" 2>/dev/null)
                    if safe_kill_installer "$pre_pid"; then
                        echo "Successfully killed stale installer process $pre_pid"
                        killed=true
                    else
                        echo "Could not verify stale installer process $pre_pid (may be running via heredoc/pipe without env var), will let heartbeat/timeout handle it"
                    fi
                fi
                if [ "$killed" = true ] || [ ! -f "$SERVER_LOCK_DIR/pid" ]; then
                    rm -rf "$SERVER_LOCK_DIR"
                fi
            fi
        fi
    fi

    # ============================================================
    # Clean up all stale lock directories in SERVER_DATA_DIR.
    # Skip locks that are still actively held (heartbeat fresh or PID alive).
    # ============================================================
    for lock_dir in "$SERVER_DATA_DIR"/.resolve-lock-*; do
        [ -d "$lock_dir" ] || continue
        [ "$lock_dir" = "$SERVER_LOCK_DIR" ] && continue

        # Skip if heartbeat is fresh (holder is still working)
        if [ -f "$lock_dir/heartbeat" ]; then
            local hb_time=$(cat "$lock_dir/heartbeat" 2>/dev/null)
            if [ -n "$hb_time" ]; then
                local now=$(date +%s)
                if [ $((now - hb_time)) -le $SERVER_LOCK_HEARTBEAT_STALE ]; then
                    continue
                fi
            fi
        fi

        # Skip if PID is still alive
        if [ -f "$lock_dir/pid" ]; then
            local lock_pid=$(cat "$lock_dir/pid" 2>/dev/null)
            if [ -n "$lock_pid" ] && kill -0 "$lock_pid" 2>/dev/null; then
                continue
            fi
        fi

        # Stale lock — clean it up
        echo "Cleaning up stale lock directory: $lock_dir"
        rm -rf "$lock_dir"
    done

    while ! mkdir "$SERVER_LOCK_DIR" 2>/dev/null; do
        # ============================================================
        # Priority 1: Check if server is already running
        # (other worker already finished the job)
        # ============================================================
        if is_server_running_with_port; then
            echo "Server started by another session while waiting for lock"
            return 1
        fi

        # ============================================================
        # Priority 2: Check if lock holder is definitely dead
        # ============================================================
        if [ -f "$SERVER_LOCK_DIR/pid" ]; then
            local lock_pid=$(cat "$SERVER_LOCK_DIR/pid" 2>/dev/null)
            if [ -n "$lock_pid" ] && ! kill -0 "$lock_pid" 2>/dev/null; then
                echo "Lock held by dead process $lock_pid, breaking lock"
                rm -rf "$SERVER_LOCK_DIR"
                continue
            fi

            # ============================================================
            # Priority 2.5: Heartbeat stale — lock holder's shell died but
            # PID was recycled. The heartbeat file stops being updated when
            # the background writer is killed (SSH disconnect kills shell).
            # ============================================================
            if [ -f "$SERVER_LOCK_DIR/heartbeat" ]; then
                local hb_time=""
                hb_time=$(cat "$SERVER_LOCK_DIR/heartbeat" 2>/dev/null)
                if [ -n "$hb_time" ]; then
                    local current_time_hb=$(date +%s)
                    if [ $((current_time_hb - hb_time)) -gt $SERVER_LOCK_HEARTBEAT_STALE ]; then
                        echo "Lock holder (pid $lock_pid) heartbeat stale (>${SERVER_LOCK_HEARTBEAT_STALE}s), breaking lock"
                        rm -rf "$SERVER_LOCK_DIR"
                        continue
                    fi
                fi
            fi

            # ============================================================
            # Priority 2.6: Same resolve ID — this is our own stale lock
            # from a previous attempt in the same resolve session. The same
            # resolve session cannot have two install scripts running.
            # We must verify via /proc/<pid>/environ that the process is
            # truly our installer before killing, to handle edge cases
            # where the script is executed via heredoc/pipe and cmdline
            # doesn't contain the script name.
            # ============================================================
            if [ -n "$ICUBE_RESOLVE_ID" ] && [ -f "$SERVER_LOCK_DIR/resolveId" ]; then
                local lock_resolve_id=$(cat "$SERVER_LOCK_DIR/resolveId" 2>/dev/null)
                if [ "$lock_resolve_id" = "$ICUBE_RESOLVE_ID" ]; then
                    echo "Lock held by same resolve ID ($ICUBE_RESOLVE_ID), breaking stale lock from previous attempt"
                    if safe_kill_installer "$lock_pid"; then
                        echo "Successfully killed stale installer process $lock_pid"
                        rm -rf "$SERVER_LOCK_DIR"
                        continue
                    else
                        echo "Could not verify stale installer process $lock_pid (may be running via heredoc/pipe without env var), will continue waiting"
                    fi
                fi
            fi

            # ============================================================
            # Priority 3: PID appears alive — but could be recycled.
            # Only break lock if:
            #   (a) lock is old AND server is running (holder finished), OR
            #   (b) lock is old AND no server (holder is stuck/dead with recycled PID)
            # Do NOT break lock just because of timeout if holder is still working.
            # ============================================================
            local lock_time_pid=""
            if stat -c %Y "$SERVER_LOCK_DIR" >/dev/null 2>&1; then
                lock_time_pid=$(stat -c %Y "$SERVER_LOCK_DIR")
            elif stat -f %m "$SERVER_LOCK_DIR" >/dev/null 2>&1; then
                lock_time_pid=$(stat -f %m "$SERVER_LOCK_DIR")
            fi
            if [ -n "$lock_time_pid" ]; then
                local current_time_pid=$(date +%s)
                if [ $((current_time_pid - lock_time_pid)) -gt $SERVER_LOCK_TIMEOUT ]; then
                    # Case (a): server running — holder finished, safe to break
                    if is_server_running_with_port; then
                        echo "Lock with pid $lock_pid stale (age > ${SERVER_LOCK_TIMEOUT}s) and server running, breaking it"
                        rm -rf "$SERVER_LOCK_DIR"
                        continue
                    fi
                    # Case (b): no server after timeout — holder likely dead with recycled PID
                    echo "Lock with pid $lock_pid appears stale (age > ${SERVER_LOCK_TIMEOUT}s, pid likely recycled), breaking it"
                    rm -rf "$SERVER_LOCK_DIR"
                    continue
                fi
            fi
        else
            # Lock dir exists but no pid file — check resolveId first, then stale by age
            if [ -n "$ICUBE_RESOLVE_ID" ] && [ -f "$SERVER_LOCK_DIR/resolveId" ]; then
                local lock_resolve_id_nopid=$(cat "$SERVER_LOCK_DIR/resolveId" 2>/dev/null)
                if [ "$lock_resolve_id_nopid" = "$ICUBE_RESOLVE_ID" ]; then
                    echo "Lock (no pid) held by same resolve ID ($ICUBE_RESOLVE_ID), breaking stale lock"
                    rm -rf "$SERVER_LOCK_DIR"
                    continue
                fi
            fi
            local lock_time=""
            if stat -c %Y "$SERVER_LOCK_DIR" >/dev/null 2>&1; then
                lock_time=$(stat -c %Y "$SERVER_LOCK_DIR")
            elif stat -f %m "$SERVER_LOCK_DIR" >/dev/null 2>&1; then
                lock_time=$(stat -f %m "$SERVER_LOCK_DIR")
            fi
            if [ -n "$lock_time" ]; then
                local current_time=$(date +%s)
                if [ $((current_time - lock_time)) -gt $SERVER_LOCK_TIMEOUT ]; then
                    echo "Lock without pid appears stale, breaking it"
                    rm -rf "$SERVER_LOCK_DIR"
                    continue
                fi
            fi
        fi

        local elapsed=$(($(date +%s) - start_time))
        if [ $elapsed -gt $SERVER_LOCK_TIMEOUT ]; then
            echo "Warning: Failed to acquire server lock after ${SERVER_LOCK_TIMEOUT}s, proceeding anyway"
            return 1
        fi
        echo "Another resolve session is starting the server, waiting... (${elapsed}s)"
        sleep 2
        # Do NOT check is_server_running_with_port here — it only verifies PID+port in log
        # which can be stale (e.g. "Last EH closed, shutting down"). Let the loop re-attempt
        # mkdir; once lock is free, the caller goes through the full health validation path.
    done
    echo "$ICUBE_RESOLVE_ID" > "$SERVER_LOCK_DIR/resolveId"
    echo $$ > "$SERVER_LOCK_DIR/pid"
    date +%s > "$SERVER_LOCK_DIR/heartbeat"
    (while [ -d "$SERVER_LOCK_DIR" ] && kill -0 $$ 2>/dev/null; do date +%s > "$SERVER_LOCK_DIR/heartbeat" 2>/dev/null; sleep 5; done) &
    SERVER_LOCK_HEARTBEAT_PID=$!
    SERVER_LOCK_ACQUIRED=true
    return 0
}

release_server_lock() {
    if [ "$SERVER_LOCK_ACQUIRED" = true ]; then
        if [ -n "$SERVER_LOCK_HEARTBEAT_PID" ]; then
            kill "$SERVER_LOCK_HEARTBEAT_PID" 2>/dev/null
            wait "$SERVER_LOCK_HEARTBEAT_PID" 2>/dev/null
            SERVER_LOCK_HEARTBEAT_PID=""
        fi
        rm -rf "$SERVER_LOCK_DIR"
        SERVER_LOCK_ACQUIRED=false
    fi
}

# --- Quick pre-lock check ---
# If the server is already running with a valid port and token, return immediately
# without acquiring the lock. This prevents unnecessary blocking when another window
# has already started the server (common case: two windows to same host).
if [ -f "$SERVER_PIDFILE" ] && [ -f "$SERVER_TOKENFILE" ] && [ -f "$SERVER_LOGFILE" ]; then
    QUICK_PID="$(cat "$SERVER_PIDFILE" 2>/dev/null)"
    if [ -n "$QUICK_PID" ] && kill -0 "$QUICK_PID" 2>/dev/null; then
        QUICK_PORT="$(grep -E 'Extension host agent listening on .+' "$SERVER_LOGFILE" | tail -1 | sed 's/.*Extension host agent listening on //' | sed 's/.*://' | tr -d '[:space:]')"
        QUICK_TOKEN="$(cat "$SERVER_TOKENFILE" 2>/dev/null)"
        if [ -n "$QUICK_PORT" ] && [ -n "$QUICK_TOKEN" ]; then
            QUICK_MATCH=true
            if [ -n "$ICUBE_MACHINE_ID" ] && [ -d "/proc" ]; then
                if ! cat "/proc/$QUICK_PID/environ" 2>/dev/null | tr '\0' '\n' | grep -q "^ICUBE_MACHINE_ID=$ICUBE_MACHINE_ID$"; then
                    QUICK_MATCH=false
                fi
            fi
            if [ "$QUICK_MATCH" = "true" ]; then
                echo "Server already running (pid=$QUICK_PID, port=$QUICK_PORT). Skipping lock."
                LISTENING_ON="$QUICK_PORT"
                SERVER_CONNECTION_TOKEN="$QUICK_TOKEN"
                print_install_results_and_exit 0
            fi
        fi
    fi
fi

# Acquire lock before checking/starting server
if ! acquire_server_lock; then
    # Lock not acquired (timeout or server became ready while waiting).
    # If server is already running with valid port, proceed to read its info.
    if [ -f "$SERVER_PIDFILE" ] && [ -f "$SERVER_TOKENFILE" ] && [ -f "$SERVER_LOGFILE" ]; then
        SKIP_PID="$(cat "$SERVER_PIDFILE" 2>/dev/null)"
        if [ -n "$SKIP_PID" ] && kill -0 "$SKIP_PID" 2>/dev/null; then
            LISTENING_ON="$(grep -E 'Extension host agent listening on .+' "$SERVER_LOGFILE" 2>/dev/null | tail -1 | sed 's/.*Extension host agent listening on //' | sed 's/.*://' | tr -d '[:space:]')"
            SERVER_CONNECTION_TOKEN="$(cat "$SERVER_TOKENFILE" 2>/dev/null)"
            if [ -n "$LISTENING_ON" ] && [ -n "$SERVER_CONNECTION_TOKEN" ]; then
                echo "Server ready (started by another session). pid=$SKIP_PID, port=$LISTENING_ON"
                print_install_results_and_exit 0
            fi
        fi
    fi
    echo "Warning: Lock not acquired and server not ready, proceeding without lock"
fi

# Check if server is already running
if [ -f "$SERVER_PIDFILE" ]; then
    SERVER_PID="$(cat "$SERVER_PIDFILE")"
    SERVER_RUNNING_PROCESS="$(ps -o pid,args -p "$SERVER_PID" 2>/dev/null | grep "$SERVER_SCRIPT")"
else
    SERVER_RUNNING_PROCESS="$(ps -o pid,args -A 2>/dev/null | grep "$SERVER_SCRIPT" | grep -v grep)"
fi

# If server process is found, check for zombie/shutdown state.
# The server with --remote-auto-shutdown-without-delay may still have the process alive
# and port open, but no longer processes new connections after "Last EH closed, shutting down".
if [ -n "$SERVER_RUNNING_PROCESS" ] && [ -f "$SERVER_LOGFILE" ]; then
    if grep -q "Last EH closed, shutting down" "$SERVER_LOGFILE" 2>/dev/null; then
        LAST_LISTEN_LINE=$(grep -n "Extension host agent listening on" "$SERVER_LOGFILE" | tail -1 | cut -d: -f1)
        LAST_SHUTDOWN_LINE=$(grep -n "Last EH closed, shutting down" "$SERVER_LOGFILE" | tail -1 | cut -d: -f1)
        if [ -n "$LAST_LISTEN_LINE" ] && [ -n "$LAST_SHUTDOWN_LINE" ] && [ "$LAST_SHUTDOWN_LINE" -gt "$LAST_LISTEN_LINE" ]; then
            echo "Warning: Server is in zombie state (shutdown after listening). Killing and restarting."
            SERVER_PID_TO_KILL="${SERVER_PID:-$(echo "$SERVER_RUNNING_PROCESS" | awk '{print $1}')}"
            if [ -n "$SERVER_PID_TO_KILL" ]; then
                kill "$SERVER_PID_TO_KILL" 2>/dev/null
                sleep 1
                if ps -p "$SERVER_PID_TO_KILL" >/dev/null 2>&1; then
                    kill -9 "$SERVER_PID_TO_KILL" 2>/dev/null
                fi
            fi
            SERVER_RUNNING_PROCESS=""
        fi
    fi
fi

# If server process is alive, verify health via /version endpoint.
if [ -n "$SERVER_RUNNING_PROCESS" ] && [ -f "$SERVER_TOKENFILE" ] && [ -f "$SERVER_LOGFILE" ]; then
    OLD_LISTENING_ON="$(cat "$SERVER_LOGFILE" | grep -E 'Extension host agent listening on .+' | tail -1 | sed 's/.*Extension host agent listening on //' | tr -d '[:space:]')"
    # Extract host and port from the log line.
    # Formats: "[::1]:PORT", "HOST:PORT", or just "PORT"
    OLD_LISTENING_HOST=""
    OLD_LISTENING_PORT=""
    case "$OLD_LISTENING_ON" in
        \[*\]:*)
            # IPv6 bracketed: [::1]:PORT
            OLD_LISTENING_HOST="$(echo "$OLD_LISTENING_ON" | sed 's/^\[\([^]]*\)\].*/\1/')"
            OLD_LISTENING_PORT="$(echo "$OLD_LISTENING_ON" | sed 's/.*\]://')"
            ;;
        *:*)
            # HOST:PORT (IPv4 or hostname)
            OLD_LISTENING_HOST="$(echo "$OLD_LISTENING_ON" | sed 's/:[^:]*$//')"
            OLD_LISTENING_PORT="$(echo "$OLD_LISTENING_ON" | sed 's/.*://')"
            ;;
        *)
            # Just PORT
            OLD_LISTENING_PORT="$OLD_LISTENING_ON"
            ;;
    esac

    if [ -n "$OLD_LISTENING_PORT" ]; then
        # Derive connectable host from log, fallback to TRAE_SERVER_HOST or 127.0.0.1
        if [ -n "$OLD_LISTENING_HOST" ] && [ "$OLD_LISTENING_HOST" != "0.0.0.0" ]; then
            SERVER_HOST_CHECK="$OLD_LISTENING_HOST"
        else
            SERVER_HOST_CHECK="${TRAE_SERVER_HOST:-127.0.0.1}"
        fi
        # IPv6 addresses need brackets in URLs
        case "$SERVER_HOST_CHECK" in
            *:*) SERVER_HOST_CHECK="[${SERVER_HOST_CHECK}]" ;;
        esac
        # Health check via /version endpoint (no auth needed), also validates commitId.
        VERSION_CHECK_URL="http://${SERVER_HOST_CHECK}:${OLD_LISTENING_PORT}/version"
        SERVER_REPORTED_VERSION=""
        if command -v curl >/dev/null 2>&1; then
            SERVER_REPORTED_VERSION=$(curl -sf --connect-timeout 3 "$VERSION_CHECK_URL" 2>/dev/null)
        elif command -v wget >/dev/null 2>&1; then
            SERVER_REPORTED_VERSION=$(wget -qO- --timeout=3 "$VERSION_CHECK_URL" 2>/dev/null)
        fi
        SERVER_REPORTED_VERSION=$(echo "$SERVER_REPORTED_VERSION" | tr -d '[:space:]')

        SHOULD_RESTART=false
        if [ -z "$SERVER_REPORTED_VERSION" ]; then
            echo "Warning: Server not responding to /version health check. Restarting server."
            SHOULD_RESTART=true
        fi

        if [ "$SHOULD_RESTART" = "true" ]; then
            SERVER_PID_TO_KILL="${SERVER_PID:-$(echo "$SERVER_RUNNING_PROCESS" | awk '{print $1}')}"
            if [ -n "$SERVER_PID_TO_KILL" ]; then
                kill "$SERVER_PID_TO_KILL" 2>/dev/null
                sleep 1
                if ps -p "$SERVER_PID_TO_KILL" >/dev/null 2>&1; then
                    kill -9 "$SERVER_PID_TO_KILL" 2>/dev/null
                fi
            fi
            rm -f "$SERVER_LOGFILE" "$SERVER_TOKENFILE" "$SERVER_PIDFILE"
            SERVER_RUNNING_PROCESS=""
        fi
    fi
fi

TOKEN_FILE_EXISTS=false
if [ -f "$SERVER_TOKENFILE" ]; then
    TOKEN_FILE_EXISTS=true
fi

# If server is running but token file missing, try to recover token first
if [ -n "$SERVER_RUNNING_PROCESS" ] && [ "$TOKEN_FILE_EXISTS" != true ]; then
    RECOVERED_TOKEN=""
    RECOVER_PID="${SERVER_PID:-$(echo "$SERVER_RUNNING_PROCESS" | awk '{print $1}')}"
    if [ -n "$RECOVER_PID" ] && [ -f "/proc/$RECOVER_PID/cmdline" ]; then
        # First try --connection-token-file (our default), then --connection-token (inline)
        TOKEN_FILE_PATH=$(tr '\0' '\n' < "/proc/$RECOVER_PID/cmdline" 2>/dev/null | grep -A1 '^--connection-token-file$' | tail -1)
        if [ -n "$TOKEN_FILE_PATH" ] && [ -f "$TOKEN_FILE_PATH" ]; then
            RECOVERED_TOKEN=$(cat "$TOKEN_FILE_PATH" 2>/dev/null)
        fi
        if [ -z "$RECOVERED_TOKEN" ]; then
            RECOVERED_TOKEN=$(tr '\0' '\n' < "/proc/$RECOVER_PID/cmdline" 2>/dev/null | grep -A1 '^--connection-token$' | tail -1)
        fi
    fi

    if [ -n "$RECOVERED_TOKEN" ]; then
        echo "Recovered connection token from running server process, recreating token file."
        echo "$RECOVERED_TOKEN" > "$SERVER_TOKENFILE"
        TOKEN_FILE_EXISTS=true
    else
        echo "Warning: Server is running but token file is missing and cannot recover token. Stopping server."
        KILL_PID="${SERVER_PID:-$(echo "$SERVER_RUNNING_PROCESS" | awk '{print $1}')}"
        if [ -n "$KILL_PID" ]; then
            kill "$KILL_PID" 2>/dev/null
        fi
        i=1
        while [ $i -le 10 ]; do
            if [ -z "$(ps -o pid= -p "$KILL_PID" 2>/dev/null)" ]; then
                break
            fi
            sleep 0.5
            i=$((i + 1))
        done
        if [ -n "$(ps -o pid= -p "$KILL_PID" 2>/dev/null)" ]; then
            echo "Warning: Server did not stop gracefully, forcing termination."
            kill -9 "$KILL_PID" 2>/dev/null
        fi
        SERVER_RUNNING_PROCESS=""
    fi
fi

# Start server (if not running)
if [ -z "$SERVER_RUNNING_PROCESS" ]; then
    echo "${SCRIPT_ID}: [step] starting_server"

    # Clean old files and create new token
    rm -f "$SERVER_LOGFILE" "$SERVER_TOKENFILE"
    touch "$SERVER_TOKENFILE"
    chmod 600 "$SERVER_TOKENFILE"
    if [ -f "/proc/sys/kernel/random/uuid" ]; then
        SERVER_CONNECTION_TOKEN="$(cat /proc/sys/kernel/random/uuid)"
    else
        SERVER_CONNECTION_TOKEN="$(uuidgen 2>/dev/null || date +%s%N)"
    fi
    echo "$SERVER_CONNECTION_TOKEN" > "$SERVER_TOKENFILE"
    if [ $? -ne 0 ] || [ ! -s "$SERVER_TOKENFILE" ]; then
        echo "Error: Failed to write connection token to file"
        release_server_lock
        print_install_results_and_exit 1005
    fi

    NODE_DNS_RESULT_ORDER=""
    if [ "$ICUBE_REMOTE_AIPREFER_IPV6" = "true" ]; then
        NODE_DNS_RESULT_ORDER="--dns-result-order=ipv6first"
    fi

    # Verify server files exist
    echo "Debug: Checking server files before startup:"
    echo "  SERVER_SCRIPT = $SERVER_SCRIPT"
    if [ -f "$SERVER_SCRIPT" ]; then
        echo "  ✓ Server script found"
    else
        echo "  ✗ Server script NOT found"
        ls -la "$SERVER_DIR/" | head -10
        release_server_lock
        print_install_results_and_exit 1006
    fi

    echo "  SERVER_DIR/node = $SERVER_DIR/node"
    if [ -f "$SERVER_DIR/node" ]; then
        echo "  ✓ Node binary found"
    else
        echo "  ✗ Node binary NOT found"
        release_server_lock
        print_install_results_and_exit 1007
    fi

    if [ -f "$SERVER_DATA_DIR/pre-server.sh" ]; then
        echo "Sourcing pre-server.sh..."
        source "$SERVER_DATA_DIR/pre-server.sh"
    fi

    SERVER_HOST="${TRAE_SERVER_HOST:-127.0.0.1}"
    SHUTDOWN_EXTRA=""
    if [ "$TRAE_SHUTDOWN_WITHOUT_DELAY" = "true" ]; then
        SHUTDOWN_EXTRA="--remote-auto-shutdown-without-delay"
    fi

    echo "Starting server with command:"
    echo "ICUBE_RUN_IN_REMOTE='true' vscode_base_dir=\"$SERVER_DATA_DIR\" \"$SERVER_DIR/node\" $NODE_DNS_RESULT_ORDER \"$SERVER_SCRIPT\" --start-server --host=$SERVER_HOST $SERVER_LISTEN_FLAG $SERVER_INITIAL_EXTENSIONS --connection-token-file \"$SERVER_TOKENFILE\" --telemetry-level all --enable-remote-auto-shutdown $SHUTDOWN_EXTRA --accept-server-license-terms --default-folder ~ --server-data-dir \"$MANAGER_DATA_DIR\" --logsPath \"$MANAGER_LOGS_DIR\" > \"$SERVER_LOGFILE\" 2>&1"

    ICUBE_RUN_IN_REMOTE='true' vscode_base_dir="$SERVER_DATA_DIR" "$SERVER_DIR/node" $NODE_DNS_RESULT_ORDER "$SERVER_SCRIPT" --start-server --host=$SERVER_HOST $SERVER_LISTEN_FLAG $SERVER_INITIAL_EXTENSIONS --connection-token-file "$SERVER_TOKENFILE" --telemetry-level all --enable-remote-auto-shutdown $SHUTDOWN_EXTRA --accept-server-license-terms --default-folder ~ --server-data-dir "$MANAGER_DATA_DIR" --logsPath "$MANAGER_LOGS_DIR" > "$SERVER_LOGFILE" 2>&1 &
    SERVER_PID=$!
    echo $SERVER_PID > "$SERVER_PIDFILE"
    echo "Server started with PID: $SERVER_PID"
fi

# Get token
if [ -f "$SERVER_TOKENFILE" ]; then
    SERVER_CONNECTION_TOKEN="$(cat "$SERVER_TOKENFILE")"
else
    echo "Error server token file not found $SERVER_TOKENFILE"
    release_server_lock
    print_install_results_and_exit 1004
fi

# Release lock now - the critical section (check/start server + write PID/token) is done.
# The port-wait loop below doesn't need mutual exclusion; other windows can safely
# acquire the lock, see the PID file, and also wait for the port independently.
release_server_lock

echo "${SCRIPT_ID}: [step] waiting_for_server_startup"

# Wait for server startup
i=1
while [ $i -le 20 ]; do
    if [ -f "$SERVER_LOGFILE" ]; then
        if cat "$SERVER_LOGFILE" | grep -q "Current trae appVersion is too old"; then
            echo "ERROR: Current trae appVersion is too old"
            print_install_results_and_exit 3003
        fi

        LISTENING_ON="$(cat "$SERVER_LOGFILE" | grep -E 'Extension host agent listening on .+' | sed 's/Extension host agent listening on //')"
        if [ -n "$LISTENING_ON" ]; then
            if [ -f "/tmp/trae-region-detect.json" ]; then
                TRAE_REGION_DETECT_JSON="$(cat /tmp/trae-region-detect.json)"
            fi
            break
        fi
    fi
    sleep 0.5
    i=$((i + 1))
done

if ! [ -f "$SERVER_LOGFILE" ]; then
    echo "Error server log file not found $SERVER_LOGFILE"
    print_install_results_and_exit 1003
fi

if [ -z "$LISTENING_ON" ]; then
    echo "Error server did not start successfully"
    if [ -f "$SERVER_LOGFILE" ]; then
        tail -n 10 "$SERVER_LOGFILE"
    fi
    print_install_results_and_exit 3001
fi

echo "${SCRIPT_ID}: [step] server_startup_completed"

# Complete installation and startup
print_install_results_and_exit 0
