#!/bin/bash
# docker-entrypoint.sh - Nexus Docker container entrypoint
# Handles initialization and starts the Nexus server.
# Refactored for maintainability: functions, extracted Python scripts, signal handling.

set -e
set -o pipefail

# Issue #3699: the faiss/torch/txtai LD_PRELOAD + GLIBC_TUNABLES + SIMD
# clamps + jemalloc plumbing that used to live here are gone — direct
# pgvector + pg_search no longer pulls those libraries in.

# Load helpers (same directory as this script)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=dockerfiles/entrypoint-helpers.sh
if [ -f "${SCRIPT_DIR}/entrypoint-helpers.sh" ]; then
    # When copied to /usr/local/bin, helpers may be alongside
    source "${SCRIPT_DIR}/entrypoint-helpers.sh"
elif [ -f "/app/dockerfiles/entrypoint-helpers.sh" ]; then
    source /app/dockerfiles/entrypoint-helpers.sh
else
    # Fallback: define minimal colors and no-op cleanup
    RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
    cleanup() { [ -n "${SERVER_PID:-}" ] && kill -TERM "$SERVER_PID" 2>/dev/null; wait "$SERVER_PID" 2>/dev/null; exit 0; }
    ensure_dir() { mkdir -p "$1"; }
    validate_port() { [[ "$1" =~ ^[0-9]+$ ]] && [ "$1" -ge 1 ] && [ "$1" -le 65535 ]; }
    run_python() { python3 "$@"; }
fi

# Configuration
ADMIN_USER="${NEXUS_ADMIN_USER:-admin}"
API_KEY_FILE="${NEXUS_API_KEY_FILE:-/app/data/.admin-api-key}"
CONFIG_FILE="${NEXUS_CONFIG_FILE:-}"
APP_DIR="${APP_DIR:-/app}"
SCRIPTS_DIR="${SCRIPTS_DIR:-/app/scripts}"
NEXUS_PORT="${NEXUS_PORT:-2026}"

# Optional: set to "true" to redact API key from startup banner (logs)
REDACT_API_KEY_IN_LOGS="${REDACT_API_KEY_IN_LOGS:-false}"

# PIDs for cleanup
SERVER_PID=""

# -----------------------------------------------------------------------------
# Bind-mount directory init + privilege drop
# -----------------------------------------------------------------------------
# When a host directory is bind-mounted over /app/data, the image's
# pre-created subdirectories (skills/) disappear and the
# mount may be owned by a different uid.  If we're root, fix that now
# and re-exec as the nexus user.
fix_data_dir_and_drop_privileges() {
    if [ "$(id -u)" = "0" ]; then
        # Create required subdirectories inside the (possibly bind-mounted) data dir
        mkdir -p /app/data/skills

        if ! chown -R nexus:nexus /app/data 2>/dev/null; then
            # chown fails on macOS Docker Desktop (VirtioFS/gRPC FUSE does not
            # support ownership changes on bind-mounted host directories).
            # Verify the nexus user can still write — on macOS, Docker maps all
            # container access through the host uid regardless of in-container
            # ownership, so writes succeed despite the chown failure.
            if gosu nexus touch /app/data/.entrypoint-write-test 2>/dev/null; then
                rm -f /app/data/.entrypoint-write-test
            else
                echo -e "${RED:-}ERROR: chown failed and /app/data is not writable by nexus user.${NC:-}"
                echo "  Check bind-mount permissions on the host directory."
                exit 1
            fi
        fi

        # Fix CLI connector config permissions (Issue #3148)
        # gws/gh configs are bind-mounted from the host and may be root-only.
        # The nexus user needs read access for CLI connectors.
        for cfg_dir in /home/nexus/.config/gws /home/nexus/.config/gh; do
            if [ -d "$cfg_dir" ]; then
                chown -R nexus:nexus "$cfg_dir" 2>/dev/null || chmod -R o+rX "$cfg_dir" 2>/dev/null || true
            fi
        done

        # Re-exec the entrypoint as the nexus user
        exec gosu nexus "$0" "$@"
    fi
}

