#!/usr/bin/env bash
set -euo pipefail

# ── Constants ──────────────────────────────────────────────────────────────────
INSTALL_DIR=/opt/bridgelink-webadmin
SERVICE_NAME=bridgelink-webadmin
PLIST_LABEL=com.innovar.bridgelink-webadmin
PLIST_PATH=/Library/LaunchDaemons/com.innovar.bridgelink-webadmin.plist
SYSTEMD_UNIT=/etc/systemd/system/bridgelink-webadmin.service
BLWEBADMIN_BIN=/usr/local/bin/blwebadmin
WRAPPER_SCRIPT=/opt/bridgelink-webadmin/start-service.sh

# ── Argument parser: flag extraction ──────────────────────────────────────────
# NOTE: --uninstall must be the FIRST argument if used; see --uninstall block below.
# --non-interactive must survive exec sudo bash "$0" "$@" -- kept in "$@", not exported.
NON_INTERACTIVE=0
_PASS_ARGS=()
for _arg in "$@"; do
    case "$_arg" in
        --non-interactive) NON_INTERACTIVE=1 ; _PASS_ARGS+=("$_arg") ;;
        *)                 _PASS_ARGS+=("$_arg") ;;
    esac
done
set -- "${_PASS_ARGS[@]+"${_PASS_ARGS[@]}"}"

# ── Argument parser: --uninstall mode ─────────────────────────────────────────
# Run BEFORE main install logic so uninstall can exit early without executing
# any install steps. Usage: sudo bash install.sh --uninstall
if [ "${1:-}" = "--uninstall" ]; then

    # Root check for uninstall
    if [ "$(id -u)" -ne 0 ]; then
        echo "Error: --uninstall must be run as root (sudo bash install.sh --uninstall)" >&2
        exit 1
    fi

    OS=$(uname -s)

    if [ "$OS" = "Linux" ]; then
        # Stop service if running
        if systemctl is-active --quiet "${SERVICE_NAME}" 2>/dev/null; then
            systemctl stop "${SERVICE_NAME}"
            echo "[uninstall] Stopped ${SERVICE_NAME}."
        fi
        systemctl disable "${SERVICE_NAME}" 2>/dev/null || true
        rm -f "${SYSTEMD_UNIT}"
        systemctl daemon-reload
        echo "[uninstall] Linux service removed and daemon-reload complete."

    elif [ "$OS" = "Darwin" ]; then
        if [ -f "${PLIST_PATH}" ]; then
            launchctl unload "${PLIST_PATH}" 2>/dev/null || true
        fi
        rm -f "${PLIST_PATH}"
        echo "[uninstall] macOS LaunchDaemon plist removed."
    fi

    # Common uninstall steps (both platforms)
    rm -rf "${INSTALL_DIR}"
    echo "[uninstall] Removed ${INSTALL_DIR}"
    rm -f "${BLWEBADMIN_BIN}"
    echo "[uninstall] Removed ${BLWEBADMIN_BIN}"
    echo "[uninstall] BridgeLink WebAdmin has been uninstalled."
    exit 0
fi

# ── Step 1: Root check ─────────────────────────────────────────────────────────
if [ "$(id -u)" -ne 0 ]; then
    echo "[install] Root required. Re-running with sudo..."
    exec sudo bash "$0" "$@"
fi

# ── Step 2: OS detection ───────────────────────────────────────────────────────
OS=$(uname -s)
if [ "$OS" = "Linux" ]; then
    PLATFORM=linux
elif [ "$OS" = "Darwin" ]; then
    PLATFORM=macos
else
    echo "Error: Unsupported OS: ${OS}. Supported: Linux, macOS" >&2
    exit 1
fi

echo "[install] Detected platform: ${PLATFORM}"

# ── Unattended install: config file + environment variable loading ────────────
# Mechanism 2: source install-answers.conf if present.
# Search order: same directory as install.sh, then /etc/bridgelink/
_CONF_LOADED=""
_SCRIPT_DIR_EARLY="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [ -f "${_SCRIPT_DIR_EARLY}/install-answers.conf" ]; then
    _CONF_LOADED="${_SCRIPT_DIR_EARLY}/install-answers.conf"
elif [ -f "/etc/bridgelink/install-answers.conf" ]; then
    _CONF_LOADED="/etc/bridgelink/install-answers.conf"
fi

