#!/usr/bin/env sh
# SPDX-License-Identifier: BUSL-1.1
# Copyright (c) 2026 Eigentic, Inc. — see LICENSE.
# RookOne's one-line uv-tool installer: curl -LsSf https://get.rookone.io | sh

set -eu

ROOKONE_INDEX_URL="${ROOKONE_INDEX_URL:-https://packages.rookone.io/simple}"
ROOKONE_INSTALL_VERSION="${ROOKONE_INSTALL_VERSION:-}"
_rk_original_path=$PATH
_rk_mcp_registered=0; _rk_install_channel=0
_rk_pipx_loaded=0; _rk_pipx_available=0; _rk_pipx_list=""
_rk_path_activation_required=0; _rk_path_activation_dir=""

usage() {
    cat <<'EOF'
Install the RookOne client stack with uv tool.

Usage:
    sh install/get/install.sh [--version VERSION] [--no-statusline]
    curl -LsSf https://get.rookone.io | sh
    curl -LsSf https://get.rookone.io | sh -s -- --version VERSION

Options:
    --version VERSION    Install one exact release (for recovery or rollback).
    --no-statusline      Do not configure the Claude Code status line.
    -h, --help           Show this help without changing the machine.

The installer migrates old pipx copies and verifies every installed RookOne
package at one version. The channel tool is kept in lockstep when Claude Code
is present or a prior channel installation exists.
It preserves ~/.rookone and the configured secret vault.
EOF
}

parse_args() {
    while [ "$#" -gt 0 ]; do
        case "$1" in
            -h|--help)
                usage
                return 2
                ;;
            --version)
                if [ "$#" -lt 2 ] || [ -z "$2" ]; then
                    log_error "--version requires a value"
                    return 1
                fi
                case "$2" in
                    -*) log_error "--version requires a value"; return 1 ;;
                esac
                ROOKONE_INSTALL_VERSION=$2
                shift 2
                ;;
            --no-statusline)
                ROOKONE_NO_STATUSLINE=1
                shift
                ;;
            *)
                log_error "unknown option: $1"
                log_error "Run with --help to see supported options."
                return 1
                ;;
        esac
    done

    ROOKONE_INSTALL_VERSION=${ROOKONE_INSTALL_VERSION#v}
    if [ -n "$ROOKONE_INSTALL_VERSION" ] \
        && ! printf '%s\n' "$ROOKONE_INSTALL_VERSION" \
            | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+([A-Za-z0-9.-]+)?$'; then
        log_error "invalid release version: $ROOKONE_INSTALL_VERSION"
        return 1
    fi
}

_rk_uv_tmp=""; _rk_preflight=""
_rk_cleanup() {
    if [ -n "$_rk_uv_tmp" ]; then
        rm -f "$_rk_uv_tmp"
    fi
    if [ -n "$_rk_preflight" ] && [ -d "$_rk_preflight" ]; then
        rm -r "$_rk_preflight"
    fi
}
trap _rk_cleanup EXIT
trap 'exit 129' HUP; trap 'exit 130' INT; trap 'exit 143' TERM

RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'

log_info()    { printf '%b==>%b %s\n' "$BLUE"   "$NC" "$1" >&2; }
log_success() { printf '%b==>%b %s\n' "$GREEN"  "$NC" "$1" >&2; }
log_warning() { printf '%b==>%b %s\n' "$YELLOW" "$NC" "$1" >&2; }
log_error()   { printf '%bError:%b %s\n' "$RED"  "$NC" "$1" >&2; }

_rk_sha256() {
    if command -v sha256sum >/dev/null 2>&1; then
        sha256sum "$1" | awk '{print $1}'
    elif command -v shasum >/dev/null 2>&1; then
        shasum -a 256 "$1" | awk '{print $1}'
    else
        log_error "no sha256 tool found (need sha256sum or shasum)"
        return 1
    fi
}

_rk_verify_sha256() {
    _rk_f="$1"; _rk_want="$2"
    _rk_have=$(_rk_sha256 "$_rk_f") || return 1
    if [ "$_rk_have" != "$_rk_want" ]; then
        log_error "integrity check failed: expected $_rk_want, got $_rk_have"
        return 1
    fi
    return 0
}

_rk_require_https() {
    case "$1" in
        https://*) return 0 ;;
        http://localhost:*|http://localhost/*|http://127.0.0.1:*|http://127.0.0.1/*) return 0 ;;
        *) log_error "ROOKONE_INDEX_URL must use HTTPS (got: $(_rk_safe_url "$1"))"; return 1 ;;
    esac
}