fix_data_dir_and_drop_privileges "$@"

# -----------------------------------------------------------------------------
# GOOGLE_APPLICATION_CREDENTIALS sanity
#
# nexus-stack.yml defaults GOOGLE_APPLICATION_CREDENTIALS to
# /app/gcs-credentials.json so that GCS-style service-account flows can work
# without extra wiring. When that file doesn't exist, Google's auto-auth
# chain still probes the env var first and fails hard — which breaks the
# gws CLI even though bind-mounted user-flow creds at ~/.config/gws/ would
# otherwise authenticate it. Clear the dangling env var so auto-auth falls
# back to the user-flow creds.
# -----------------------------------------------------------------------------
if [ -n "${GOOGLE_APPLICATION_CREDENTIALS:-}" ] && [ ! -s "${GOOGLE_APPLICATION_CREDENTIALS}" ]; then
    echo "${YELLOW:-}GOOGLE_APPLICATION_CREDENTIALS points to missing file '${GOOGLE_APPLICATION_CREDENTIALS}'; unsetting so user-flow auth can take over.${NC:-}"
    unset GOOGLE_APPLICATION_CREDENTIALS
fi

# -----------------------------------------------------------------------------
# AWS_PROFILE sanity
#
# nexus-stack.yml passes AWS_PROFILE through from the operator's shell. When
# unset upstream, Compose still injects it as an empty string ("") because
# the service has an AWS_PROFILE: key. botocore treats "" like "use profile
# ''" and then raises ProfileNotFound — which breaks env-only creds
# (AWS_ACCESS_KEY_ID/..) and EC2/ECS IAM-role auth even though boto3's
# default chain would otherwise resolve them cleanly.
#
# Also unset when:
#   * no ~/.aws file is mounted (nothing to resolve the profile against), or
#   * the named profile is absent from the mounted ~/.aws/credentials and
#     ~/.aws/config files (stale shell-inherited name).
#
# Profile-presence check uses exact INI-header matching (NOT regex), so
# profile names with regex metacharacters (``.``, ``+``, ``*``) resolve
# correctly — e.g. ``prod.us`` matches only ``[prod.us]``, not anything
# containing "prod" + any char + "us". Botocore accepts both the bare
# ``[name]`` form (credentials file) and the ``[profile <name>]`` form
# (config file); either is treated as a match.
# -----------------------------------------------------------------------------
_aws_profile_file_has_name() {
    # Exact INI-header comparison for an AWS profile name. Accepts both
    # spellings botocore honors: ``[name]`` (credentials file) and
    # ``[profile <name>]`` (config file). No regex — profile names
    # containing ``.``, ``+`` or other metacharacters compare as plain
    # strings.
    local _file="$1" _wanted="$2" _prof_line="" _rest=""
    [ -s "$_file" ] || return 1
    local _bracketed_wanted="[${_wanted}]"
    # ``|| [ -n "$_prof_line" ]`` keeps the loop running for a final line
    # that isn't newline-terminated (common in hand-edited AWS config
    # files). Without it, ``[prod]`` at EOF without ``\n`` would be
    # silently dropped, making a valid profile look missing and tripping
    # the fail-closed startup guard.
    while IFS= read -r _prof_line || [ -n "$_prof_line" ]; do
        # Strip leading + trailing whitespace from the raw line.
        _prof_line="${_prof_line#"${_prof_line%%[![:space:]]*}"}"
        _prof_line="${_prof_line%"${_prof_line##*[![:space:]]}"}"
        case "$_prof_line" in
            \[*\])
                if [ "$_prof_line" = "$_bracketed_wanted" ]; then
                    return 0
                fi
                case "$_prof_line" in
                    "[profile "*"]")
                        _rest="${_prof_line#\[profile }"
                        _rest="${_rest%\]}"
                        # Collapse any whitespace around the name so
                        # ``[profile   prod]`` still matches ``prod``.
                        _rest="${_rest#"${_rest%%[![:space:]]*}"}"
                        _rest="${_rest%"${_rest##*[![:space:]]}"}"
                        [ "$_rest" = "$_wanted" ] && return 0
                        ;;
                esac
                ;;
        esac
    done < "$_file"
    return 1
}