if [ -n "${_CONF_LOADED}" ]; then
    echo "[install] Loaded config from ${_CONF_LOADED}"
    while IFS= read -r _line || [ -n "${_line}" ]; do
        case "${_line}" in
            ''|'#'*) continue ;;
        esac
        _key="${_line%%=*}"
        _val="${_line#*=}"
        # trim leading/trailing whitespace from key and value
        _key="${_key#"${_key%%[![:space:]]*}"}"
        _key="${_key%"${_key##*[![:space:]]}"}"
        _val="${_val#"${_val%%[![:space:]]*}"}"
        _val="${_val%"${_val##*[![:space:]]}"}"
        [ -n "${_key}" ] && printf -v "${_key}" '%s' "${_val}"
    done < "${_CONF_LOADED}"
fi

# Mechanism 1: announce which env vars are active (informational only).
if [ -n "${BL_PORT:-}" ];         then echo "[install] Using BL_PORT=${BL_PORT} (from environment)"; fi
if [ -n "${BL_SERVER_URL:-}" ];   then echo "[install] Using BL_SERVER_URL=${BL_SERVER_URL} (from environment)"; fi
if [ -n "${BL_SERVER_URLS:-}" ];  then echo "[install] Using BL_SERVER_URLS=${BL_SERVER_URLS} (from environment)"; fi
if [ -n "${NODEJS_HOME:-}" ];     then echo "[install] Using NODEJS_HOME=${NODEJS_HOME} (from environment)"; fi
if [ -n "${BL_SAME_HOST:-}" ];    then echo "[install] Using BL_SAME_HOST=${BL_SAME_HOST} (from environment)"; fi
if [ -n "${BL_ACCEPT_LICENSE:-}" ]; then echo "[install] Using BL_ACCEPT_LICENSE=${BL_ACCEPT_LICENSE} (from environment)"; fi

# Validation + conflict check for BL_SAME_HOST (strict: only the exact value 1 activates it).
if [ -n "${BL_SAME_HOST:-}" ] && [ "${BL_SAME_HOST}" != "1" ]; then
    echo "Error: BL_SAME_HOST must be 1 (or unset)" >&2
    exit 1
fi
if [ "${BL_SAME_HOST:-}" = "1" ] && { [ -n "${BL_SERVER_URL:-}" ] || [ -n "${BL_SERVER_URLS:-}" ]; }; then
    echo "Error: BL_SAME_HOST=1 conflicts with BL_SERVER_URL/BL_SERVER_URLS — choose one deployment model." >&2
    exit 1
fi

# Mechanism 3: --non-interactive pre-flight check.
# BL_PORT has a safe default so it is not required.
# At least one deployment var is required -- no silent localhost default.
if [ "${NON_INTERACTIVE}" = "1" ]; then
    if [ -z "${BL_SERVER_URL:-}" ] && [ -z "${BL_SERVER_URLS:-}" ] && [ "${BL_SAME_HOST:-}" != "1" ]; then
        echo "Error: --non-interactive requires BL_SERVER_URL, BL_SERVER_URLS, or BL_SAME_HOST=1 to be set." >&2
        echo "  Set via environment:  BL_SERVER_URL=https://your-server:8443" >&2
        echo "  Or via config file:   install-answers.conf with BL_SERVER_URL=..." >&2
        echo "  Same Host example:    BL_SAME_HOST=1" >&2
        exit 1
    fi
fi

# ── License agreement ──────────────────────────────────────────────────────────
# The package ships two license documents next to install.sh:
#   LICENSE                — Business Source License 1.1
#   SUPPLEMENTAL-TERMS.txt — BridgeLink WebAdmin Supplemental Terms
# Installation requires acceptance BEFORE any system change. Unattended installs
# accept by setting BL_ACCEPT_LICENSE=1 (env var or install-answers.conf).
if [ -n "${BL_ACCEPT_LICENSE:-}" ] && [ "${BL_ACCEPT_LICENSE}" != "1" ]; then
    echo "Error: BL_ACCEPT_LICENSE must be 1 (or unset)" >&2
    exit 1
fi

if [ "${BL_ACCEPT_LICENSE:-}" = "1" ]; then
    echo "[install] License terms accepted via BL_ACCEPT_LICENSE=1"
elif [ "${NON_INTERACTIVE}" = "1" ]; then
    echo "Error: --non-interactive requires BL_ACCEPT_LICENSE=1 to accept the license terms." >&2
    echo "  License documents: ${_SCRIPT_DIR_EARLY}/LICENSE" >&2
    echo "                     ${_SCRIPT_DIR_EARLY}/SUPPLEMENTAL-TERMS.txt" >&2
    exit 1
else
    echo ""
    echo "BridgeLink WebAdmin is licensed under the Business Source License 1.1"
    echo "and the BridgeLink WebAdmin Supplemental Terms. The full documents are"
    echo "included in this package:"
    echo "  ${_SCRIPT_DIR_EARLY}/LICENSE"
    echo "  ${_SCRIPT_DIR_EARLY}/SUPPLEMENTAL-TERMS.txt"
    echo ""
    printf "Do you accept the license terms? [y/N]: "
    read -r LICENSE_ANSWER
    case "${LICENSE_ANSWER}" in
        y|Y|yes|YES|Yes)
            echo "[install] License terms accepted."
            ;;
        *)
            echo "License terms not accepted — installation cancelled." >&2
            exit 1
            ;;
    esac