_rk_validate_index_url() {
    case "$1" in
        *\?*|*\#*)
            log_error "ROOKONE_INDEX_URL must not include a query or fragment."
            return 1
            ;;
    esac
    _rk_require_https "$1"
}

_rk_safe_url() {
    _rk_url=$1
    case "$_rk_url" in
        *://*@*)
            _rk_scheme=${_rk_url%%://*}
            _rk_url="${_rk_scheme}://[redacted]@${_rk_url#*@}"
            ;;
    esac
    case "$_rk_url" in
        *\?*) printf '%s?[redacted]\n' "${_rk_url%%\?*}" ;;
        *) printf '%s\n' "$_rk_url" ;;
    esac
}

_rk_uv_resolve() {
    (
        unset UV_FIND_LINKS UV_INDEX UV_DEFAULT_INDEX UV_INDEX_URL
        unset UV_EXTRA_INDEX_URL UV_CONSTRAINT UV_OVERRIDE UV_BUILD_CONSTRAINT
        unset UV_NO_INDEX UV_INSECURE_HOST UV_CONFIG_FILE UV_PROJECT UV_WORKING_DIR
        unset UV_OFFLINE UV_EXCLUDE_NEWER UV_EXCLUDE UV_PRERELEASE UV_RESOLUTION
        unset UV_INDEX_STRATEGY UV_NO_CONFIG UV_NO_SOURCES
        uv "$@"
    )
}

_rk_install_tool() {
    _rk_spec=$1
    shift
    _rk_uv_resolve tool install "$_rk_spec" \
        --index "$ROOKONE_INDEX_URL" \
        --index-strategy first-index \
        --no-config \
        --no-sources \
        --no-build-package sqlcipher3 \
        --no-build-package sqlcipher3-binary \
        "$@"
}

assert_supported_platform() {
    _rk_os=$(uname -s 2>/dev/null || printf unknown)
    _rk_arch=$(uname -m 2>/dev/null || printf unknown)
    case "${_rk_os}:${_rk_arch}" in
        Darwin:*)
            if ! command -v sqlcipher >/dev/null 2>&1; then
                log_error "SQLCipher is required for RookOne's local message database but was not found on PATH."
                log_error "Install SQLCipher first, then re-run the installer:"
                log_error "  brew install sqlcipher"
                log_error "Install is stopped before any local database can be created."
                exit 1
            fi
            ;;
    esac
}

ensure_uv() {
    if command -v uv >/dev/null 2>&1; then
        log_info "uv already installed ($(uv --version 2>/dev/null))"
        return
    fi

    log_info "uv not found — bootstrapping from astral.sh"

    if ! command -v curl >/dev/null 2>&1; then
        log_error "curl is required to bootstrap uv but was not found on PATH."
        log_error "Install curl first, then re-run the installer."
        exit 1
    fi

    _rk_uv_tmp=$(mktemp /tmp/uv-install.XXXXXX)

    UV_VERSION="0.11.19"
    UV_INSTALLER_SHA256="ef8cf0575d37cf3c72e05f153dd72a845a87a7bb9be86184d5fe931b8c426250"

    if ! curl -LsSf "https://github.com/astral-sh/uv/releases/download/${UV_VERSION}/uv-installer.sh" -o "$_rk_uv_tmp"; then
        log_error "Failed to download the pinned uv installer (v${UV_VERSION})."
        exit 1
    fi

    if ! _rk_verify_sha256 "$_rk_uv_tmp" "$UV_INSTALLER_SHA256"; then
        log_error "uv installer integrity check failed — refusing to execute. Aborting."
        exit 1
    fi
    log_info "uv installer v${UV_VERSION} verified (sha256 ok)"

    sh "$_rk_uv_tmp" </dev/null
    rm -f "$_rk_uv_tmp"
    _rk_uv_tmp=""

    export PATH="$HOME/.local/bin:$PATH"

    if ! command -v uv >/dev/null 2>&1; then
        log_error "uv was installed but is not on PATH."
        log_error "Add ~/.local/bin to your PATH, then re-run the installer."
        exit 1
    fi

    log_success "uv installed ($(uv --version 2>/dev/null))"
}