_aws_profile_fail_open="${NEXUS_AWS_PROFILE_FAIL_OPEN:-false}"
case "$_aws_profile_fail_open" in
    true|TRUE|True|1|yes|YES|Yes|on|ON|On) _aws_profile_fail_open="true" ;;
    *) _aws_profile_fail_open="false" ;;
esac

_aws_profile_cannot_resolve() {
    # Behavior when AWS_PROFILE is set but can't be found. Default is
    # fail-closed: exit non-zero with a clear error so operators learn of
    # the typo / missing mount instead of silently sending traffic to the
    # wrong AWS account via env/IAM fallback. Set
    # NEXUS_AWS_PROFILE_FAIL_OPEN=true to restore the old lenient behavior.
    local _reason="$1"
    if [ "$_aws_profile_fail_open" = "true" ]; then
        echo "${YELLOW:-}AWS_PROFILE=${AWS_PROFILE} ${_reason}; NEXUS_AWS_PROFILE_FAIL_OPEN=true, unsetting so env/IAM-role creds can take over.${NC:-}"
        unset AWS_PROFILE
        return 0
    fi
    echo "${RED:-}ERROR: AWS_PROFILE=${AWS_PROFILE} ${_reason}.${NC:-}" >&2
    echo "  This is almost always a typo, a missing bind mount, or a stale" >&2
    echo "  shell export. Nexus refuses to start rather than silently fall" >&2
    echo "  back to env/IAM credentials — which could route operations to" >&2
    echo "  a different AWS account." >&2
    echo "" >&2
    echo "  Fix: unset AWS_PROFILE in your shell, mount the correct ~/.aws," >&2
    echo "       or point AWS_SHARED_CREDENTIALS_FILE / AWS_CONFIG_FILE at" >&2
    echo "       the right paths." >&2
    echo "  Override (accepts the fallback risk):" >&2
    echo "       NEXUS_AWS_PROFILE_FAIL_OPEN=true" >&2
    exit 1
}

# Compose's ``AWS_PROFILE: ${AWS_PROFILE:-}`` injects an empty string when
# nothing was exported upstream. Treat that as "no preference" — unset
# silently so it doesn't masquerade as an explicit selection.
if [ -n "${AWS_PROFILE+x}" ] && [ -z "${AWS_PROFILE:-}" ]; then
    unset AWS_PROFILE
fi

# Normalize AWS_SHARED_CREDENTIALS_FILE / AWS_CONFIG_FILE. Same story:
#   * Empty string -> no preference -> unset. (Prevents downstream bugs
#     where ``Path('').exists()`` evaluates to True on the cwd.)
#   * Non-empty value pointing at a file not visible inside the container
#     -> we can't honor it. The action depends on whether AWS_PROFILE is
#     also set:
#       - AWS_PROFILE set: fail closed by default because botocore would
#         silently resolve the profile against the mounted ``~/.aws/``,
#         potentially a different account. Opt-out via
#         NEXUS_AWS_PROFILE_FAIL_OPEN=true.
#       - AWS_PROFILE unset: creds come from env / IAM role / instance
#         metadata, and the file var is irrelevant to auth. Warn and
#         unset so it doesn't confuse botocore later; starting the
#         server is the right move.
for _aws_env in AWS_SHARED_CREDENTIALS_FILE AWS_CONFIG_FILE; do
    _val="${!_aws_env:-}"
    if [ -z "$_val" ]; then
        unset "$_aws_env"
    elif [ ! -f "$_val" ]; then
        if [ -z "${AWS_PROFILE:-}" ] || [ "$_aws_profile_fail_open" = "true" ]; then
            _reason="not visible inside container"
            [ "$_aws_profile_fail_open" = "true" ] \
                && _reason="$_reason; NEXUS_AWS_PROFILE_FAIL_OPEN=true"
            echo "${YELLOW:-}${_aws_env}=${_val} ${_reason}; unsetting so botocore's default chain can take over.${NC:-}"
            unset "$_aws_env"
            unset _reason
        else
            echo "${RED:-}ERROR: ${_aws_env}=${_val} is set but that file is not visible inside the container, and AWS_PROFILE=${AWS_PROFILE} points at it.${NC:-}" >&2
            echo "  nexus-stack.yml forwards this env var from your shell, but" >&2
            echo "  host absolute paths (e.g. /Users/you/.aws/config) aren't" >&2
            echo "  reachable inside the container's filesystem. Silently" >&2
            echo "  falling back to the default ~/.aws/ mount risks resolving" >&2
            echo "  AWS_PROFILE against a different AWS account than you" >&2
            echo "  intended." >&2
            echo "" >&2
            echo "  Fix: unset ${_aws_env} in your shell (the bind-mounted" >&2
            echo "       ~/.aws/ location will be used), OR point it at a" >&2
            echo "       container-visible path, OR set" >&2
            echo "       NEXUS_AWS_PROFILE_FAIL_OPEN=true to accept the" >&2
            echo "       fallback." >&2
            exit 1
        fi
    fi