fi

# ── Helper: install_node_lts ───────────────────────────────────────────────────
NODE_MAJOR=22

install_node_lts() {
    if [ "$PLATFORM" = "linux" ]; then
        # Ensure curl is available for the NodeSource setup script
        if ! command -v curl >/dev/null 2>&1; then
            echo "[install] curl not found — installing curl first..."
            if command -v apt-get >/dev/null 2>&1; then
                apt-get update -y && apt-get install -y curl
            elif command -v dnf >/dev/null 2>&1; then
                dnf install -y curl
            elif command -v yum >/dev/null 2>&1; then
                yum install -y curl
            else
                echo "Error: curl not found and no supported package manager to install it." >&2
                exit 1
            fi
        fi
        if command -v apt-get >/dev/null 2>&1; then
            echo "[install] Installing Node.js ${NODE_MAJOR} via NodeSource (apt)..."
            curl -fsSL https://deb.nodesource.com/setup_${NODE_MAJOR}.x | bash -
            apt-get install -y nodejs
        elif command -v dnf >/dev/null 2>&1; then
            echo "[install] Installing Node.js ${NODE_MAJOR} via NodeSource (dnf)..."
            curl -fsSL https://rpm.nodesource.com/setup_${NODE_MAJOR}.x | bash -
            dnf install -y nodejs
        elif command -v yum >/dev/null 2>&1; then
            echo "[install] Installing Node.js ${NODE_MAJOR} via NodeSource (yum)..."
            curl -fsSL https://rpm.nodesource.com/setup_${NODE_MAJOR}.x | bash -
            yum install -y nodejs
        else
            echo "Error: Cannot auto-install Node.js: no supported package manager found (apt-get, dnf, yum). Install Node.js ${NODE_MAJOR} manually then re-run install.sh" >&2
            exit 1
        fi
    elif [ "$PLATFORM" = "macos" ]; then
        if ! command -v brew >/dev/null 2>&1; then
            echo "[install] Installing Homebrew..."
            /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
        fi
        if command -v brew >/dev/null 2>&1; then
            echo "[install] Running: brew install node@${NODE_MAJOR}"
            brew install node@${NODE_MAJOR}
            brew link node@${NODE_MAJOR} --force --overwrite || true
        else
            echo "Error: Cannot auto-install Node.js: brew not available after install attempt. Install Node.js ${NODE_MAJOR} manually then re-run install.sh" >&2
            exit 1
        fi
    fi
    # Re-derive NODEJS_PATH after install
    NODEJS_PATH=$(command -v node 2>/dev/null || true)
}

# ── Step 3: Node.js detection ──────────────────────────────────────────────────
NODEJS_PATH=$(command -v node 2>/dev/null || true)

if [ -z "$NODEJS_PATH" ] && [ -n "${NODEJS_HOME:-}" ]; then
    NODEJS_PATH=${NODEJS_HOME}/bin/node
fi