detect_existing_rookone() {
    load_pipx_state || return 1
    if [ "$_rk_pipx_available" = "1" ]; then
        _rk_pipx_ver=$(printf '%s\n' "$_rk_pipx_list" | awk '$1=="rookone"{print $2; exit}')
        if [ -z "${_rk_pipx_ver:-}" ]; then
            _rk_pipx_ver=$(printf '%s\n' "$_rk_pipx_list" | awk '$1=="rookone-channel"{print $2; exit}')
        fi
        if [ -n "${_rk_pipx_ver:-}" ]; then
            printf 'pipx %s\n' "$_rk_pipx_ver"
            return 0
        fi
    fi

    if command -v uv >/dev/null 2>&1; then
        _rk_uv_ver=$(_rk_uv_resolve tool list 2>/dev/null | awk '$1=="rookone"{print $2; exit}')
        if [ -n "${_rk_uv_ver:-}" ]; then
            printf 'uv %s\n' "$_rk_uv_ver"
            return 0
        fi
    fi

    _rk_stale=$(command -v rookone 2>/dev/null || true)
    if [ -n "$_rk_stale" ]; then
        printf 'stale %s\n' "$_rk_stale"
        return 0
    fi

    printf 'fresh\n'
}

load_pipx_state() {
    [ "$_rk_pipx_loaded" = "0" ] || return 0
    if ! command -v pipx >/dev/null 2>&1; then
        _rk_pipx_loaded=1
        return 0
    fi
    if ! _rk_pipx_list=$(pipx list --short 2>/dev/null); then
        log_error "Could not verify pipx ownership; no installation was changed."
        return 1
    fi
    _rk_pipx_available=1
    _rk_pipx_loaded=1
}

print_identity_warning() {
    log_warning "Your identity spans ~/.rookone metadata and the configured secret vault."
    log_warning "Upgrades preserve both; use 'rookone deregister <agent>' for intentional removal."
    log_warning "Deleting only ~/.rookone can orphan vault entries and does not deregister the agent."
}

announce_install_plan() {
    _rk_case="$1"
    _rk_detail="${2:-}"
    case "$_rk_case" in
        fresh)
            log_info "No existing rookone found — installing fresh."
            ;;
        uv)
            log_info "Existing rookone ${_rk_detail} found (uv tool). Converging the client stack."
            print_identity_warning
            ;;
        pipx)
            log_warning "Existing rookone ${_rk_detail} found, installed via pipx."
            log_warning "Migrating to uv tool: proving the replacement before removing pipx…"
            print_identity_warning
            ;;
        stale)
            log_warning "Found an existing 'rookone' at ${_rk_detail} not managed by uv or pipx."
            log_warning "Installing a uv-managed copy. If the old one shadows it, remove it: rm ${_rk_detail}"
            print_identity_warning
            ;;
    esac
}

remove_legacy_pipx_tools() {
    load_pipx_state || return 1
    [ "$_rk_pipx_available" = "1" ] || return 0
    for _rk_package in rookone rookone-channel; do
        if printf '%s\n' "$_rk_pipx_list" | awk '{print $1}' | grep -qx "$_rk_package"; then
            log_info "Removing legacy pipx tool: $_rk_package"
            pipx uninstall "$_rk_package" >/dev/null 2>&1 || {
                log_error "Could not remove the legacy pipx $_rk_package tool."
                return 1
            }
        fi
    done
    if ! _rk_pipx_list=$(pipx list --short 2>/dev/null); then
        log_error "Could not verify pipx ownership after uninstall; refusing mixed ownership."
        return 1
    fi
    for _rk_package in rookone rookone-channel; do
        if printf '%s\n' "$_rk_pipx_list" | awk '{print $1}' | grep -qx "$_rk_package"; then
            log_error "pipx still reports $_rk_package after uninstall; refusing mixed ownership."
            return 1
        fi
    done
}

channel_is_wanted() {
    command -v claude >/dev/null 2>&1 && return 0
    command -v rookone-channel >/dev/null 2>&1 && return 0
    if _rk_uv_resolve tool list 2>/dev/null | awk '{print $1}' | grep -qx rookone-channel; then
        return 0
    fi
    load_pipx_state || return 1
    if [ "$_rk_pipx_available" = "1" ] \
        && printf '%s\n' "$_rk_pipx_list" | awk '{print $1}' | grep -qx rookone-channel; then
        return 0
    fi
    return 1
}