done
unset _aws_env _val

# With the env vars above normalized, validate AWS_PROFILE against the
# resolved credential files (env-override first, then ~/.aws defaults).
if [ -n "${AWS_PROFILE:-}" ]; then
    _aws_cred_file="${AWS_SHARED_CREDENTIALS_FILE:-${HOME}/.aws/credentials}"
    _aws_conf_file="${AWS_CONFIG_FILE:-${HOME}/.aws/config}"
    if [ ! -s "$_aws_cred_file" ] && [ ! -s "$_aws_conf_file" ]; then
        _aws_profile_cannot_resolve \
            "set but neither ${_aws_cred_file} nor ${_aws_conf_file} is present"
    elif ! _aws_profile_file_has_name "$_aws_cred_file" "$AWS_PROFILE" \
          && ! _aws_profile_file_has_name "$_aws_conf_file" "$AWS_PROFILE"; then
        _aws_profile_cannot_resolve \
            "not present in ${_aws_cred_file} or ${_aws_conf_file}"
    fi
    unset _aws_cred_file _aws_conf_file
fi
unset _aws_profile_fail_open

# -----------------------------------------------------------------------------
# Functions
# -----------------------------------------------------------------------------
print_banner() {
    echo ""
    echo "╔═══════════════════════════════════════════╗"
    echo "║        Nexus Server - Docker Init        ║"
    echo "╚═══════════════════════════════════════════╝"
    echo ""
}

check_permissions_flags() {
    if [ "${NEXUS_SKIP_PERMISSIONS:-false}" = "true" ]; then
        echo -e "${YELLOW}⚠️  NEXUS_SKIP_PERMISSIONS=true${NC}"
        echo -e "${YELLOW}   Entity registry and permission setup will be skipped${NC}"
        echo ""
    fi
    if [ "${NEXUS_ENFORCE_PERMISSIONS:-true}" = "false" ]; then
        echo -e "${YELLOW}⚠️  NEXUS_ENFORCE_PERMISSIONS=false${NC}"
        echo -e "${YELLOW}   Runtime permission checks are DISABLED${NC}"
        echo ""
    fi
}

wait_for_postgres() {
    [ -z "${NEXUS_DATABASE_URL:-}" ] && return 0

    echo "🔌 Waiting for PostgreSQL..."

    DB_HOST="$(python3 "$SCRIPTS_DIR/parse_db_url.py" "$NEXUS_DATABASE_URL" host 2>/dev/null)" || true
    DB_PORT="$(python3 "$SCRIPTS_DIR/parse_db_url.py" "$NEXUS_DATABASE_URL" port 2>/dev/null)" || true
    DB_PORT="${DB_PORT:-5432}"

    if [ -z "$DB_HOST" ]; then
        echo -e "${YELLOW}Could not parse DB host from NEXUS_DATABASE_URL, skipping wait${NC}"
        return 0
    fi

    MAX_TRIES=30
    COUNT=0
    while [ $COUNT -lt $MAX_TRIES ]; do
        if nc -z "$DB_HOST" "$DB_PORT" 2>/dev/null; then
            echo -e "${GREEN}✓ PostgreSQL is ready${NC}"
            return 0
        fi
        COUNT=$((COUNT + 1))
        if [ $COUNT -eq $MAX_TRIES ]; then
            echo -e "${RED}✗ PostgreSQL is not available after ${MAX_TRIES}s${NC}"
            exit 1
        fi
        sleep 1
    done
}