if [ -z "$NODEJS_PATH" ]; then
    # Guard: non-interactive mode requires NODEJS_HOME (no interactive menu available)
    if [ "${NON_INTERACTIVE}" = "1" ]; then
        echo "Error: --non-interactive requires NODEJS_HOME to be set (Node.js not found in PATH)." >&2
        exit 1
    fi
    echo ""
    echo "Node.js was not found on this system."
    echo "Choose an option:"
    echo "  1) Auto-install Node.js LTS now"
    echo "  2) Enter NODEJS_HOME path to an existing Node.js installation"
    printf "Selection [1]: "
    read -r NODE_CHOICE
    NODE_CHOICE="${NODE_CHOICE:-1}"

    if [ "$NODE_CHOICE" = "1" ]; then
        install_node_lts
    elif [ "$NODE_CHOICE" = "2" ]; then
        printf "Enter NODEJS_HOME path (directory containing bin/node): "
        read -r NODEJS_HOME
        NODEJS_PATH="${NODEJS_HOME}/bin/node"
    else
        echo "Error: Invalid choice. Exiting." >&2
        exit 1
    fi
fi

# Verify node works
if ! "$NODEJS_PATH" --version >/dev/null 2>&1; then
    echo "Error: Node.js not functional at '${NODEJS_PATH}'. Please install Node.js and re-run." >&2
    exit 1
fi

export NODEJS_PATH
echo "[install] Node.js: $(${NODEJS_PATH} --version) at ${NODEJS_PATH}"

# ── Step 4: File copy ──────────────────────────────────────────────────────────
mkdir -p "${INSTALL_DIR}"

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"

echo "[install] Copying files to ${INSTALL_DIR}..."
cp -r "${SCRIPT_DIR}/." "${INSTALL_DIR}/"

# Remove install.sh from install dir (not needed at runtime)
rm -f "${INSTALL_DIR}/install.sh"