stage_pipx_migration() {
    load_pipx_state || return 1
    [ "$_rk_pipx_available" = "1" ] || return 0
    printf '%s\n' "$_rk_pipx_list" | awk '{print $1}' \
        | grep -Eq '^(rookone|rookone-channel)$' || return 0

    _rk_preflight=$(mktemp -d /tmp/rookone-install-preflight.XXXXXX)
    if ! (
        UV_TOOL_DIR="$_rk_preflight/tools"
        UV_TOOL_BIN_DIR="$_rk_preflight/bin"
        export UV_TOOL_DIR UV_TOOL_BIN_DIR
        _rk_install_tool "$_rk_rookone_spec"
    ) >/dev/null 2>&1; then
        rm -r "$_rk_preflight"
        _rk_preflight=""
        log_error "The requested RookOne release is not installable from the configured index."
        log_error "The existing pipx tools were preserved; check the index and retry."
        return 1
    fi
    _rk_staged_version=$(UV_TOOL_DIR="$_rk_preflight/tools" _rk_uv_resolve tool list 2>/dev/null \
        | awk '$1 == "rookone" {sub(/^v/, "", $2); print $2; exit}')
    if [ -z "$_rk_staged_version" ]; then
        rm -r "$_rk_preflight"
        _rk_preflight=""
        log_error "The staged RookOne tool did not report a version; pipx was preserved."
        return 1
    fi
    _rk_rookone_spec="rookone==$_rk_staged_version"
    if [ "$_rk_install_channel" = "1" ] && ! (
        UV_TOOL_DIR="$_rk_preflight/tools"
        UV_TOOL_BIN_DIR="$_rk_preflight/bin"
        export UV_TOOL_DIR UV_TOOL_BIN_DIR
        _rk_install_tool "rookone-channel==$_rk_staged_version"
    ) >/dev/null 2>&1; then
        rm -r "$_rk_preflight"
        _rk_preflight=""
        log_error "rookone-channel $_rk_staged_version is not installable; pipx was preserved."
        return 1
    fi
    rm -r "$_rk_preflight"
    _rk_preflight=""
}

pipx_owns_entry() {
    _rk_command=$1; _rk_owner=$2; _rk_entry=$3
    [ "$_rk_pipx_available" = "1" ] || return 1
    printf '%s\n' "$_rk_pipx_list" | awk '{print $1}' | grep -qx "$_rk_owner" || return 1
    [ -L "$_rk_entry" ] || return 1
    _rk_pipx_home=$(pipx environment --value PIPX_HOME 2>/dev/null) || return 1
    _rk_pipx_bin=$(pipx environment --value PIPX_BIN_DIR 2>/dev/null) || return 1
    _rk_pipx_target=$(readlink "$_rk_entry") || return 1
    [ "$_rk_entry" = "${_rk_pipx_bin%/}/$_rk_command" ] \
        && [ "$_rk_pipx_target" = "${_rk_pipx_home%/}/venvs/$_rk_owner/bin/$_rk_command" ]
}