ensure_skills_directory() {
    echo ""
    echo "📦 Checking for default skills..."

    SKILLS_DIR="/app/data/skills"
    ensure_dir "$SKILLS_DIR" || exit 1

    SKILL_COUNT=0
    for f in "$SKILLS_DIR"/*.skill; do
        [ -e "$f" ] && SKILL_COUNT=$((SKILL_COUNT + 1))
    done 2>/dev/null || true

    if [ "$SKILL_COUNT" -gt 0 ]; then
        echo -e "${GREEN}✓ Found $SKILL_COUNT skill file(s)${NC}"
    else
        echo -e "${YELLOW}⚠ No skill files found in $SKILLS_DIR${NC}"
        echo "  Skills will be imported if available during provisioning"
    fi
}

init_database() {
    echo ""
    echo "📊 Initializing database..."

    if [ ! -f "$SCRIPTS_DIR/init_database.py" ]; then
        echo -e "${RED}✗ init_database.py not found${NC}"
        exit 1
    fi

    cd "$APP_DIR" || exit 1
    if ! python3 "$SCRIPTS_DIR/init_database.py"; then
        echo -e "${RED}✗ Database initialization failed${NC}"
        exit 1
    fi
    echo -e "${GREEN}✓ Database initialized${NC}"
}

setup_admin_api_key() {
    local need_create=1
    ADMIN_API_KEY=""

    if [ -f "$API_KEY_FILE" ]; then
        ADMIN_API_KEY="$(cat "$API_KEY_FILE")"
        if [ -n "$ADMIN_API_KEY" ] && [ -n "${NEXUS_DATABASE_URL:-}" ]; then
            local key_status
            key_status="$(python3 "$SCRIPTS_DIR/check_api_key.py" "$NEXUS_DATABASE_URL" "$ADMIN_API_KEY" 2>/dev/null)" || true
            if [ "$key_status" = "EXISTS" ]; then
                echo ""
                echo "🔑 Using existing admin API key (registered in database)"
                need_create=0
            else
                echo ""
                echo "⚠️  API key file exists but key not registered in database"
                echo "   Re-registering key..."
                ADMIN_API_KEY=""
            fi
        else
            ADMIN_API_KEY=""
        fi
    fi

    if [ "$need_create" -eq 1 ] && [ -z "$ADMIN_API_KEY" ]; then
        echo ""
        if [ -n "${NEXUS_API_KEY:-}" ]; then
            echo "🔑 Registering custom API key from environment..."
        else
            echo "🔑 Creating admin API key..."
        fi

        local skip_perm
        skip_perm="false"
        [ "${NEXUS_SKIP_PERMISSIONS:-false}" = "true" ] && skip_perm="true"

        local custom_key="${NEXUS_API_KEY:-}"
        local out
        out="$(python3 "$SCRIPTS_DIR/create_admin_key.py" "$NEXUS_DATABASE_URL" "$ADMIN_USER" "$custom_key" "$skip_perm" 2>&1)" || {
            echo -e "${RED}✗ Failed to create admin API key${NC}"
            echo "$out"
            exit 1
        }

        ADMIN_API_KEY="$(echo "$out" | grep "API Key:" | sed -n 's/.*API Key: *//p')"
        if [ -z "$ADMIN_API_KEY" ]; then
            echo -e "${RED}✗ Failed to extract API key${NC}"
            echo "$out"
            exit 1
        fi
        echo "$ADMIN_API_KEY" > "$API_KEY_FILE"
        echo -e "${GREEN}✓ Admin API key created and saved${NC}"
    else
        echo ""
        echo "🔑 Using existing admin API key"
    fi

    # Display API key info (set REDACT_API_KEY_IN_LOGS=true to hide key in logs)
    echo ""
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo -e "${YELLOW}ADMIN API KEY${NC}"
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo ""
    echo -e "  User:    ${BLUE}${ADMIN_USER}${NC}"
    if [ "$REDACT_API_KEY_IN_LOGS" = "true" ]; then
        echo -e "  API Key: ${GREEN}(redacted - set REDACT_API_KEY_IN_LOGS=false to show)${NC}"
    else
        echo -e "  API Key: ${GREEN}${ADMIN_API_KEY}${NC}"
        echo ""
        echo "  To use this key:"
        echo "    export NEXUS_API_KEY='${ADMIN_API_KEY}'"
        echo "    export NEXUS_URL='http://localhost:${NEXUS_PORT:-2026}'"
        echo ""
        echo "  Or retrieve from container:"
        echo "    docker logs <container-name> | grep 'API Key:'"
    fi
    echo ""
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo ""
}