# Remove Windows installer artifacts
rm -f "${INSTALL_DIR}"/*.ps1 "${INSTALL_DIR}"/*.bat "${INSTALL_DIR}"/*.iss

echo "[install] Installing Node.js dependencies..."
cd "${INSTALL_DIR}" && npm install --omit=dev --ignore-scripts

echo "[install] Files copied to ${INSTALL_DIR}"

# ── Step 5: Port prompt ────────────────────────────────────────────────────────
if [ -n "${BL_PORT:-}" ]; then
    PORT="${BL_PORT}"
else
    printf "Enter the port for BridgeLink WebAdmin [default: 8444]: "
    read -r PORT
    PORT="${PORT:-8444}"
fi
# Strip newlines from port (T-04-01 threat mitigation)
PORT="${PORT//$'\n'/}"
PORT="${PORT//$'\r'/}"
if ! [[ "$PORT" =~ ^[0-9]+$ ]]; then
    echo "Error: Port must be a number" >&2
    exit 1
fi

# ── Step 6: Server URL type prompt ────────────────────────────────────────────
CONF_URL_LINE=""

if [ "${BL_SAME_HOST:-}" = "1" ]; then
    # Same Host supplied via env/config: write no server URL
    CONF_URL_LINE=""
    echo "[install] Same Host: no server URL will be written — WebAdmin auto-authorizes the BridgeLink server on this host"

elif [ -n "${BL_SERVER_URL:-}" ]; then
    # Single server supplied via env/config
    BL_SERVER_URL="${BL_SERVER_URL//$'\n'/}"
    BL_SERVER_URL="${BL_SERVER_URL//$'\r'/}"
    if [[ "${BL_SERVER_URL}" != https://* ]]; then
        echo "Error: BL_SERVER_URL must start with https://" >&2
        exit 1
    fi
    CONF_URL_LINE="BRIDGELINK_SERVER_URL=${BL_SERVER_URL}"

elif [ -n "${BL_SERVER_URLS:-}" ]; then
    # Multi-server list supplied via env/config (comma-separated)
    BL_SERVER_URLS="${BL_SERVER_URLS//$'\n'/}"
    BL_SERVER_URLS="${BL_SERVER_URLS//$'\r'/}"
    _URL_COUNT=0
    _OLD_IFS="$IFS"
    IFS=','
    for _url in ${BL_SERVER_URLS}; do
        _url="${_url## }"
        _url="${_url%% }"
        if [[ "${_url}" != https://* ]]; then
            echo "Error: BL_SERVER_URLS entry '${_url}' must start with https://" >&2
            exit 1
        fi
        _URL_COUNT=$(( _URL_COUNT + 1 ))
    done
    IFS="$_OLD_IFS"
    if [ "${_URL_COUNT}" -lt 2 ]; then
        echo "Error: BL_SERVER_URLS must contain at least 2 URLs (comma-separated)" >&2
        exit 1
    fi
    CONF_URL_LINE="BRIDGELINK_ALLOWED_SERVERS=${BL_SERVER_URLS}"

else
    # Interactive flow — original code verbatim (lines 195–261 of pre-Phase-5 file)
    echo ""
    echo "Select the BridgeLink deployment model:"
    echo "  1) Same Host (Recommended)"
    echo "       I want WebAdmin to manage the BridgeLink installation on this server."
    echo "  2) Dedicated Host – Single Server"
    echo "       I want WebAdmin to manage a single BridgeLink installation on another server."
    echo "  3) Dedicated Host – Multi-Server"
    echo "       I want WebAdmin to manage BridgeLink installations on multiple servers."
    printf "Selection [1]: "
    read -r SERVER_TYPE
    SERVER_TYPE="${SERVER_TYPE:-1}"

    case "$SERVER_TYPE" in
        1)
            CONF_URL_LINE=""
            echo "[install] Same Host: no server URL will be written — WebAdmin auto-authorizes the BridgeLink server on this host"
            ;;
        2)
            SINGLE_URL=""
            URL_ATTEMPTS=0
            while [ $URL_ATTEMPTS -lt 3 ]; do
                printf "Enter BridgeLink server URL (https://...): "
                read -r SINGLE_URL
                SINGLE_URL="${SINGLE_URL//$'\n'/}"
                SINGLE_URL="${SINGLE_URL//$'\r'/}"
                if [[ "${SINGLE_URL}" == https://* ]]; then
                    break
                fi
                echo "Error: URL must start with https://" >&2
                URL_ATTEMPTS=$((URL_ATTEMPTS + 1))
                if [ $URL_ATTEMPTS -ge 3 ]; then
                    echo "Error: Maximum attempts reached. Exiting." >&2
                    exit 1
                fi
            done
            CONF_URL_LINE="BRIDGELINK_SERVER_URL=${SINGLE_URL}"
            ;;
        3)
            echo "Enter BridgeLink server URLs one per line. Press Enter on blank line when done (minimum 2 required)."
            MULTI_URLS=()
            while true; do
                printf "URL (or blank to finish): "
                read -r URL_ENTRY
                URL_ENTRY="${URL_ENTRY//$'\n'/}"
                URL_ENTRY="${URL_ENTRY//$'\r'/}"
                if [ -z "$URL_ENTRY" ]; then
                    break
                fi
                if [[ "${URL_ENTRY}" != https://* ]]; then
                    echo "Error: URL must start with https://" >&2
                    continue
                fi
                MULTI_URLS+=("$URL_ENTRY")
            done
            if [ "${#MULTI_URLS[@]}" -lt 2 ]; then
                echo "Error: At least 2 URLs required for multiple server mode" >&2
                exit 1
            fi
            MULTI_URL_STRING=$(IFS=','; echo "${MULTI_URLS[*]}")
            CONF_URL_LINE="BRIDGELINK_ALLOWED_SERVERS=${MULTI_URL_STRING}"
            ;;
        *)
            echo "Error: Invalid selection '${SERVER_TYPE}'. Choose 1, 2, or 3." >&2
            exit 1
            ;;
    esac
fi

# ── Step 7: Write webadmin.conf ────────────────────────────────────────────────
# CRITICAL: bare KEY=VALUE format — no export keyword, no surrounding quotes.
# systemd EnvironmentFile= reads bare KEY=VALUE natively.
if [ -n "${CONF_URL_LINE}" ]; then
    printf "PORT=%s\n%s\n" "${PORT}" "${CONF_URL_LINE}" > "${INSTALL_DIR}/webadmin.conf"
else
    printf "PORT=%s\n" "${PORT}" > "${INSTALL_DIR}/webadmin.conf"
fi
chmod 640 "${INSTALL_DIR}/webadmin.conf"
if ! grep -q "^PORT=" "${INSTALL_DIR}/webadmin.conf"; then
    echo "Error: webadmin.conf write verification failed" >&2
    exit 1
fi
echo "[install] webadmin.conf written"

# ── Step 8: TLS certificate generation ────────────────────────────────────────
command -v openssl >/dev/null 2>&1 || { echo "Error: openssl not found. Install it and re-run." >&2; exit 1; }

openssl req -x509 -newkey rsa:2048 -days 3650 -nodes \
    -out "${INSTALL_DIR}/cert.pem" \
    -keyout "${INSTALL_DIR}/key.pem" \
    -subj "/CN=localhost/O=Innovar Healthcare/C=US"

chmod 600 "${INSTALL_DIR}/key.pem"
chmod 644 "${INSTALL_DIR}/cert.pem"
echo "[install] TLS certificate generated (self-signed, 10 years)"

# Append SSL paths to webadmin.conf so the Linux EnvironmentFile picks them up
printf "SSL_CERT_FILE=%s/cert.pem\nSSL_KEY_FILE=%s/key.pem\n" \
    "${INSTALL_DIR}" "${INSTALL_DIR}" >> "${INSTALL_DIR}/webadmin.conf"

# ── SERVICE REGISTRATION (added by 04-02 Linux / 04-03 macOS) ────────────────
if [ "${PLATFORM}" = "linux" ]; then

    echo "[install] Writing systemd unit file to ${SYSTEMD_UNIT}..."
    # Double-quoted heredoc delimiter: ${NODEJS_PATH} is expanded at install time,
    # baking the resolved node path into the unit file (service runs as root and may
    # not have the user's PATH).
    cat > "${SYSTEMD_UNIT}" <<EOF
[Unit]
Description=BridgeLink WebAdmin
After=network.target

[Service]
# TODO: add User= directive for least-privilege operation (run as dedicated non-root user)
Type=simple
WorkingDirectory=/opt/bridgelink-webadmin
ExecStart=${NODEJS_PATH} /opt/bridgelink-webadmin/server.js
EnvironmentFile=/opt/bridgelink-webadmin/webadmin.conf
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
EOF

    echo "[install] systemd unit file written to ${SYSTEMD_UNIT}"
    systemctl daemon-reload
    systemctl enable --now "${SERVICE_NAME}"

    # Verify service started successfully
    SERVICE_ACTIVE=$(systemctl is-active "${SERVICE_NAME}" 2>/dev/null || true)
    if [ "${SERVICE_ACTIVE}" != "active" ]; then
        echo "Warning: service ${SERVICE_NAME} status is '${SERVICE_ACTIVE}' — it may still be starting. Check with: systemctl status ${SERVICE_NAME}" >&2
    fi

    echo "[install] Service ${SERVICE_NAME} registered, enabled, and started."

elif [ "${PLATFORM}" = "macos" ]; then

    # ── Sub-step M1: Write wrapper script ────────────────────────────────────────
    # Single-quoted heredoc so variable references inside the wrapper are NOT
    # expanded by install.sh — they run literally at service start time.
    # NODE_PATH_PLACEHOLDER is substituted afterwards via sed.
    cat > "${WRAPPER_SCRIPT}" <<'WRAPPER_EOF'
#!/bin/bash
# BridgeLink WebAdmin service wrapper — sources webadmin.conf at runtime
# (no set -e: service wrappers must not exit on non-fatal sourcing errors)
if [ -f /opt/bridgelink-webadmin/webadmin.conf ]; then
    while IFS='=' read -r key val; do
        # Skip blank lines and comments
        case "$key" in
            ''|\#*) continue ;;
        esac
        export "$key"="$val"
    done < /opt/bridgelink-webadmin/webadmin.conf
fi
export SSL_CERT_FILE=/opt/bridgelink-webadmin/cert.pem
export SSL_KEY_FILE=/opt/bridgelink-webadmin/key.pem
cd /opt/bridgelink-webadmin
exec NODE_PATH_PLACEHOLDER /opt/bridgelink-webadmin/server.js
WRAPPER_EOF

    # Substitute NODE_PATH_PLACEHOLDER with the resolved node binary path
    sed -i.bak "s|NODE_PATH_PLACEHOLDER|${NODEJS_PATH}|g" "${WRAPPER_SCRIPT}" && rm -f "${WRAPPER_SCRIPT}.bak"

    chmod 700 "${WRAPPER_SCRIPT}"
    echo "[install] macOS wrapper script written to ${WRAPPER_SCRIPT}"

    # ── Sub-step M2: Write LaunchDaemon plist ────────────────────────────────────
    # Double-quoted heredoc: ${WRAPPER_SCRIPT} and ${PLIST_LABEL} expand at install time.
    # No EnvironmentVariables dict — wrapper script sources webadmin.conf at runtime,
    # so conf changes take effect after blwebadmin restart without regenerating the plist.
    # NOTE: v1 runs as root (no UserName key); add UserName= for least-privilege in future version.
    cat > "${PLIST_PATH}" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>${PLIST_LABEL}</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/bash</string>
        <string>${WRAPPER_SCRIPT}</string>
    </array>
    <key>WorkingDirectory</key>
    <string>/opt/bridgelink-webadmin</string>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/var/log/bridgelink-webadmin.log</string>
    <key>StandardErrorPath</key>
    <string>/var/log/bridgelink-webadmin.err</string>
</dict>
</plist>
EOF

    chmod 644 "${PLIST_PATH}"
    echo "[install] LaunchDaemon plist written to ${PLIST_PATH}"

    # ── Sub-step M3: Load the service ────────────────────────────────────────────
    launchctl load -w "${PLIST_PATH}"
    echo "[install] LaunchDaemon loaded. Verifying..."

    sleep 2
    MACOS_SERVICE_STATUS=$(launchctl list | grep "${SERVICE_NAME}" || true)
    if [ -z "${MACOS_SERVICE_STATUS}" ]; then
        echo "[install] Warning: service may not have started — check /var/log/bridgelink-webadmin.err" >&2
    else
        echo "[install] Service com.innovar.bridgelink-webadmin is running."
    fi

fi

# ── Step 9: Install blwebadmin CLI ────────────────────────────────────────────
# Single-quoted heredoc: the script's own variable references must NOT be
# expanded by install.sh — they are literal bash code that runs at CLI invocation
# time. The blwebadmin script detects OS at runtime via uname -s.
cat > "${BLWEBADMIN_BIN}" <<'BLWEBADMIN_EOF'
#!/usr/bin/env bash
# blwebadmin — BridgeLink WebAdmin service management CLI
# Usage: blwebadmin {start|stop|restart|status|logs}
# Note: start/stop/restart require sudo on most systems

SERVICE="bridgelink-webadmin"
PLIST="/Library/LaunchDaemons/com.innovar.bridgelink-webadmin.plist"
OS=$(uname -s)

case "${1:-help}" in
    start)
        if [ "$OS" = "Darwin" ]; then
            launchctl load -w "${PLIST}"
        else
            systemctl start "${SERVICE}"
        fi ;;
    stop)
        if [ "$OS" = "Darwin" ]; then
            launchctl unload "${PLIST}"
        else
            systemctl stop "${SERVICE}"
        fi ;;
    restart)
        if [ "$OS" = "Darwin" ]; then
            launchctl unload "${PLIST}" 2>/dev/null || true
            launchctl load -w "${PLIST}"
        else
            systemctl restart "${SERVICE}"
        fi ;;
    status)
        if [ "$OS" = "Darwin" ]; then
            launchctl print "system/com.innovar.bridgelink-webadmin" 2>/dev/null || echo "Service not loaded or not running"
        else
            systemctl status "${SERVICE}"
        fi ;;
    logs)
        if [ "$OS" = "Darwin" ]; then
            tail -f /var/log/bridgelink-webadmin.log 2>/dev/null || \
                log stream --predicate 'subsystem == "com.innovar.bridgelink-webadmin"'
        else
            journalctl -u "${SERVICE}" -f
        fi ;;
    *)
        echo "Usage: blwebadmin {start|stop|restart|status|logs}"
        exit 1 ;;
esac
BLWEBADMIN_EOF

chmod +x "${BLWEBADMIN_BIN}"
echo "[install] blwebadmin CLI installed to ${BLWEBADMIN_BIN}"

# ── Step 10: Completion message ────────────────────────────────────────────────
echo "------------------------------------------------------------"
echo "BridgeLink WebAdmin installed successfully!"
echo ""
echo "Service:   ${SERVICE_NAME}"
echo "URL:       https://localhost:${PORT}"
echo "CLI:       blwebadmin start|stop|restart|status|logs"
echo "Config:    ${INSTALL_DIR}/webadmin.conf"
echo ""
if [ "${PLATFORM}" = "linux" ]; then
    echo "Service is active and enabled (auto-starts on reboot)."
else
    echo "LaunchDaemon loaded (auto-starts on reboot)."
fi
echo "------------------------------------------------------------"
echo ""
echo "Unattended install:"
echo "  Env vars:    BL_ACCEPT_LICENSE, BL_PORT, BL_SERVER_URL, BL_SERVER_URLS, BL_SAME_HOST, NODEJS_HOME"
echo "  Config file: place install-answers.conf next to install.sh"
echo "  sudo note:   use 'sudo -E bash install.sh' to preserve env vars through sudo"
echo "  CI/pipeline: add --non-interactive to error on any missing required var"