uv_owns_entry() {
    _rk_command=$1; _rk_owner=$2; _rk_entry=$3; _rk_uv_tools=$4
    printf '%s\n' "$_rk_uv_tools" | awk '{print $1}' | grep -qx "$_rk_owner" || return 1
    [ -L "$_rk_entry" ] || return 1
    _rk_uv_tool_dir=$(_rk_uv_resolve tool dir 2>/dev/null) || return 1
    [ -n "$_rk_uv_tool_dir" ] || return 1
    _rk_uv_target=$(readlink "$_rk_entry") || return 1
    case "$_rk_uv_target" in
        /*) ;;
        *) _rk_uv_target="${_rk_entry%/*}/$_rk_uv_target" ;;
    esac
    _rk_uv_target_name=${_rk_uv_target##*/}
    _rk_uv_target_parent=${_rk_uv_target%/*}
    _rk_uv_expected_parent="${_rk_uv_tool_dir%/}/$_rk_owner/bin"
    [ "$_rk_uv_target_parent" != "$_rk_uv_target" ] || return 1
    _rk_uv_target_parent=$(CDPATH='' cd -P "$_rk_uv_target_parent" 2>/dev/null && pwd -P) \
        || return 1
    _rk_uv_expected_parent=$(CDPATH='' cd -P "$_rk_uv_expected_parent" 2>/dev/null && pwd -P) \
        || return 1
    [ "$_rk_uv_target_parent/$_rk_uv_target_name" \
        = "$_rk_uv_expected_parent/$_rk_command" ]
}

guard_uv_bin_entry() {
    _rk_command=$1; _rk_owner=$2
    if ! _rk_tool_bin=$(_rk_uv_resolve tool dir --bin 2>/dev/null) \
        || [ -z "$_rk_tool_bin" ]; then
        log_error "Could not locate the uv tool bin directory; no command was changed."
        return 1
    fi
    _rk_entry="$_rk_tool_bin/$_rk_command"
    [ ! -e "$_rk_entry" ] && [ ! -L "$_rk_entry" ] && return 0
    if ! _rk_uv_tools=$(_rk_uv_resolve tool list 2>/dev/null); then
        log_error "Could not verify uv ownership of $_rk_entry; it was preserved."
        return 1
    fi
    uv_owns_entry "$_rk_command" "$_rk_owner" "$_rk_entry" "$_rk_uv_tools" && return 0
    pipx_owns_entry "$_rk_command" "$_rk_owner" "$_rk_entry" && return 0
    log_error "Unmanaged command exists at $_rk_entry; it was preserved."
    log_error "Move it aside, then re-run the installer."
    return 1
}

guard_install_entries() {
    for _rk_command in \
        rookone rookone-mirror rookone-codex-hook rookone-codex-collector; do
        guard_uv_bin_entry "$_rk_command" rookone || return 1
    done
    if [ "$_rk_install_channel" = "1" ]; then
        guard_uv_bin_entry rookone-channel rookone-channel || return 1
    fi
}

pid_file_is_live() {
    [ -r "$1" ] || return 1
    _rk_pid=""
    IFS= read -r _rk_pid < "$1" || [ -n "$_rk_pid" ] || return 1
    case "$_rk_pid" in
        ''|*[!0-9]*) return 1 ;;
    esac
    [ "$_rk_pid" -gt 1 ] && kill -0 "$_rk_pid" 2>/dev/null
}

local_stack_is_running() {
    _rk_home=${ROOKONE_HOME:-"$HOME/.rookone"}
    pid_file_is_live "$_rk_home/server.pid" \
        || pid_file_is_live "$_rk_home/leaf/nats-server.pid" \
        || pid_file_is_live "$_rk_home/inbox-mirror.pid"
}

install_rookone() {
    _rk_validate_index_url "$ROOKONE_INDEX_URL" || return 1
    _rk_plan=$(detect_existing_rookone) || return 1
    _rk_case=${_rk_plan%% *}
    _rk_detail=${_rk_plan#* }
    [ "$_rk_detail" = "$_rk_plan" ] && _rk_detail=""

    announce_install_plan "$_rk_case" "$_rk_detail"
    _rk_stack_was_running=0
    if local_stack_is_running; then
        _rk_stack_was_running=1
    fi

    _rk_rookone_spec=rookone
    if [ -n "$ROOKONE_INSTALL_VERSION" ]; then
        _rk_rookone_spec="rookone==${ROOKONE_INSTALL_VERSION}"
    fi
    guard_install_entries || return 1
    stage_pipx_migration || return 1
    remove_legacy_pipx_tools || return 1
    guard_install_entries || return 1

    log_info "Installing ${_rk_rookone_spec} from $(_rk_safe_url "$ROOKONE_INDEX_URL")"

    # first-index prevents dependency confusion; --force converges old owners.
    _rk_force=""
    case "$_rk_case" in uv|pipx|stale) _rk_force="--force" ;; esac

    # SQLCipher must be a published wheel, never a machine-specific source build.
    # shellcheck disable=SC2086  # _rk_force is intentionally word-split (empty or --force)
    if ! _rk_install_tool "$_rk_rookone_spec" $_rk_force; then
        log_error "Could not install $_rk_rookone_spec; no post-install setup was changed."
        return 1
    fi

    _rk_tool_bin=$(_rk_uv_resolve tool dir --bin 2>/dev/null || printf '%s/.local/bin' "$HOME")
    export PATH="${_rk_tool_bin}:$PATH"

    if [ "$_rk_case" != "fresh" ] || [ "$_rk_stack_was_running" = "1" ]; then
        _rk_candidate="${_rk_tool_bin}/rookone"
        if [ ! -x "$_rk_candidate" ]; then
            log_error "Installed rookone candidate was not found at ${_rk_candidate}."
            return 1
        fi
        log_info "Stopping the existing local stack with the installed candidate"
        if ! "$_rk_candidate" stop; then
            log_error "The installed candidate could not prove a complete shutdown."
            log_error "Post-install setup was aborted; lifecycle evidence was retained."
            return 1
        fi
        if [ "$_rk_stack_was_running" = "1" ]; then
            log_info "Restarting the local stack with the installed version"
            if ! "$_rk_candidate" start --background; then
                log_warning "The cloud bridge could not be restored; recovering local messaging."
                if ! "$_rk_candidate" stop; then
                    log_error "The failed bridge start could not be stopped safely."
                    log_error "Resolve it before retrying the installation."
                    return 1
                fi
                if ! "$_rk_candidate" start --local-only --background; then
                    log_error "The installed candidate could not restore either local stack mode."
                    log_error "Recover with: rookone start --local-only --background"
                    return 1
                fi
                log_warning "Local messaging is running, but cross-machine delivery is unavailable."
                log_warning "After fixing credentials, run: rookone start --as NAME --background"
            fi
        fi
    fi
}

installed_rookone_version() {
    _rk_uv_resolve tool list 2>/dev/null | awk '
        $1 == "rookone" {
            version = $2
            sub(/^v/, "", version)
            print version
            exit
        }
    '
}

install_channel() {
    _rk_rookone_version=$(installed_rookone_version)
    if [ -z "$_rk_rookone_version" ]; then
        log_error "Could not determine the installed rookone version; refusing channel install."
        return 1
    fi
    _rk_channel_spec="rookone-channel==${_rk_rookone_version}"

    log_info "Installing rookone-channel ${_rk_rookone_version} (CLI lockstep)"

    if ! _rk_install_tool "$_rk_channel_spec" --force; then
        log_error "Could not install matching rookone-channel ${_rk_rookone_version}."
        log_error "Installation stopped to avoid running a stale channel against the CLI."
        return 1
    fi

    _rk_tool_bin=$(_rk_uv_resolve tool dir --bin 2>/dev/null || printf '%s/.local/bin' "$HOME")
    export PATH="${_rk_tool_bin}:$PATH"

}

verify_installation() {
    _rk_expect_channel=$1
    _rk_expected=$(installed_rookone_version)
    [ -n "$_rk_expected" ] || { log_error "uv does not report an installed rookone tool."; return 1; }
    if [ -n "$ROOKONE_INSTALL_VERSION" ] && [ "$_rk_expected" != "$ROOKONE_INSTALL_VERSION" ]; then
        log_error "uv installed rookone $_rk_expected, expected $ROOKONE_INSTALL_VERSION."
        return 1
    fi

    _rk_tool_bin=$(_rk_uv_resolve tool dir --bin 2>/dev/null || true)
    [ -n "$_rk_tool_bin" ] || { log_error "Could not locate the uv tool bin directory."; return 1; }
    export PATH="${_rk_tool_bin}:$PATH"
    _rk_active=$(command -v rookone 2>/dev/null || true)
    if [ "$_rk_active" != "${_rk_tool_bin}/rookone" ]; then
        log_error "Active rookone is ${_rk_active:-missing}, not the uv-owned command."
        return 1
    fi
    _rk_reported=$("$_rk_active" --version 2>/dev/null | awk '{print $NF; exit}')
    if [ "${_rk_reported#v}" != "$_rk_expected" ]; then
        log_error "Active rookone reports ${_rk_reported:-no version}, expected $_rk_expected."
        return 1
    fi

    _rk_tool_root=$(_rk_uv_resolve tool dir 2>/dev/null || true)
    [ -n "$_rk_tool_root" ] || { log_error "Could not locate uv tool environments."; return 1; }
    "$_rk_tool_root/rookone/bin/python" -c \
        'import importlib.metadata as m,sys; e=sys.argv[1]; bad=[f"{n}={m.version(n)}" for n in sys.argv[2:] if m.version(n)!=e]; raise SystemExit("version mismatch: "+", ".join(bad) if bad else 0)' \
        "$_rk_expected" rookone rookone-core rookone-sdk rookone-shared || return 1

    verify_invoking_path rookone "${_rk_tool_bin}/rookone" || return 1

    if [ "$_rk_expect_channel" = "1" ]; then
        _rk_channel_version=$(_rk_uv_resolve tool list 2>/dev/null | awk '$1 == "rookone-channel" {sub(/^v/, "", $2); print $2; exit}')
        [ "$_rk_channel_version" = "$_rk_expected" ] || {
            log_error "rookone-channel ${_rk_channel_version:-missing} does not match rookone $_rk_expected."
            return 1
        }
        _rk_active=$(command -v rookone-channel 2>/dev/null || true)
        if [ "$_rk_active" != "${_rk_tool_bin}/rookone-channel" ]; then
            log_error "Active rookone-channel is ${_rk_active:-missing}, not the uv-owned command."
            return 1
        fi
        _rk_reported=$("$_rk_active" --version 2>/dev/null | awk '{print $NF; exit}')
        [ "${_rk_reported#v}" = "$_rk_expected" ] || {
            log_error "Active rookone-channel reports ${_rk_reported:-no version}, expected $_rk_expected."
            return 1
        }
        "$_rk_tool_root/rookone-channel/bin/python" -c \
            'import importlib.metadata as m,sys; e=sys.argv[1]; bad=[f"{n}={m.version(n)}" for n in sys.argv[2:] if m.version(n)!=e]; raise SystemExit("version mismatch: "+", ".join(bad) if bad else 0)' \
            "$_rk_expected" rookone-channel rookone-core rookone-sdk rookone-shared || return 1
        verify_invoking_path rookone-channel "${_rk_tool_bin}/rookone-channel" || return 1
        log_success "verified uv-owned $_rk_expected lockstep across all five RookOne packages"
    else
        log_success "verified uv-owned $_rk_expected CLI, core, SDK, and shared packages"
    fi
}

verify_invoking_path() {
    _rk_name=$1
    _rk_expected_path=$2
    _rk_parent_active=$(
        PATH=$_rk_original_path
        export PATH
        command -v "$_rk_name" 2>/dev/null || true
    )
    if [ "$_rk_parent_active" = "$_rk_expected_path" ]; then
        return 0
    fi
    if [ -n "$_rk_parent_active" ]; then
        log_error "After this installer exits, PATH resolves $_rk_name to $_rk_parent_active."
        log_error "That file was preserved. Move it aside or put $(dirname "$_rk_expected_path") earlier in PATH, then retry."
        return 1
    else
        _rk_path_activation_required=1
        _rk_path_activation_dir=$(dirname "$_rk_expected_path")
        log_warning "Installed $_rk_name is not on the PATH inherited by this installer."
    fi
    return 0
}

setup_claude_mcp() {
    if ! command -v claude >/dev/null 2>&1; then
        log_info "Claude Code not detected — skipping MCP setup."
        return 0
    fi

    _rk_uv_bin=$(_rk_uv_resolve tool dir --bin 2>/dev/null || printf '%s/.local/bin' "$HOME")
    _rk_uv_channel="${_rk_uv_bin}/rookone-channel"

    if claude mcp add --scope user rookone -- rookone-channel >/dev/null 2>&1; then
        _rk_mcp_registered=1
        log_success "Registered the 'rookone' MCP server in Claude Code (user scope)."
        log_success "It resolves from any directory — launch with:"
        log_success "  claude --dangerously-load-development-channels=server:rookone"
    elif _rk_mcp_output=$(claude mcp get rookone 2>/dev/null); then
        _rk_mcp_command=$(printf '%s\n' "$_rk_mcp_output" | awk '
            /^[[:space:]]*Command:[[:space:]]*/ {
                sub(/^[[:space:]]*Command:[[:space:]]*/, "")
                print
                exit
            }
        ')
        _rk_mcp_path=$(printf '%s\n' "$_rk_mcp_output" | awk '
            /^[[:space:]]*Environment:[[:space:]]*$/ { environment = 1; next }
            environment && /^[[:space:]]*PATH=/ {
                sub(/^[[:space:]]*PATH=/, "")
                print
                exit
            }
        ')
        case "$_rk_mcp_command" in
            rookone-channel)
                if [ -z "$_rk_mcp_path" ]; then
                    _rk_mcp_registered=1
                    log_info "The existing 'rookone' MCP server is already registered with the verified generic channel command."
                else
                    log_warning "The existing 'rookone' MCP registration pins PATH=$_rk_mcp_path; left unchanged and unverified."
                    log_warning "To use this release, run:"
                    log_warning "  claude mcp remove rookone -s user"
                    log_warning "  claude mcp add --scope user rookone -- $_rk_uv_channel"
                fi
                ;;
            "$_rk_uv_channel")
                _rk_mcp_registered=1
                log_info "The existing 'rookone' MCP registration uses the verified uv-owned channel command."
                ;;
            *)
                log_warning "The existing 'rookone' MCP registration uses ${_rk_mcp_command:-an unknown command}; left unchanged and unverified."
                log_warning "To use this release, run:"
                log_warning "  claude mcp remove rookone -s user"
                log_warning "  claude mcp add --scope user rookone -- $_rk_uv_channel"
                ;;
        esac
    else
        log_warning "Could not register the rookone MCP server automatically. Run:"
        log_warning "  claude mcp add --scope user rookone -- rookone-channel"
    fi
}