init_semantic_search_if_enabled() {
    if [ -z "$CONFIG_FILE" ] || [ ! -f "$CONFIG_FILE" ]; then
        echo ""
        echo "ℹ️  No config file — semantic search controlled by deployment profile (${NEXUS_PROFILE:-full})"
        return 0
    fi

    local enabled
    enabled="$(python3 "$SCRIPTS_DIR/check_semantic_search_config.py" "$CONFIG_FILE" 2>/dev/null)" || enabled="false"

    if [ "$enabled" != "true" ]; then
        echo ""
        echo "ℹ️  Semantic search not enabled in config (features.semantic_search: false)"
        return 0
    fi

    echo ""
    echo "🔍 Initializing semantic search (from config)..."

    if ! python3 "$SCRIPTS_DIR/init_semantic_search.py"; then
        echo -e "${RED}✗ Semantic search initialization failed${NC}"
        exit 1
    fi
    echo -e "${GREEN}✓ Semantic search initialized${NC}"
}

build_serve_cmd() {
    local auth_type="${NEXUS_AUTH_TYPE:-database}"
    if [ -n "$CONFIG_FILE" ] && [ -f "$CONFIG_FILE" ]; then
        echo "nexusd --config $CONFIG_FILE --auth-type $auth_type"
    else
        # No config file — use env vars and deployment profile (Grafana/Gitea pattern).
        # NEXUS_PROFILE env var controls which bricks are enabled (default: full).
        local cmd="nexusd --host ${NEXUS_HOST:-0.0.0.0} --port ${NEXUS_PORT:-2026} --auth-type $auth_type"
        if [ -n "${NEXUS_PROFILE:-}" ]; then
            cmd="$cmd --profile ${NEXUS_PROFILE}"
        fi
        # GCS/S3 backends are configured via env vars (NEXUS_GCS_BUCKET_NAME,
        # NEXUS_GCS_PROJECT_ID) read by the config loader, not CLI flags.
        echo "$cmd"
    fi
}

wait_for_health() {
    local port="${1:-$NEXUS_PORT}"
    local max="${2:-30}"
    local i
    for i in $(seq 1 "$max"); do
        if curl --max-time 5 -sf "http://localhost:${port}/health" > /dev/null 2>&1; then
            echo -e "${GREEN}✓ Server is ready${NC}"
            return 0
        fi
        sleep 5
    done
    echo -e "${YELLOW}⚠ Server health check timeout after ${max} attempts (continuing anyway)${NC}"
    return 0
}