# `codex mcp add` silently replaces an entry of the same name, so look first
# and never overwrite a registration the user made.
setup_codex_mcp() {
    if ! command -v codex >/dev/null 2>&1; then
        log_info "Codex not detected — skipping Codex MCP setup."
        return 0
    fi
    if codex mcp get rookone >/dev/null 2>&1; then
        log_info "Codex already has an MCP server named 'rookone'; left unchanged."
    elif codex mcp add rookone -- rookone mcp >/dev/null 2>&1; then
        log_success "Registered the 'rookone' MCP server in Codex (rookone mcp)."
    else
        log_warning "Could not register the rookone MCP server in Codex. Run:"
        log_warning "  codex mcp add rookone -- rookone mcp"
    fi
}

setup_claude_statusline() {
    if [ -n "${ROOKONE_NO_STATUSLINE:-}" ]; then
        log_info "ROOKONE_NO_STATUSLINE set — skipping status line setup."
        return 0
    fi

    if ! command -v claude >/dev/null 2>&1; then
        log_info "Claude Code not detected — skipping status line setup."
        return 0
    fi

    _rk_bin=$(command -v rookone 2>/dev/null || true)
    if [ -z "$_rk_bin" ]; then
        log_warning "rookone not on PATH — skipping status line setup."
        return 0
    fi

    if rookone claude install-statusline --binary "$_rk_bin" >/dev/null 2>&1; then
        log_success "Configured the Claude Code status line (RookOne agent state)."
        log_info "  Opt out next time with ROOKONE_NO_STATUSLINE=1."
        log_info "  Remove it with: rookone claude uninstall-statusline"
    else
        log_warning "Could not configure the Claude Code status line (skipped)."
        log_warning "  Set it up later with: rookone claude install-statusline"
    fi
}

print_next_steps() {
    printf '\n'
    log_success "rookone installed successfully!"
    printf '\n'
    if [ "$_rk_path_activation_required" = "1" ]; then
        _rk_quoted_path=$(printf '%s' "$_rk_path_activation_dir" | sed 's/[\\"]/\\&/g; s/[$`]/\\&/g')
        printf 'Current terminal: add the RookOne install directory to PATH before running it:\n'
        printf '  export PATH="%s:$PATH"\n' "$_rk_quoted_path"
        printf 'For future terminals, run `uv tool update-shell` after the current-shell command, then open a new terminal.\n'
        printf '\n'
    fi
    printf 'Getting started:\n'
    printf '  rookone --help          show all commands\n'
    printf '  rookone register        create your agent\n'
    printf '  rookone whoami          confirm your identity\n'
    printf '  rookone inbox           check for new messages\n'
    printf '  rookone send            send a direct message\n'
    printf '\n'
    printf 'Claude Code (MCP):\n'
    if [ "$_rk_mcp_registered" = "1" ]; then
        printf '  The "rookone" MCP server is registered (user scope) — works from any\n'
        printf '  directory. Start a session with real-time messaging:\n'
        printf '    claude --dangerously-load-development-channels=server:rookone\n'
    elif command -v claude >/dev/null 2>&1; then
        printf '  Automatic MCP registration did not complete. Run:\n'
        printf '    claude mcp add --scope user rookone -- rookone-channel\n'
    else
        printf '  Claude Code was not detected. Install it, then re-run this installer to\n'
        printf '  install and register the optional channel integration.\n'
    fi
    printf '\n'
    printf 'Docs: https://docs.rookone.io\n'
    printf '\n'
    printf 'Your identity now lives in ~/.rookone — keep that directory to keep your agent.\n'
    printf '\n'
}

main() {
    if parse_args "$@"; then
        :
    else
        _rk_parse_status=$?
        [ "$_rk_parse_status" -eq 2 ] && return 0
        return "$_rk_parse_status"
    fi
    _rk_validate_index_url "$ROOKONE_INDEX_URL" || return 1
    log_info "RookOne installer starting"
    assert_supported_platform
    ensure_uv
    load_pipx_state || return 1
    _rk_install_channel=0
    channel_is_wanted && _rk_install_channel=1
    install_rookone || return 1
    if [ "$_rk_install_channel" = "1" ]; then
        install_channel || return 1
    fi
    verify_installation "$_rk_install_channel" || return 1
    setup_claude_mcp || return 1
    setup_codex_mcp
    setup_claude_statusline
    print_next_steps
}

if [ -z "${ROOKONE_INSTALL_LIB:-}" ]; then
    main "$@"
fi