load_saved_mounts_if_needed() {
    if [ -n "$CONFIG_FILE" ] && [ -f "$CONFIG_FILE" ]; then
        echo ""
        echo -e "${GREEN}✓ Backends loaded from configuration file${NC}"
        return 0
    fi

    echo ""
    echo "🔄 Loading saved mounts from database..."

    local base_url="http://localhost:${NEXUS_PORT:-2026}"
    if python3 "$SCRIPTS_DIR/load_saved_mounts.py" "$base_url" "$ADMIN_API_KEY" 2>/dev/null; then
        : # script prints result
    else
        echo -e "${YELLOW}⚠ Could not load saved mounts (API not ready or error)${NC}"
    fi
}

# join_cluster_if_needed() — REMOVED.
# TLS provisioning is now automatic via 2-phase TLS bootstrap.
# All nodes start with NEXUS_PEERS; the Raft leader generates the CA
# and signs certs for followers via JoinCluster RPC.

cleanup_stale_pid_files() {
    # After an abnormal exit (e.g. SIGSEGV from a native extension), nexusd
    # cannot run its Python-level finally block, so PID/ready files survive
    # into the next container start.  Remove them unconditionally — the daemon
    # hasn't started yet at this point, so there is nothing legitimate to
    # protect.
    local nexus_home="${HOME}/.nexus"
    for f in "$nexus_home/nexusd.pid" "$nexus_home/nexusd.ready"; do
        if [ -f "$f" ]; then
            echo -e "${YELLOW}Removing stale $f from previous run${NC}"
            rm -f "$f"
        fi
    done
}

start_nexus_server() {
    echo ""
    echo "🚀 Starting Nexus server..."
    echo ""
    echo "  Host: ${NEXUS_HOST:-0.0.0.0}"
    echo "  Port: ${NEXUS_PORT:-2026}"
    echo "  Backend: ${NEXUS_BACKEND:-local}"

    if [ -n "$CONFIG_FILE" ] && [ -f "$CONFIG_FILE" ]; then
        echo "  Config: $CONFIG_FILE"
        echo ""
        echo -e "${GREEN}✓ Using configuration file${NC}"
    else
        echo "  Config: env vars + profile (${NEXUS_PROFILE:-full})"
        echo ""
        echo -e "${GREEN}✓ Using deployment profile — no config file needed${NC}"
    fi

    local auth_type="${NEXUS_AUTH_TYPE:-database}"
    local cmd=()
    if [ -n "$CONFIG_FILE" ] && [ -f "$CONFIG_FILE" ]; then
        cmd=(nexusd --config "$CONFIG_FILE" --auth-type "$auth_type")
    else
        cmd=(
            nexusd
            --host "${NEXUS_HOST:-0.0.0.0}"
            --port "${NEXUS_PORT:-2026}"
            --auth-type "$auth_type"
        )
        if [ -n "${NEXUS_PROFILE:-}" ]; then
            cmd+=(--profile "$NEXUS_PROFILE")
        fi
    fi

    echo "Starting server..."
    "${cmd[@]}" &
    SERVER_PID=$!

    trap 'cleanup TERM' SIGTERM SIGINT
    # Allow more time for server initialization (GCS sync, cache population, etc.)
    wait_for_health "${NEXUS_PORT:-2026}" 60
    sleep 2

    load_saved_mounts_if_needed

    echo ""
    echo -e "${GREEN}✓ Server initialization complete${NC}"
    echo ""

    wait $SERVER_PID
}

# -----------------------------------------------------------------------------
# Main
# -----------------------------------------------------------------------------
main() {
    print_banner
    cleanup_stale_pid_files
    check_permissions_flags
    ensure_skills_directory

    # Skip PostgreSQL-dependent steps when no database URL (cluster profile)
    if [ -n "${NEXUS_DATABASE_URL:-}" ]; then
        wait_for_postgres
        init_database
        setup_admin_api_key
    else
        echo -e "${GREEN}✓ No NEXUS_DATABASE_URL — skipping PostgreSQL init (cluster profile)${NC}"
    fi
    init_semantic_search_if_enabled
    # Note: TLS provisioning is file-based. If {data_dir}/tls/join-token
    # exists, nexusd reads it and provisions certs from the leader.
    start_nexus_server
}

main "$@"
