#!/bin/bash

# Neocortex - Instalador Unix
# Development Orchestrator

# Versao do instalador
VERSION="4.60.23"

# Flags
MIGRATION_DETECTED=false
MIGRATION_SOURCES=""
CODERABBIT_INSTALLED=false
LEGACY_ITEMS=""
LEGACY_WARNINGS=0
SELECTED_TARGETS=""
VALID_TARGETS="claude-code cursor vscode gemini gemini-cli codex antigravity opencode kimi openclaw"
# LOCAL_MODE removed (Epic 50): thin-client ALWAYS, zero IP on client
# SSoT: packages/client/src/constants.ts DEFAULT_SERVER_URL
NEOCORTEX_SERVER_URL="${NEOCORTEX_SERVER_URL:-$(printf '%s://%s.%s.%s' 'https' 'api' 'neocortex' 'sh')}"

# =============================================================================
# DETECCAO DE AMBIENTE
# =============================================================================

is_interactive() { [ -t 0 ] && [ -t 1 ]; }

supports_colors() { [ -t 1 ] && [ "$(tput colors 2>/dev/null || echo 0)" -ge 8 ]; }

supports_unicode() {
    case "${LANG:-}${LC_ALL:-}${LC_CTYPE:-}" in
        *UTF-8*|*utf-8*|*utf8*) return 0 ;;
        *) return 1 ;;
    esac
}

if [ -z "$HOME" ]; then
    HOME=$(getent passwd "$(whoami)" | cut -d: -f6 2>/dev/null || echo "/root")
    export HOME
fi

# =============================================================================
# PARSING DE ARGUMENTOS
# =============================================================================

AUTO_YES=false
DEBUG_MODE=false
SKIP_PROJECT_DIRS=false
CREATE_PROJECT=false
QUIET_MODE=false
NO_BANNER=false
CLEANUP_LEGACY=false
DRY_RUN=false

while [[ $# -gt 0 ]]; do
    case $1 in
        -y|--yes) AUTO_YES=true; shift ;;
        -d|--debug) DEBUG_MODE=true; shift ;;
        -s|--skip-project) SKIP_PROJECT_DIRS=true; shift ;;
        -q|--quiet) QUIET_MODE=true; shift ;;
        --dry-run) DRY_RUN=true; shift ;;
        --no-banner) NO_BANNER=true; shift ;;
        --cleanup-legacy) CLEANUP_LEGACY=true; shift ;;
        --targets=*) SELECTED_TARGETS="${1#--targets=}"; shift ;;
        --create-project) CREATE_PROJECT=true; shift ;;
        --local) warn "--local flag removed: thin-client mode is now mandatory (zero IP on client)"; shift ;;
        --server-url=*) NEOCORTEX_SERVER_URL="${1#--server-url=}"; shift ;;
        -h|--help)
            echo ""
            echo "  Neocortex Installer v${VERSION}"
            echo "  Development Orchestrator"
            echo ""
            echo "  Uso: npx @ornexus/neocortex [opcoes]"
            echo ""
            echo "  Opcoes:"
            echo "    -y, --yes                 Modo automatico (Claude Code only)"
            echo "    --targets=<lista>         Plataformas separadas por virgula"
            echo "    --create-project          Instalar estrutura no projeto"
            echo "    -s, --skip-project        Nao perguntar sobre projeto"
            echo "    -q, --quiet               Modo silencioso"
            echo "    --dry-run                 Mostra adapters/stubs resolvidos sem modificar arquivos"
            echo "    --cleanup-legacy          (Redundante) Limpeza agora e automatica"
            echo "    --local                   (Removido) Thin-client obrigatorio, zero IP no client"
            echo "    --server-url=<url>        Override avancado do endpoint remoto"
            echo "    -d, --debug               Modo debug"
            echo "    -h, --help                Mostra esta ajuda"
            echo ""
            echo "  Plataformas: claude-code, cursor, vscode, gemini, codex, antigravity, opencode, kimi, openclaw"
            exit 0
            ;;
        *) shift ;;
    esac
done

if ! is_interactive; then
    AUTO_YES=true
fi

# =============================================================================
# CORES E SIMBOLOS
# =============================================================================

if supports_colors && [ "$QUIET_MODE" = false ]; then
    RED='\033[0;31m'
    GREEN='\033[0;32m'
    YELLOW='\033[1;33m'
    BLUE='\033[0;34m'
    MAGENTA='\033[0;35m'
    CYAN='\033[0;36m'
    WHITE='\033[1;37m'
    GRAY='\033[0;90m'
    BOLD='\033[1m'
    DIM='\033[2m'
    NC='\033[0m'
else
    RED='' GREEN='' YELLOW='' BLUE='' MAGENTA='' CYAN=''
    WHITE='' GRAY='' BOLD='' DIM='' NC=''
fi

if supports_unicode; then
    SYM_OK="✓"
    SYM_FAIL="✗"
    SYM_WARN="!"
    SYM_ARROW="→"
    SYM_DOT="·"
    SYM_SPINNER_FRAMES=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
else
    SYM_OK="ok"
    SYM_FAIL="x"
    SYM_WARN="!"
    SYM_ARROW="->"
    SYM_DOT="."
    SYM_SPINNER_FRAMES=('|' '/' '-' '\')
fi

# =============================================================================
# FUNCOES DE LOG
# =============================================================================

# Step header: [1/5] Installing core...
step() {
    local num=$1 total=$2 msg=$3
    echo ""
    echo -e "  ${BOLD}${CYAN}[$num/$total]${NC} ${BOLD}$msg${NC}"
}

# Success line
ok() { echo -e "  ${GREEN}${SYM_OK}${NC} $1"; }

# Warning line
warn() { echo -e "  ${YELLOW}${SYM_WARN}${NC} $1"; }

# Error line
fail() { echo -e "  ${RED}${SYM_FAIL}${NC} $1"; }

# Info line
info() { echo -e "  ${DIM}${SYM_ARROW} $1${NC}"; }

# Public-safe diagnostic helpers. Installer diagnostics must stay bounded and must
# not print raw child output, private paths, tokens or vendor onboarding text.
redact_public_text() {
    local msg="${1:-}"
    if [ -n "${HOME:-}" ]; then msg="${msg//$HOME/~}"; fi
    if [ -n "${SOURCE_DIR:-}" ]; then msg="${msg//$SOURCE_DIR/<install-dir>}"; fi
    if [ -n "${SCRIPT_DIR:-}" ]; then msg="${msg//$SCRIPT_DIR/<install-dir>}"; fi
    if [ -n "${PWD:-}" ]; then msg="${msg//$PWD/<project-dir>}"; fi
    printf '%s' "$msg" | sed -E \
        -e 's#/home/[^[:space:]]+#<redacted-path>#g' \
        -e 's#/Users/[^[:space:]]+#<redacted-path>#g' \
        -e 's#/(tmp|media|mnt|workspace|workspaces)/[^[:space:]]+#<redacted-path>#g' \
        -e 's#C:\\Users\\[^[:space:]]+#<redacted-path>#g' \
        -e 's#[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}#<redacted-email>#g' \
        -e 's#(^|[^A-Za-z0-9])(NX|ghp|github_pat|ctx7sk|sk|pk)[_-][A-Za-z0-9_.-]{8,}#\1<redacted-token>#g'
}

sanitize_diag_token() {
    local value
    value="$(redact_public_text "${1:-unknown}")"
    value="$(printf '%s' "$value" | tr -c 'A-Za-z0-9_.:/-' '_')"
    value="${value%%_}"
    [ -n "$value" ] && printf '%s' "$value" || printf '%s' "unknown"
}

emit_installer_diagnostic() {
    local tool phase status reason_code code signal timed_out retry_attempt timeout_ms summary doc_path
    tool="$(sanitize_diag_token "${1:-neocortex}")"
    phase="$(sanitize_diag_token "${2:-installer}")"
    status="$(sanitize_diag_token "${3:-info}")"
    reason_code="$(sanitize_diag_token "${4:-P177_PUBLIC_SAFE_LOG_REDACTED}")"
    code="$(sanitize_diag_token "${5:-null}")"
    signal="$(sanitize_diag_token "${6:-null}")"
    timed_out="$(sanitize_diag_token "${7:-false}")"
    retry_attempt="$(sanitize_diag_token "${8:-0}")"
    timeout_ms="$(sanitize_diag_token "${9:-0}")"
    summary="$(sanitize_diag_token "${10:-public_safe_bounded_diagnostic}")"
    doc_path="$(sanitize_diag_token "${11:-docs/install/coderabbit-manual-setup.md}")"
    echo "  [neocortex-installer] tool=${tool} phase=${phase} status=${status} reasonCode=${reason_code} code=${code} signal=${signal} timedOut=${timed_out} retryAttempt=${retry_attempt} timeoutMs=${timeout_ms} docPath=${doc_path} redactionReasonCode=P177_PUBLIC_SAFE_LOG_REDACTED summary=${summary}"
}

# Debug line
debug() { [ "$DEBUG_MODE" = true ] && echo -e "  ${GRAY}[debug] $(redact_public_text "$1")${NC}"; }

get_effective_uid() {
    if [ -n "${NEOCORTEX_TEST_EFFECTIVE_UID:-}" ]; then
        printf '%s\n' "$NEOCORTEX_TEST_EFFECTIVE_UID"
        return 0
    fi
    if command -v id >/dev/null 2>&1; then
        id -u 2>/dev/null || true
        return 0
    fi
    printf '\n'
}

get_platform_name() {
    if [ -n "${NEOCORTEX_TEST_UNAME:-}" ]; then
        printf '%s\n' "$NEOCORTEX_TEST_UNAME"
        return 0
    fi
    uname -s 2>/dev/null || printf '\n'
}

is_linux_platform() {
    [ "$(get_platform_name)" = "Linux" ]
}

is_root_like_home() {
    case "${1:-}" in
        ""|"/"|"/root"|"/root/"*) return 0 ;;
        *) return 1 ;;
    esac
}

is_system_npm_prefix() {
    case "${1:-}" in
        "/usr"|"/usr/"*|"/usr/local"|"/usr/local/"*|"/opt"|"/opt/"*|"/root"|"/root/"*) return 0 ;;
        *) return 1 ;;
    esac
}

is_safe_nonroot_home() {
    local candidate="${1:-}"
    [ -n "$candidate" ] || return 1
    is_root_like_home "$candidate" && return 1
    case "$candidate" in
        *$'\n'*|*":"*) return 1 ;;
    esac
    return 0
}

resolve_sudo_user_home() {
    if [ -n "${NEOCORTEX_TEST_SUDO_HOME:-}" ]; then
        printf '%s\n' "$NEOCORTEX_TEST_SUDO_HOME"
        return 0
    fi
    [ -n "${SUDO_USER:-}" ] || return 1
    [ "${SUDO_USER:-}" != "root" ] || return 1
    if command -v getent >/dev/null 2>&1; then
        getent passwd "$SUDO_USER" 2>/dev/null | cut -d: -f6
        return 0
    fi
    return 1
}

warn_linux_root_home_context() {
    is_linux_platform || return 0

    local effective_uid sudo_context root_home system_prefix intended_home can_resolve_intended
    effective_uid="$(get_effective_uid)"
    sudo_context=false
    root_home=false
    system_prefix=false
    can_resolve_intended=false

    [ -n "${SUDO_USER:-}" ] && [ "${SUDO_USER:-}" != "root" ] && sudo_context=true
    is_root_like_home "${HOME:-}" && root_home=true
    is_system_npm_prefix "${npm_config_prefix:-}" && system_prefix=true

    intended_home="$(resolve_sudo_user_home 2>/dev/null || true)"
    if is_safe_nonroot_home "$intended_home"; then
        can_resolve_intended=true
    fi

    if [ "$effective_uid" = "0" ] || [ "$sudo_context" = true ] || [ "$root_home" = true ] || [ "$system_prefix" = true ]; then
        emit_installer_diagnostic "neocortex" "root-home-check" "warning" "P177_ROOT_HOME_DETECTED" "null" "null" "false" "0" "0" "root_home_context_detected" "docs/install/linux-global-install.md"
        warn "P177_ROOT_HOME_DETECTED: Linux root/sudo install may write Neocortex user config under an elevated HOME."
        info "Prefer a no-sudo npm user prefix or a Node version manager before retrying."
        if [ "$sudo_context" = true ] && [ "$can_resolve_intended" = true ]; then
            info "Intended-user HOME was detected but not printed; rerun without sudo or configure that user's npm prefix."
            if [ "${NEOCORTEX_USE_SUDO_USER_HOME:-}" = "1" ] && [ "$effective_uid" != "0" ]; then
                HOME="$intended_home"
                export HOME
                info "Using intended-user HOME because NEOCORTEX_USE_SUDO_USER_HOME=1 and the effective user is not root."
            fi
        else
            info "Intended-user HOME unresolved [TBD]; review docs/install/linux-global-install.md before writing user config."
        fi
    fi
}

is_neocortex_source_project() {
    local candidate_dir="$1"
    local package_file="$candidate_dir/package.json"

    [ -f "$package_file" ] || return 1
    grep -q '"name"[[:space:]]*:[[:space:]]*"@ornexus/neocortex"' "$package_file" 2>/dev/null || return 1
    [ -d "$candidate_dir/core" ] || return 1
    [ -d "$candidate_dir/packages/server" ] || return 1
    [ -d "$candidate_dir/targets-stubs" ] || return 1
    [ -f "$candidate_dir/install.sh" ] || return 1

    return 0
}

resolve_target_dir() {
    local target="$1"
    local stub_dir="$SOURCE_DIR/targets-stubs/$target"
    local dev_dir="$SOURCE_DIR/targets/$target"

    [ "$target" = "gemini" ] && stub_dir="$SOURCE_DIR/targets-stubs/gemini-cli" && dev_dir="$SOURCE_DIR/targets/gemini-cli"

    if [ -d "$stub_dir" ]; then
        printf '%s\n' "$stub_dir"
        return 0
    fi

    if is_neocortex_source_project "$SOURCE_DIR" && [ -d "$dev_dir" ]; then
        debug "Fallback dev targets/ para $target: $dev_dir"
        printf '%s\n' "$dev_dir"
        return 0
    fi

    warn "$target ${DIM}(adapter nao encontrado; paths tentados: $stub_dir, $dev_dir)${NC}"
    return 1
}

resolve_target_adapter_script() {
    local target="$1"
    local adapter_name="$target"
    [ "$target" = "gemini-cli" ] && adapter_name="gemini"

    local target_dir
    target_dir=$(resolve_target_dir "$target") || return 1
    local adapter_script="$target_dir/install-${adapter_name}.sh"
    if [ -f "$adapter_script" ]; then
        printf '%s\n' "$adapter_script"
        return 0
    fi
    warn "$target ${DIM}(adapter nao encontrado; path tentado: $adapter_script)${NC}"
    return 1
}

# Spinner for background tasks
run_with_spinner() {
    local msg="$1"
    shift
    "$@" &
    local pid=$!
    local i=0
    local len=${#SYM_SPINNER_FRAMES[@]}

    while kill -0 "$pid" 2>/dev/null; do
        printf "\r  ${CYAN}%s${NC} %s" "${SYM_SPINNER_FRAMES[$((i % len))]}" "$msg"
        i=$((i + 1))
        sleep 0.08
    done

    wait "$pid"
    local exit_code=$?

    if [ $exit_code -eq 0 ]; then
        printf "\r  ${GREEN}${SYM_OK}${NC} %s\n" "$msg"
    else
        printf "\r  ${RED}${SYM_FAIL}${NC} %s\n" "$msg"
    fi

    return $exit_code
}

# =============================================================================
# FUNCOES DE COPIA (silenciosas - sem log individual)
# =============================================================================

copy_file() {
    local src="$1" dest="$2"
    debug "Copiando: $src -> $dest"
    if [ -f "$src" ]; then
        managed_copy_file "$src" "$dest" "profile-$(basename "$src")" "$HOME"
    else
        debug "Arquivo nao encontrado: $src"
        return 1
    fi
}

managed_helper_path() {
    printf '%s\n' "$SOURCE_DIR/targets-stubs/lib/managed-config-merge.js"
}

managed_manifest_path() {
    local base_dir="$1"
    printf '%s\n' "$base_dir/.neocortex/managed-config-manifest.json"
}

managed_apply_policy() {
    local base_dir="${1:-$HOME}"
    local config_file="$base_dir/.neocortex/config.json"
    local helper manifest
    helper="$(managed_helper_path)"
    manifest="$(managed_manifest_path "$base_dir")"
    [ -f "$helper" ] && command -v node >/dev/null 2>&1 || {
        warn "Managed config helper indisponivel; policy preservada sem alteracao"
        return 1
    }
    local args=(policy "$config_file" "$manifest" "$base_dir")
    [ "$DRY_RUN" = true ] && args+=(--dry-run)
    node "$helper" "${args[@]}" >/dev/null
}

managed_validate_policy() {
    local base_dir="${1:-$HOME}"
    local helper manifest config_file
    helper="$(managed_helper_path)"
    manifest="$(managed_manifest_path "$base_dir")"
    config_file="$base_dir/.neocortex/config.json"
    [ -f "$helper" ] && command -v node >/dev/null 2>&1 || return 1
    node "$helper" policy "$config_file" "$manifest" "$base_dir" --dry-run >/dev/null 2>&1
}

managed_copy_file() {
    local src="$1" dest="$2" managed_id="$3" base_dir="${4:-$HOME}"
    local helper manifest target_dest
    helper="$(managed_helper_path)"
    manifest="$(managed_manifest_path "$base_dir")"
    [ -f "$src" ] || return 1
    [ -f "$helper" ] && command -v node >/dev/null 2>&1 || return 1
    if [ -d "$dest" ] || [[ "$dest" == */ ]]; then
        target_dest="${dest%/}/$(basename "$src")"
    else
        target_dest="$dest"
    fi
    local args=(profile "$src" "$target_dest" "$manifest" "$base_dir" "$managed_id" --adopt-legacy)
    local tier_value tier_label
    tier_value=$(grep '"tier"' "$HOME/.neocortex/config.json" 2>/dev/null | head -1 | sed 's/.*"tier"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
    case "$tier_value" in
        free) tier_label="Free" ;;
        pro) tier_label="Pro" ;;
        enterprise) tier_label="Enterprise" ;;
        *) tier_label="" ;;
    esac
    [ -n "$tier_label" ] && args+=("--tier=$tier_label")
    [ "$DRY_RUN" = true ] && args+=(--dry-run)
    node "$helper" "${args[@]}" >/dev/null
}

managed_reconcile_file() {
    local target_file="$1" base_dir="$2"
    local helper manifest
    helper="$(managed_helper_path)"
    manifest="$(managed_manifest_path "$base_dir")"
    [ -f "$helper" ] && [ -f "$target_file" ] && command -v node >/dev/null 2>&1 || return 1
    node "$helper" reconcile "$target_file" "$manifest" "$base_dir" >/dev/null 2>&1
}

copy_dir() {
    local src="$1" dest="$2"
    debug "Copiando dir: $src -> $dest"
    [ -d "$src" ] && cp -r "$src" "$dest" 2>/dev/null
}

# Patch description tier in any agent file (YAML frontmatter or Markdown H1)
# Usage: patch_description_tier <file>
# Reads tier from ~/.neocortex/config.json and replaces ANY tier label with actual tier.
# Supports: (Free) <-> (Pro) <-> (Enterprise) in any direction.
# Works on both frontmatter description and banner body lines.
patch_description_tier() {
    local target_file="$1"
    local config_file="${HOME}/.neocortex/config.json"

    [ -f "$target_file" ] || return 0
    [ -f "$config_file" ] || return 0
    local before_checksum after_checksum
    before_checksum=$(cksum "$target_file" 2>/dev/null)

    local tier
    tier=$(cat "$config_file" 2>/dev/null | grep -o '"tier"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)"$/\1/')
    [ -n "$tier" ] || return 0

    local tier_label
    case "$tier" in
        free)       tier_label="Free" ;;
        pro)        tier_label="Pro" ;;
        enterprise) tier_label="Enterprise" ;;
        *)          return 0 ;;
    esac

    # Replace ANY tier label with the correct one in non-banner lines
    # (frontmatter description, body text, H1 headers, etc.)
    # Each sed expression replaces one possible source tier with the target tier.
    # If source == target, sed is a no-op (replaces X with X). This is correct.
    sed -i.bak \
        -e "s/(Free)/(${tier_label})/g" \
        -e "s/(Pro)/(${tier_label})/g" \
        -e "s/(Enterprise)/(${tier_label})/g" \
        "$target_file" 2>/dev/null
    rm -f "${target_file}.bak" 2>/dev/null

    # Fix banner alignment: the OrNexus Team line has a fixed 62-char frame.
    # After replacing tier labels of different lengths, re-pad to maintain alignment.
    # Pattern: "│ ##   ###  ######  ##    OrNexus Team (Tier)<spaces>│"
    # Inner width must be exactly 60 chars (│ + 60 + │ = 62).
    if grep -q "OrNexus Team (${tier_label})" "$target_file" 2>/dev/null; then
        local prefix="│ ##   ###  ######  ##    "  # 25 chars inner prefix
        local content="OrNexus Team (${tier_label})"
        local content_len=${#content}
        local pad_len=$((60 - 25 - content_len))
        local padding=""
        local i
        for ((i=0; i<pad_len; i++)); do padding="${padding} "; done
        local new_line="${prefix}${content}${padding}│"
        # Replace the (possibly misaligned) banner line with the correctly padded one
        sed -i.bak -e "s|│ ##   ###  ######  ##    OrNexus Team ([^)]*).*│|${new_line}|" "$target_file" 2>/dev/null
        rm -f "${target_file}.bak" 2>/dev/null
    fi

    debug "Tier patched to (${tier_label}) in $(basename "$target_file")"
    after_checksum=$(cksum "$target_file" 2>/dev/null)
    if [ "$before_checksum" != "$after_checksum" ]; then
        managed_reconcile_file "$target_file" "$HOME" || \
            managed_reconcile_file "$target_file" "$PWD" || true
    fi
}

# =============================================================================
# DETECCAO DE VERSAO ANTIGA
# =============================================================================

detect_old_installation() {
    debug "Verificando instalacoes antigas..."

    local dest_dir="${HOME}/.claude/agents/neocortex"

    if [ -f "$dest_dir/.version" ]; then
        INSTALLED_VERSION=$(cat "$dest_dir/.version" 2>/dev/null)
        local new_version=""
        if [ -f "$SOURCE_DIR/package.json" ]; then
            new_version=$(grep '"version"' "$SOURCE_DIR/package.json" 2>/dev/null | head -1 | sed 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
        fi
        if [ -n "$INSTALLED_VERSION" ] && [ -n "$new_version" ]; then
            if [ "$INSTALLED_VERSION" = "$new_version" ]; then
                info "Reinstalando v$new_version"
            else
                info "Atualizando v$INSTALLED_VERSION ${SYM_ARROW} v$new_version"
            fi
        fi
    fi

    # Check for old files
    local old_files=()
    if [ -f "$dest_dir/step-registry.json" ]; then
        if ! grep -q "state.json" "$dest_dir/workflow.md" 2>/dev/null; then
            old_files+=("workflow antigo")
        fi
    fi

    if grep -r "orchestrator.db" "$dest_dir" 2>/dev/null | grep -v "backup" | grep -v "migrated" > /dev/null; then
        old_files+=("referencias SQLite")
    fi

    if [ ${#old_files[@]} -gt 0 ]; then
        MIGRATION_DETECTED=true
        MIGRATION_SOURCES="${old_files[*]}"
        debug "Detectado: $MIGRATION_SOURCES"
    fi
}

# =============================================================================
# LIMPEZA AUTOMATICA DE ARTEFATOS LEGADOS (auto_cleanup_legacy)
# Unifica: detect_legacy_artifacts + cleanup_legacy_artifacts + cleanup_legacy_ip
#          + cleanup_legacy_ip_project + npm globals + cross-platform cleanup
# Roda AUTOMATICAMENTE a cada instalacao - sem necessidade de --cleanup-legacy
# Idempotente: rodar multiplas vezes sem efeitos colaterais
# Seguro: NAO remove dados do usuario (stories, epics, state.json, config.json)
# =============================================================================

auto_cleanup_legacy() {
    local removed=0
    local claude_dir="$HOME/.claude"
    local neocortex_dir="$claude_dir/agents/neocortex"
    local skills_dir="$claude_dir/skills/neocortex"

    debug "Executando limpeza automatica de artefatos legados..."

    # ─── Helper: remove e loga ───────────────────────────────────────────
    _remove_legacy() {
        local target="$1"
        local label="$2"
        if [ -e "$target" ]; then
            local display="${target/#$HOME/~}"
            if rm -rf "$target" 2>/dev/null; then
                info "Removido: $display ($label)"
                removed=$((removed + 1))
            else
                debug "Falha ao remover: $display"
            fi
        fi
    }

    # ─── Categoria 1: Pacotes NPM globais legados ────────────────────────
    if command -v npm >/dev/null 2>&1; then
        # Verificar e remover pacotes npm globais legados
        for pkg in "@ornexus/neocortex-cli" "@ornexus-ai/neocortex"; do
            if npm list -g "$pkg" --depth=0 2>/dev/null | grep -q "$pkg"; then
                if npm uninstall -g "$pkg" 2>/dev/null; then
                    info "Removido: $pkg (pacote npm global legado)"
                    removed=$((removed + 1))
                else
                    debug "Falha ao remover pacote npm: $pkg"
                fi
            fi
        done

        # Verificar binario neocortex-cli no PATH
        local ncli_path
        ncli_path=$(command -v neocortex-cli 2>/dev/null || true)
        if [ -n "$ncli_path" ]; then
            # Verificar se e um link/binario npm (nao remover se for outro tool)
            if [[ "$ncli_path" == *"node_modules"* ]] || [[ "$ncli_path" == *"npm"* ]]; then
                rm -f "$ncli_path" 2>/dev/null && {
                    info "Removido: $ncli_path (binario neocortex-cli legado)"
                    removed=$((removed + 1))
                }
            fi
        fi
    fi

    # ─── Categoria 2: Claude Code (~/.claude/) ────────────────────────────
    # IP proprietaria de versoes anteriores
    _remove_legacy "$neocortex_dir/core"             "IP legada"
    _remove_legacy "$skills_dir"                     "skills legadas"
    _remove_legacy "$neocortex_dir/workflow.md"      "workflow removido no Tier 3"
    _remove_legacy "$neocortex_dir/package.json"     "arquivo desnecessario"
    _remove_legacy "$neocortex_dir/README.md"        "arquivo desnecessario"

    # Step folders legados
    for folder in steps-c steps-e steps-p steps-r steps-u; do
        _remove_legacy "$neocortex_dir/$folder"      "steps legados"
    done

    # Artefatos de instalacoes anteriores
    _remove_legacy "$claude_dir/agents/.git"         "repo git antigo"
    _remove_legacy "$claude_dir/.claude"             "diretorio aninhado (erro BMAD)"
    _remove_legacy "$claude_dir/agents-ldtn"         "diretorio legado"
    _remove_legacy "$claude_dir/.superclaude-metadata.json" "metadata SuperClaude"

    # Backups SuperClaude antigos
    if [ -d "$claude_dir/backups" ]; then
        for f in "$claude_dir/backups"/superclaude_backup_*.tar.gz; do
            [ -f "$f" ] || continue
            _remove_legacy "$f"                      "backup SuperClaude antigo"
        done
    fi

    # ─── Categoria 3: Cursor ──────────────────────────────────────────────
    _remove_legacy "$HOME/.cursor/neocortex"         "configs Cursor legadas"
    # .cursorrules com referencias neocortex (verificar antes de remover)
    if [ -f "$HOME/.cursorrules" ]; then
        if grep -qi "neocortex\|ornexus\|synapse" "$HOME/.cursorrules" 2>/dev/null; then
            _remove_legacy "$HOME/.cursorrules"      "cursorrules com refs legadas"
        fi
    fi

    # ─── Categoria 4: VS Code ─────────────────────────────────────────────
    _remove_legacy "$HOME/.vscode/neocortex"         "configs VS Code legadas"
    # .instructions.md com referencias neocortex antigo
    if [ -f "$HOME/.instructions.md" ]; then
        if grep -qi "neocortex\|ornexus\|synapse" "$HOME/.instructions.md" 2>/dev/null; then
            _remove_legacy "$HOME/.instructions.md"  "instructions.md com refs legadas"
        fi
    fi

    # ─── Categoria 5: Gemini CLI ──────────────────────────────────────────
    _remove_legacy "$HOME/.gemini/neocortex"         "configs Gemini legadas"

    # ─── Categoria 6: Codex ───────────────────────────────────────────────
    _remove_legacy "$HOME/.codex/neocortex"          "configs Codex legadas"

    # ─── Categoria 7: Antigravity ─────────────────────────────────────────
    # Configs legadas de Antigravity sao gerenciadas pelo adapter, sem path fixo global

    # ─── Categoria 8: Plaintext cache cleanup (Epic 62 - GAP 1) ─────────
    local cache_dir="$HOME/.neocortex/cache"
    if [ -d "$cache_dir" ]; then
        # Remove plaintext menu-cache.json
        _remove_legacy "$cache_dir/menu-cache.json" "cache plaintext (menu)"

        # Remove any non-.enc files in cache dir (excluding directories)
        for cache_file in "$cache_dir"/*; do
            [ -f "$cache_file" ] || continue
            case "$cache_file" in
                *.enc) continue ;;  # Keep encrypted cache files
                *) _remove_legacy "$cache_file" "cache plaintext" ;;
            esac
        done
    fi

    # ─── Resultado ────────────────────────────────────────────────────────
    if [ $removed -gt 0 ]; then
        ok "$removed artefato(s) legado(s) removido(s) automaticamente"
    else
        debug "Nenhum artefato legado encontrado"
    fi

    # Resetar contadores legados (compatibilidade)
    LEGACY_ITEMS=""
    LEGACY_WARNINGS=0

    return 0
}

# Cleanup de IP legada em projetos individuais (project-level)
auto_cleanup_legacy_project() {
    local project_dir="$1"
    local neocortex_dir="$project_dir/.claude/agents/neocortex"
    local skills_dir="$project_dir/.claude/skills/neocortex"
    local cleaned=false

    # Remover core/ e seus subdiretorios
    if [ -d "$neocortex_dir/core" ]; then
        rm -rf "$neocortex_dir/core"
        cleaned=true
    fi

    # Remover step folders
    for folder in steps-c steps-e steps-p steps-r steps-u; do
        if [ -d "$neocortex_dir/$folder" ]; then
            rm -rf "$neocortex_dir/$folder"
            cleaned=true
        fi
    done

    # Remover skills
    if [ -d "$skills_dir" ]; then
        rm -rf "$skills_dir"
        cleaned=true
    fi

    # Remover arquivos desnecessarios
    for file in package.json README.md workflow.md; do
        if [ -f "$neocortex_dir/$file" ]; then
            rm -f "$neocortex_dir/$file"
            cleaned=true
        fi
    done

    if [ "$cleaned" = true ]; then
        info "IP proprietaria removida do projeto (agora servida via server remoto)"
    fi
}

detect_project_migration_needs() {
    local migration_needed=false
    local sources=()

    [ -f ".neocortex/orchestrator.db" ] && { migration_needed=true; sources+=("orchestrator.db"); }
    for yaml_path in "bmad-output/sprint-status.yaml" ".neocortex/sprint-status.yaml" "docs/sprint-status.yaml"; do
        [ -f "$yaml_path" ] && { migration_needed=true; sources+=("$yaml_path"); }
    done

    if [ "$migration_needed" = true ]; then
        echo "${sources[*]}"
        return 0
    fi
    return 1
}

# =============================================================================
# BANNER
# =============================================================================

show_banner() {
    [ "$QUIET_MODE" = true ] && return
    [ "$NO_BANNER" = true ] && return

    echo ""
    echo -e "${CYAN}       #######${NC}"
    echo -e "${CYAN}      ###  ########${NC}"
    echo -e "${CYAN} #########      #####${NC}"
    echo -e "${CYAN} ##    ##############    ${BOLD}N E O C O R T E X${NC}"
    echo -e "${CYAN} ##   ###  ######  ##    ${BOLD}v${VERSION}${NC}"
    echo -e "${CYAN} ##   ###    ###   ##${NC}"
    echo -e "${CYAN} ##  ######  ###   ##    ${DIM}OrNexus Team${NC}"
    echo -e "${CYAN} ###############   ##${NC}"
    echo -e "${CYAN} #####       ########${NC}"
    echo -e "${CYAN}   ########  ##${NC}"
    echo -e "${CYAN}        #######${NC}"
    echo ""
}

# =============================================================================
# DETECCAO DO DIRETORIO FONTE
# =============================================================================

detect_source_dir() {
    if [ -n "${BASH_SOURCE[0]}" ]; then
        SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd)"
    elif [ -n "$0" ]; then
        SCRIPT_DIR="$(cd "$(dirname "$0")" 2>/dev/null && pwd)"
    else
        SCRIPT_DIR="$PWD"
    fi

    local source_found=false
    if [ -f "$SCRIPT_DIR/targets-stubs/claude-code/neocortex.md" ] || \
       [ -f "$SCRIPT_DIR/neocortex.md" ]; then
        source_found=true
    else
        for possible_dir in \
            "$SCRIPT_DIR" \
            "$(npm root -g 2>/dev/null)/@ornexus/neocortex" \
            "$(npm root -g 2>/dev/null)/neocortex" \
            "$(dirname "$0")" \
            "$PWD/node_modules/@ornexus/neocortex" \
            "$PWD/node_modules/neocortex" \
            "$HOME/.npm/_npx/"*"/node_modules/@ornexus/neocortex" \
            "$HOME/.npm/_npx/"*"/node_modules/neocortex"; do
            if [ -f "$possible_dir/targets-stubs/claude-code/neocortex.md" ] 2>/dev/null || \
               [ -f "$possible_dir/neocortex.md" ] 2>/dev/null; then
                SCRIPT_DIR="$possible_dir"
                source_found=true
                break
            fi
        done
    fi

    SOURCE_DIR="$SCRIPT_DIR"
    debug "Source: $SOURCE_DIR"

    # Epic 65: Emit visible warning when stubs cannot be found
    if [ "$source_found" = false ]; then
        warn "Arquivos de instalacao (stubs) nao encontrados em nenhum path conhecido"
        warn "SCRIPT_DIR: $SCRIPT_DIR"
        warn "Tente reinstalar: npm install -g @ornexus/neocortex"
    fi
}

show_target_resolution_dry_run() {
    local targets="$1"
    targets=$(echo "$targets" | tr ',' ' ')
    info "Dry-run: nenhum arquivo do usuario sera modificado"
    for target in $targets; do
        local resolved
        if resolved=$(resolve_target_dir "$target"); then
            info "Dry-run target=$target adapter=$resolved scope=published-stub"
            case "$target" in
                cursor)
                    info "Dry-run MCP target=cursor file=${CURSOR_HOME:-$HOME/.cursor}/mcp.json root=mcpServers managed=playwright,context7,browser_use,figma,shadcn,chrome-devtools action=add-or-update-preserve-custom"
                    ;;
                vscode)
                    local vscode_mcp
                    if [ -n "$VSCODE_MCP_FILE" ]; then vscode_mcp="$VSCODE_MCP_FILE"; elif [ "$(uname -s 2>/dev/null)" = "Darwin" ]; then vscode_mcp="$HOME/Library/Application Support/Code/User/mcp.json"; else vscode_mcp="${XDG_CONFIG_HOME:-$HOME/.config}/Code/User/mcp.json"; fi
                    info "Dry-run MCP target=vscode file=$vscode_mcp root=servers managed=playwright,context7,browser_use,figma,shadcn,chrome-devtools action=add-or-update-preserve-custom"
                    ;;
                gemini|gemini-cli)
                    info "Dry-run MCP target=gemini file=${GEMINI_HOME:-$HOME/.gemini}/settings.json root=mcpServers managed=playwright,context7,browser_use,figma,shadcn,chrome-devtools action=add-or-update-preserve-custom"
                    ;;
                codex)
                    info "Dry-run MCP target=codex file=${CODEX_HOME:-$HOME/.codex}/config.toml root=mcp_servers managed=playwright,context7,browser_use,figma,shadcn,chrome-devtools action=replace-managed-sections-preserve-unknown"
                    ;;
                opencode)
                    info "Dry-run MCP target=opencode file=${OPENCODE_HOME:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/opencode.json root=mcp managed=playwright,context7,browser_use,figma,shadcn,chrome-devtools action=add-or-update-preserve-custom"
                    ;;
                kimi)
                    info "Dry-run MCP target=kimi file=${KIMI_HOME:-$HOME/.kimi}/mcp.json root=mcpServers managed=playwright,context7,browser_use,figma,shadcn,chrome-devtools action=add-or-update-preserve-custom"
                    ;;
                openclaw)
                    info "Dry-run Skill target=openclaw file=${OPENCLAW_HOME:-$HOME/.openclaw}/skills/neocortex/SKILL.md action=copy-skill-preserve-openclaw-json config=${OPENCLAW_HOME:-$HOME/.openclaw}/openclaw.json non-overwrite"
                    info "Dry-run project target=openclaw file=.openclaw/skills/neocortex/SKILL.md action=copy-skill-when-create-project"
                    ;;
            esac
        else
            info "Dry-run target=$target adapter=NOT_FOUND"
        fi
    done
}

# =============================================================================
# TIER 3: CONFIG DO THIN CLIENT
# =============================================================================

setup_thin_client_config() {
    local config_dir="${HOME}/.neocortex"
    local config_file="${config_dir}/config.json"

    mkdir -p "$config_dir" 2>/dev/null
    mkdir -p "$config_dir/cache" 2>/dev/null

    # Story 61.4 - F4 remediation: restrictive permissions
    chmod 700 "$config_dir" 2>/dev/null
    chmod 700 "$config_dir/cache" 2>/dev/null

    if [ -f "$config_file" ]; then
        # P201.08: validate before any legacy rewrite. Invalid JSON is then sent
        # to the helper solely to create a verified backup; original stays intact.
        if ! managed_validate_policy "$HOME"; then
            managed_apply_policy "$HOME" >/dev/null 2>&1 || true
            warn "Config invalida ou ownership ambiguo; original preservado"
            return 1
        fi
        # Preservar config existente, atualizar apenas serverUrl se necessario
        local existing_mode
        existing_mode=$(grep '"mode"' "$config_file" 2>/dev/null | head -1 | sed 's/.*"mode"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')

        if [ "$existing_mode" = "active" ] || [ "$existing_mode" = "local" ] || [ "$existing_mode" = "remote" ]; then
            # ─── Config schema migration (Epic 62 - GAP 4) ────────────────
            # Check if config needs schema migration
            local has_config_version
            has_config_version=$(grep '"configVersion"' "$config_file" 2>/dev/null)
            if [ -z "$has_config_version" ]; then
                debug "Migrando schema do config.json (adicionando configVersion)"
                if command -v node >/dev/null 2>&1; then
                    node -e "
const fs = require('fs');
const path = '$config_file';
try {
    const cfg = JSON.parse(fs.readFileSync(path, 'utf-8'));
    // Add configVersion
    cfg.configVersion = 1;
    // Remove known obsolete fields from old base template
    delete cfg.version;
    delete cfg.cache;
    // Clean obsolete tier:3 from old base template (preserve real tier values)
    if (cfg.tier === 3 && cfg.mode === 'pending-activation') {
        delete cfg.tier;
    }
    const tmp = path + '.tmp-' + process.pid;
    fs.writeFileSync(tmp, JSON.stringify(cfg, null, 2) + '\n', { mode: 0o600 });
    fs.renameSync(tmp, path);
}" 2>/dev/null
                    chmod 600 "$config_file" 2>/dev/null
                    debug "Config migrada para configVersion 1"
                fi
            fi
            # ─── Fix stale localhost serverUrl (Epic 70 - Story 70.02) ────
            # If config has localhost serverUrl and installer has production URL,
            # update serverUrl while preserving all other config fields.
            local existing_server_url
            existing_server_url=$(grep '"serverUrl"' "$config_file" 2>/dev/null | head -1 | sed 's/.*"serverUrl"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')

            if echo "$existing_server_url" | grep -qE '^https?://(localhost|127\.0\.0\.1|0\.0\.0\.0)(:[0-9]+)?'; then
                if [ "$NEOCORTEX_SERVER_URL" != "$existing_server_url" ]; then
                    debug "Fixing stale localhost serverUrl to configured remote endpoint"
                    if command -v node >/dev/null 2>&1; then
                        node -e "
const fs = require('fs');
const p = '$config_file';
try {
    const raw = fs.readFileSync(p, 'utf-8').replace(/^\uFEFF/, '');
    const cfg = JSON.parse(raw);
    cfg.serverUrl = '${NEOCORTEX_SERVER_URL}';
    const tmp = p + '.tmp-' + process.pid;
    fs.writeFileSync(tmp, JSON.stringify(cfg, null, 2) + '\n', { mode: 0o600 });
    fs.renameSync(tmp, p);
}" 2>/dev/null
                        chmod 600 "$config_file" 2>/dev/null
                        debug "serverUrl updated to configured remote endpoint"
                    fi
                fi
            fi

            # ─── Fix stale legacy serverUrl (Epic P46 - Story P46.03) ────
            # If config has a deprecated API host, update to the configured remote endpoint.
            local legacy_server_host
            legacy_server_host="$(printf '%s.%s.%s.%s' api neocortex ornexus com)"
            if echo "$existing_server_url" | grep -qF "$legacy_server_host"; then
                debug "Fixing legacy serverUrl to configured remote endpoint"
                if command -v node >/dev/null 2>&1; then
                    node -e "
const fs = require('fs');
const p = '$config_file';
try {
    const raw = fs.readFileSync(p, 'utf-8').replace(/^\uFEFF/, '');
    const cfg = JSON.parse(raw);
    cfg.serverUrl = '${NEOCORTEX_SERVER_URL}';
    const tmp = p + '.tmp-' + process.pid;
    fs.writeFileSync(tmp, JSON.stringify(cfg, null, 2) + '\n', { mode: 0o600 });
    fs.renameSync(tmp, p);
}" 2>/dev/null
                    chmod 600 "$config_file" 2>/dev/null
                    debug "serverUrl updated from legacy host to configured remote endpoint"
                fi
            fi

            # Story 61.4 - ensure permissions are always enforced even on existing configs
            chmod 600 "$config_file" 2>/dev/null
            managed_apply_policy "$HOME" || return 1
            debug "Config existente preservada (mode=$existing_mode)"
            return 0
        fi
    fi

    # Criar config base para thin client
    local config_tmp="${config_file}.tmp-$$"
    cat > "$config_tmp" << EOFCONFIG
{
  "configVersion": 1,
  "mode": "pending-activation",
  "serverUrl": "${NEOCORTEX_SERVER_URL}",
  "resilience": {
    "circuitBreaker": true,
    "maxRetries": 3,
    "timeoutMs": 5000
  },
  "installedAt": "$(date -Iseconds 2>/dev/null || date '+%Y-%m-%dT%H:%M:%S')",
  "installerVersion": "${VERSION}",
  "subagentApprovalPolicy": {
    "schemaVersion": 1,
    "managedDefault": "max",
    "updatedAt": "$(date -Iseconds 2>/dev/null || date '+%Y-%m-%dT%H:%M:%S')"
  }
}
EOFCONFIG

    # Story 61.4 - F4 remediation: config file readable only by owner
    chmod 600 "$config_tmp" 2>/dev/null
    mv "$config_tmp" "$config_file" || { rm -f "$config_tmp"; return 1; }

    # Adopt the fresh managed block into the versioned ownership manifest.
    managed_apply_policy "$HOME" || return 1

    debug "Thin client config criada: $config_file"
}

# cleanup_legacy_ip_project() substituida por auto_cleanup_legacy_project()
# (definida na secao de limpeza automatica acima)

# =============================================================================
# INSTALACAO CORE
# =============================================================================

install_core() {
    local errors=0

    CLAUDE_DIR="${HOME}/.claude"
    AGENTS_DIR="${CLAUDE_DIR}/agents"
    DEST_DIR="${AGENTS_DIR}/neocortex"

    debug "HOME=$HOME | DEST=$DEST_DIR | MODE=thin-client"

    # Create directories
    if [ ! -d "$CLAUDE_DIR" ]; then
        mkdir -p "$CLAUDE_DIR" 2>/dev/null || { fail "Falha ao criar $CLAUDE_DIR"; return 1; }
    fi
    mkdir -p "$AGENTS_DIR" 2>/dev/null || { fail "Falha ao criar $AGENTS_DIR"; return 1; }

    # P201.08: retain the managed destination and update owned files additively.
    mkdir -p "$DEST_DIR" 2>/dev/null || { fail "Falha ao criar $DEST_DIR"; return 1; }

    # Thin-client ONLY: zero IP on client, all content from server
    setup_thin_client_config || ((errors++))

    # ─── Version-aware cache purge on upgrade (Epic 62 - GAP 2+3) ───────
    local pkg_version=""
    if [ -f "$SOURCE_DIR/package.json" ]; then
        pkg_version=$(grep '"version"' "$SOURCE_DIR/package.json" 2>/dev/null | head -1 | sed 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
    fi

    if [ -n "$pkg_version" ]; then
        local old_version=""
        # Read existing .version from either location
        if [ -f "$DEST_DIR/.version" ]; then
            old_version=$(cat "$DEST_DIR/.version" 2>/dev/null | tr -d '[:space:]')
        elif [ -f "$HOME/.neocortex/.version" ]; then
            old_version=$(cat "$HOME/.neocortex/.version" 2>/dev/null | tr -d '[:space:]')
        fi

        # If version changed, purge all cache files
        if [ -n "$old_version" ] && [ "$old_version" != "$pkg_version" ]; then
            local cache_dir="$HOME/.neocortex/cache"
            if [ -d "$cache_dir" ]; then
                local purged=0
                # Remove all .enc files
                for enc_file in "$cache_dir"/*.enc; do
                    [ -f "$enc_file" ] || continue
                    rm -f "$enc_file" 2>/dev/null && purged=$((purged + 1))
                done
                # Remove menu-cache.json (redundancy with 62.1)
                [ -f "$cache_dir/menu-cache.json" ] && rm -f "$cache_dir/menu-cache.json" 2>/dev/null && purged=$((purged + 1))
                # Remove any other non-directory files
                for cache_file in "$cache_dir"/*; do
                    [ -f "$cache_file" ] || continue
                    rm -f "$cache_file" 2>/dev/null && purged=$((purged + 1))
                done
                [ $purged -gt 0 ] && info "Cache purgado: versao alterada de $old_version para $pkg_version ($purged arquivo(s))"
            fi
        fi

        # Write version file
        echo "$pkg_version" > "$DEST_DIR/.version"
    fi

    if [ $errors -eq 0 ]; then
        ok "Remote mode configured ${DIM}(thin client ready)${NC}"
    else
        fail "Core instalado com erros"
    fi

    return $errors
}

# =============================================================================
# INSTALACAO DE SKILLS
# =============================================================================

install_skills() {
    local errors=0

    SKILLS_DIR="${CLAUDE_DIR}/skills"
    SKILLS_DEST="${SKILLS_DIR}/neocortex"
    SKILLS_SOURCE="${SOURCE_DIR}/core/skills"

    # Thin-client: skills delivered by remote server, never copied
    ok "Skills: delivered by remote server"
    return 0

    mkdir -p "$SKILLS_DIR" 2>/dev/null || { fail "Falha ao criar $SKILLS_DIR"; return 1; }

    # Clean previous
    [ -d "$SKILLS_DEST" ] && rm -rf "$SKILLS_DEST" 2>/dev/null

    if cp -r "$SKILLS_SOURCE" "$SKILLS_DEST" 2>/dev/null; then
        # Count skills
        local skill_count=0
        for skill_file in "$SKILLS_DEST"/step-skills/*/*.md "$SKILLS_DEST"/domain-skills/*/*.md; do
            if [ -f "$skill_file" ] && [[ "$(basename "$skill_file")" != "_template.md" ]]; then
                ((skill_count++))
            fi
        done
        ok "Skills instaladas ${DIM}($skill_count skills) [modo local]${NC}"
    else
        fail "Falha ao copiar skills"
        ((errors++))
    fi

    return $errors
}

# =============================================================================
# INSTALACAO DE AGENT (Claude Code)
# =============================================================================

install_agent() {
    local errors=0

    CLAUDE_TARGET_DIR="$SOURCE_DIR/targets-stubs/claude-code"
    if [ ! -d "$CLAUDE_TARGET_DIR" ] && is_neocortex_source_project "$SOURCE_DIR" && [ -d "$SOURCE_DIR/targets/claude-code" ]; then
        CLAUDE_TARGET_DIR="$SOURCE_DIR/targets/claude-code"
    fi
    if [ ! -d "$CLAUDE_TARGET_DIR" ]; then
        CLAUDE_TARGET_DIR="$SOURCE_DIR"
    fi

    # Epic 65: Early validation — if neither source file exists, emit clear error
    if [ ! -f "$CLAUDE_TARGET_DIR/neocortex.md" ] && [ ! -f "$CLAUDE_TARGET_DIR/neocortex.agent.yaml" ]; then
        fail "Arquivos fonte nao encontrados para target claude-code em: $CLAUDE_TARGET_DIR"
        fail "Diretorio de origem ($SOURCE_DIR) pode estar incompleto"
        fail "Tente reinstalar: npm install -g @ornexus/neocortex"
        return 2
    fi

    # Tier 3 Stub-Only: copiar apenas 2 arquivos de interface (stubs minimos)
    copy_file "$CLAUDE_TARGET_DIR/neocortex.md" "$DEST_DIR/" || ((errors++))
    copy_file "$CLAUDE_TARGET_DIR/neocortex.agent.yaml" "$DEST_DIR/" || ((errors++))

    # Epic 65: Post-copy verification
    if [ ! -f "$DEST_DIR/neocortex.md" ]; then
        fail "neocortex.md nao encontrado no destino apos copia: $DEST_DIR/"
        ((errors++))
    fi
    if [ ! -f "$DEST_DIR/neocortex.agent.yaml" ]; then
        fail "neocortex.agent.yaml nao encontrado no destino apos copia: $DEST_DIR/"
        ((errors++))
    fi

    # Tier transformation is applied before hashing by managed_copy_file.

    # Cleanup: remover workflow.md de instalacoes anteriores (v3.8 -> v3.9)
    if [ -f "$DEST_DIR/workflow.md" ]; then
        rm -f "$DEST_DIR/workflow.md"
        [ "$QUIET_MODE" = false ] && echo "  Removed workflow.md (content now server-side)"
    fi

    # Thin-client ONLY: 2 stub files + server-side orchestration

    return $errors
}

# =============================================================================
# INSTALACAO DE TARGETS
# =============================================================================

install_targets() {
    local targets="$1"
    local target_errors=0
    local target_count=0
    local target_results=()

    targets=$(echo "$targets" | tr ',' ' ')

    for target in $targets; do
        local func_target="$target"
        [ "$target" = "gemini-cli" ] && func_target="gemini"
        local func_name="install_$(echo "$func_target" | tr '-' '_')"
        local adapter_script=""

        case "$target" in
            opencode)
                local opencode_target="$SOURCE_DIR/targets-stubs/opencode"
                local opencode_home="${OPENCODE_HOME:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}"
                if [ -f "$opencode_target/neocortex.md" ] && [ -f "$opencode_target/neocortex-root.md" ]; then
                    mkdir -p "$opencode_home/agents" 2>/dev/null
                    if managed_copy_file "$opencode_target/neocortex.md" "$opencode_home/agents/neocortex.md" "profile-opencode-neocortex" "$HOME" && \
                       managed_copy_file "$opencode_target/neocortex-root.md" "$opencode_home/agents/neocortex-root.md" "profile-opencode-neocortex-root" "$HOME"; then
                        if [ -f "$opencode_target/opencode-mcp.json" ] && command -v node >/dev/null 2>&1; then
                            node "$SOURCE_DIR/targets-stubs/lib/mcp-merge.js" json "$opencode_target/opencode-mcp.json" "$opencode_home/opencode.json" mcp OPENCODE || true
                        fi
                        target_results+=("$target:OK")
                        ((target_count++))
                        ok "${BOLD}opencode${NC} ${DIM}(root + subagent)${NC}"
                    else
                        target_results+=("$target:SKIP")
                        warn "opencode ${DIM}(perfil indisponivel)${NC}"
                    fi
                else
                    target_results+=("$target:SKIP")
                    warn "opencode ${DIM}(root/subagent stubs nao encontrados)${NC}"
                fi
                continue
                ;;
            kimi)
                local kimi_target="$SOURCE_DIR/targets-stubs/kimi"
                local kimi_home="${KIMI_HOME:-$HOME/.kimi}"
                local kimi_code_home="${KIMI_CODE_HOME:-$HOME/.kimi-code}"
                if [ -f "$kimi_target/neocortex.md" ]; then
                    mkdir -p "$kimi_home/skills/neocortex" \
                             "$HOME/.config/agents/skills/neocortex" \
                             "$kimi_code_home/agents" \
                             "$HOME/.agents/agents" 2>/dev/null
                    local kimi_ok=0
                    managed_copy_file "$kimi_target/neocortex.md" "$kimi_home/skills/neocortex/SKILL.md" "profile-kimi-skill" "$HOME" || kimi_ok=1
                    managed_copy_file "$kimi_target/neocortex.md" "$HOME/.config/agents/skills/neocortex/SKILL.md" "profile-kimi-shared-skill" "$HOME" || kimi_ok=1
                    managed_copy_file "$kimi_target/neocortex.md" "$kimi_code_home/agents/neocortex.md" "profile-kimi-neocortex" "$kimi_code_home" || kimi_ok=1
                    managed_copy_file "$kimi_target/neocortex.md" "$HOME/.agents/agents/neocortex.md" "profile-kimi-shared-agent" "$HOME" || kimi_ok=1
                    if [ -f "$kimi_target/mcp.json" ] && command -v node >/dev/null 2>&1; then
                        node "$SOURCE_DIR/targets-stubs/lib/mcp-merge.js" json "$kimi_target/mcp.json" "$kimi_home/mcp.json" mcpServers KIMI || true
                    fi
                    if [ $kimi_ok -eq 0 ]; then
                        target_results+=("$target:OK")
                        ((target_count++))
                        ok "${BOLD}kimi${NC} ${DIM}(native-needs-validation agent + Skill fallback)${NC}"
                    else
                        target_results+=("$target:SKIP")
                        warn "kimi ${DIM}(perfil indisponivel)${NC}"
                    fi
                else
                    target_results+=("$target:SKIP")
                    warn "kimi ${DIM}(adapter nao encontrado)${NC}"
                fi
                continue
                ;;
            openclaw)
                local openclaw_target="$SOURCE_DIR/targets-stubs/openclaw"
                local openclaw_home="${OPENCLAW_HOME:-$HOME/.openclaw}"
                local openclaw_dest="$openclaw_home/skills/neocortex"
                if [ -f "$openclaw_target/SKILL.md" ]; then
                    mkdir -p "$openclaw_dest" 2>/dev/null
                    if managed_copy_file "$openclaw_target/SKILL.md" "$openclaw_dest/SKILL.md" "profile-openclaw-skill" "$HOME"; then
                        target_results+=("$target:OK")
                        ((target_count++))
                        ok "${BOLD}openclaw${NC} ${DIM}(Skill thin-client; openclaw.json preservado)${NC}"
                    else
                        target_results+=("$target:SKIP")
                        warn "openclaw ${DIM}(falha ao copiar Skill para $openclaw_dest/SKILL.md)${NC}"
                    fi
                else
                    target_results+=("$target:SKIP")
                    warn "openclaw ${DIM}(Skill stub nao encontrado; path tentado: $openclaw_target/SKILL.md)${NC}"
                fi
                continue
                ;;
        esac

        if ! adapter_script=$(resolve_target_adapter_script "$target"); then
            if [ "$target" = "claude-code" ]; then
                install_agent
                local result=$?
                if [ $result -eq 0 ]; then
                    target_results+=("$target:OK")
                    ((target_count++))
                    ok "${BOLD}claude-code${NC} ${DIM}(thin client)${NC}"
                else
                    target_results+=("$target:FAIL")
                    ((target_errors++))
                    fail "claude-code"
                fi
                continue
            fi

            target_results+=("$target:SKIP")
            ((target_errors++))
            continue
        fi

        . "$adapter_script"

        if type "$func_name" >/dev/null 2>&1; then
            "$func_name" "$SOURCE_DIR" "$HOME"
            local result=$?
            if [ $result -eq 0 ]; then
                target_results+=("$target:OK")
                ((target_count++))
                ok "${BOLD}$target${NC}"
            else
                target_results+=("$target:WARN")
                warn "$target ${DIM}(instalado com avisos)${NC}"
                ((target_count++))
            fi
        else
            fail "$target ${DIM}(funcao $func_name nao encontrada)${NC}"
            target_results+=("$target:FAIL")
            ((target_errors++))
        fi
    done

    TARGET_RESULTS=("${target_results[@]}")
    TARGET_COUNT=$target_count

    return $target_errors
}

# =============================================================================
# CARREGAMENTO DE .ENV
# =============================================================================

load_env_file() {
    local env_file=""
    for possible_env in "./.env" "$SOURCE_DIR/.env"; do
        [ -f "$possible_env" ] && { env_file="$possible_env"; break; }
    done

    if [ -n "$env_file" ] && [ -f "$env_file" ]; then
        local loaded=0
        while IFS='=' read -r key value || [ -n "$key" ]; do
            [[ -z "$key" || "$key" =~ ^[[:space:]]*# ]] && continue
            key=$(echo "$key" | xargs)
            value=$(echo "$value" | xargs)
            value="${value%\"}"; value="${value#\"}"
            value="${value%\'}"; value="${value#\'}"
            if [ -n "$value" ] && [ -z "${!key}" ]; then
                export "$key=$value"
                ((loaded++))
            fi
        done < "$env_file"
        [ $loaded -gt 0 ] && info "$loaded variavel(eis) carregada(s) do .env"
        return 0
    fi
    return 1
}

# =============================================================================
# CONFIGURACAO DE TOKENS
# =============================================================================

prompt_tokens() {
    [ "$QUIET_MODE" = true ] && return
    [ -n "$CONTEXT7_API_KEY" ] && return

    if [ "$AUTO_YES" = true ]; then
        return
    fi

    echo ""
    info "MCP Context7 nao configurado ${DIM}(opcional)${NC}"
    echo -ne "  Configurar agora? ${BOLD}[s/N]:${NC} "

    local response=""
    if read -r -t 15 response 2>/dev/null; then
        : # resposta recebida
    else
        echo ""
        return
    fi

    if [[ "$response" =~ ^([sS][iI]?[mM]?|[yY][eE]?[sS]?)$ ]]; then
        echo ""
        info "Obtenha sua API Key em: ${WHITE}https://context7.com${NC}"
        echo -ne "  Cole sua ${BOLD}CONTEXT7_API_KEY${NC}: "

        local api_key=""
        if read -r -t 60 api_key 2>/dev/null; then
            if [[ "$api_key" =~ ^ctx7sk- ]]; then
                export CONTEXT7_API_KEY="$api_key"
                ok "CONTEXT7_API_KEY configurada"
                echo ""
                info "Para persistir: ${DIM}echo 'export CONTEXT7_API_KEY=\"$api_key\"' >> ~/.bashrc${NC}"
            elif [ -n "$api_key" ]; then
                warn "Formato invalido (esperado: ctx7sk-...)"
            fi
        fi
    fi
}

# =============================================================================
# INSTALACAO DE MCP SERVERS
# =============================================================================

install_mcps() {
    if ! command -v claude >/dev/null 2>&1; then
        warn "Claude CLI nao encontrado ${DIM}(MCPs Claude user-scope serao instalados depois; outros targets continuam)${NC}"
        return 0
    fi

    local mcp_results=""
    local browser_use_env_args=()

    [ -n "${OPENAI_API_KEY:-}" ] && browser_use_env_args+=(-e "OPENAI_API_KEY=$OPENAI_API_KEY")
    [ -n "${ANTHROPIC_API_KEY:-}" ] && browser_use_env_args+=(-e "ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY")

    if claude mcp add --scope user playwright -- npx -y @playwright/mcp@latest 2>/dev/null; then
        mcp_results="${mcp_results}playwright:OK "
    else
        mcp_results="${mcp_results}playwright:FAIL "
    fi

    if [ -n "${CONTEXT7_API_KEY:-}" ]; then
        if claude mcp add --scope user --transport http context7 https://mcp.context7.com/mcp --header "CONTEXT7_API_KEY: $CONTEXT7_API_KEY" 2>/dev/null; then
            mcp_results="${mcp_results}context7:OK "
        else
            mcp_results="${mcp_results}context7:FAIL "
        fi
    else
        mcp_results="${mcp_results}context7:SKIP "
    fi

    # browser_use: optional (requires uvx and at least one supported LLM API key).
    # Supported env vars are referenced by name only in logs: OPENAI_API_KEY,
    # ANTHROPIC_API_KEY.
    if [ ${#browser_use_env_args[@]} -gt 0 ]; then
        if ! command -v uvx >/dev/null 2>&1; then
            warn "uvx nao encontrado ${DIM}(Browser Use MCP requer uvx; instale uv/uvx e tente novamente)${NC}"
            mcp_results="${mcp_results}browser_use:FAIL "
        elif claude mcp add --scope user browser_use "${browser_use_env_args[@]}" -- uvx --from 'browser-use[cli]' browser-use --mcp 2>/dev/null; then
            mcp_results="${mcp_results}browser_use:OK "
        else
            mcp_results="${mcp_results}browser_use:FAIL "
        fi
    else
        mcp_results="${mcp_results}browser_use:SKIP "
    fi

    # Design/component MCPs follow the same baseline install pattern as
    # Playwright because public target fragments include them by default.
    if claude mcp add --scope user --transport http figma https://mcp.figma.com/mcp 2>/dev/null; then
        mcp_results="${mcp_results}figma:OK "
    else
        mcp_results="${mcp_results}figma:FAIL "
    fi

    if claude mcp add --scope user shadcn -- npx shadcn@latest mcp 2>/dev/null; then
        mcp_results="${mcp_results}shadcn:OK "
    else
        mcp_results="${mcp_results}shadcn:FAIL "
    fi

    if claude mcp add --scope user chrome-devtools -- npx -y chrome-devtools-mcp@latest 2>/dev/null; then
        mcp_results="${mcp_results}chrome-devtools:OK "
    else
        mcp_results="${mcp_results}chrome-devtools:FAIL "
    fi

    # Show compact MCP results
    local mcp_ok="" mcp_skip=""
    for entry in $mcp_results; do
        local name="${entry%%:*}"
        local status="${entry##*:}"
        case "$status" in
            OK) mcp_ok="${mcp_ok}${name}, " ;;
            SKIP) mcp_skip="${mcp_skip}${name}, " ;;
        esac
    done

    [ -n "$mcp_ok" ] && ok "MCPs: ${DIM}${mcp_ok%, }${NC}"
    [ -n "$mcp_skip" ] && info "MCPs pendentes: ${DIM}${mcp_skip%, }${NC}"

    return 0
}

# =============================================================================
# STATUS DO CODERABBIT CLI (OPCIONAL / MANUAL)
# =============================================================================

is_ci_environment() {
    [ "${CI:-}" = "true" ] || [ "${GITHUB_ACTIONS:-}" = "true" ] || [ "${TF_BUILD:-}" = "true" ]
}

coderabbit_skip_reason() {
    if is_ci_environment; then
        printf '%s\n' "P177_OPTIONAL_TOOL_SKIPPED_CI"
    elif ! is_interactive || [ "$AUTO_YES" = true ] || [ "$QUIET_MODE" = true ]; then
        printf '%s\n' "P177_OPTIONAL_TOOL_SKIPPED_NONINTERACTIVE"
    else
        printf '%s\n' "P177_OPTIONAL_TOOL_OPT_IN_REQUIRED"
    fi
}

optional_tool_info() {
    echo -e "  ${DIM}${SYM_ARROW} $1${NC}"
}

report_coderabbit_status() {
    if command -v coderabbit >/dev/null 2>&1; then
        emit_installer_diagnostic "coderabbit" "optional-tool-setup" "detected" "P177_OPTIONAL_TOOL_DETECTED_MANUAL" "0" "null" "false" "0" "0" "manual_optional_tool_detected" "docs/install/coderabbit-manual-setup.md"
        ok "CodeRabbit CLI ${DIM}(opcional/manual detectado)${NC}"
        CODERABBIT_INSTALLED=true
        return 0
    fi

    local reason_code
    reason_code="$(coderabbit_skip_reason)"
    emit_installer_diagnostic "coderabbit" "optional-tool-setup" "skipped" "$reason_code" "null" "null" "false" "0" "0" "optional_tool_setup_skipped" "docs/install/coderabbit-manual-setup.md"
    optional_tool_info "CodeRabbit CLI ${DIM}(opcional; setup manual em docs/install/coderabbit-manual-setup.md; ${reason_code})${NC}"
    CODERABBIT_INSTALLED=false

    return 0
}

# =============================================================================
# VERIFICACAO POS-INSTALACAO
# =============================================================================

verify_installation() {
    local fails=0
    local warns=0
    local report=""

    if ! echo "$SELECTED_TARGETS" | grep -q "claude-code"; then
        return 0
    fi

    # ─── Layer 1: File existence + minimum size ─────────────────────────
    local check_fname check_min check_fpath check_size check_ext check_first_line check_display
    for check_fname in neocortex.md neocortex.agent.yaml; do
        case "$check_fname" in
            neocortex.md)          check_min=512 ;;
            neocortex.agent.yaml)  check_min=128 ;;
        esac
        check_fpath="${DEST_DIR}/${check_fname}"

        if [ ! -f "$check_fpath" ]; then
            report="${report}FAIL ${check_fname} (nao encontrado)\n"
            fails=$((fails + 1))
        else
            check_size=$(wc -c < "$check_fpath" 2>/dev/null | tr -d ' ')
            if [ "$check_size" -lt "$check_min" ] 2>/dev/null; then
                report="${report}FAIL ${check_fname} (${check_size} bytes - possivelmente corrompido, minimo ${check_min})\n"
                fails=$((fails + 1))
            else
                # ─── Layer 2: Content marker (frontmatter) ──────────────
                check_ext="${check_fname##*.}"
                if [ "$check_ext" = "md" ]; then
                    check_first_line=$(head -1 "$check_fpath" 2>/dev/null)
                    if [ "$check_first_line" != "---" ]; then
                        report="${report}WARN ${check_fname} (formato invalido - sem frontmatter)\n"
                        warns=$((warns + 1))
                    else
                        if [ "$check_size" -ge 1024 ]; then check_display="$((check_size / 1024))KB"; else check_display="${check_size}B"; fi
                        report="${report}OK   ${check_fname} (${check_display})\n"
                    fi
                else
                    if [ "$check_size" -ge 1024 ]; then check_display="$((check_size / 1024))KB"; else check_display="${check_size}B"; fi
                    report="${report}OK   ${check_fname} (${check_display})\n"
                fi
            fi
        fi
    done

    if false; then
        # ─── Layer 3: Step directories (removed - thin-client only) ──────
        for dir in steps-c steps-e steps-p steps-r steps-u; do
            local dir_path="$DEST_DIR/$dir"
            if [ ! -d "$dir_path" ]; then
                report="${report}FAIL ${dir}/ (diretorio nao encontrado)\n"
                fails=$((fails + 1))
            else
                local md_count=0
                for f in "$dir_path"/*.md; do
                    [ -f "$f" ] && md_count=$((md_count + 1))
                done
                if [ $md_count -eq 0 ]; then
                    report="${report}WARN ${dir}/ (vazio - nenhum arquivo .md)\n"
                    warns=$((warns + 1))
                else
                    report="${report}OK   ${dir}/ (${md_count} arquivos)\n"
                fi
            fi
        done

        # ─── Layer 3b: Core directory (local mode only) ──────────────────
        if [ ! -d "$DEST_DIR/core" ]; then
            report="${report}FAIL core/ (diretorio nao encontrado)\n"
            fails=$((fails + 1))
        else
            report="${report}OK   core/\n"
        fi
    else
        # ─── Layer 3: Thin client config (remote mode) ───────────────────
        local config_file="${HOME}/.neocortex/config.json"
        if [ -f "$config_file" ]; then
            report="${report}OK   ~/.neocortex/config.json (thin client configured)\n"
        else
            report="${report}WARN ~/.neocortex/config.json (nao encontrado)\n"
            warns=$((warns + 1))
        fi

        # Verify NO IP directories exist
        local ip_found=false
        for dir in core steps-c steps-e steps-p steps-r steps-u; do
            if [ -d "$DEST_DIR/$dir" ]; then
                report="${report}WARN ${dir}/ ainda existe (deveria ter sido removido)\n"
                warns=$((warns + 1))
                ip_found=true
            fi
        done
        if [ "$ip_found" = false ]; then
            report="${report}OK   Zero IP no filesystem (modo remoto)\n"
        fi
    fi

    # ─── Display report ─────────────────────────────────────────────────
    if [ $fails -eq 0 ] && [ $warns -eq 0 ]; then
        # All good - compact output
        if [ "$QUIET_MODE" != true ]; then
            ok "Instalacao verificada"
        fi
        return 0
    fi

    # Show detailed report when issues found
    if [ "$QUIET_MODE" = true ] && [ $fails -eq 0 ]; then
        return 0  # In quiet mode, skip warnings-only report
    fi

    echo ""
    info "Verificacao pos-instalacao:"
    echo -e "$report" | while IFS= read -r line; do
        [ -z "$line" ] && continue
        local status="${line%% *}"
        local detail="${line#* }"
        case "$status" in
            OK)   echo -e "    ${GREEN}${SYM_OK}${NC} $detail" ;;
            WARN) echo -e "    ${YELLOW}${SYM_WARN}${NC} $detail" ;;
            FAIL) echo -e "    ${RED}${SYM_FAIL}${NC} $detail" ;;
        esac
    done

    [ $fails -gt 0 ] && return 1
    return 0
}

# =============================================================================
# RESULTADO
# =============================================================================

show_result() {
    local install_status=$1
    [ "$QUIET_MODE" = true ] && return

    echo ""
    echo -e "  ${DIM}────────────────────────────────────────${NC}"

    if [ $install_status -eq 0 ] && verify_installation; then
        echo ""

        # Success logo (brain/cortex shape, text centered vertically)
        echo -e "${CYAN}       #######${NC}"
        echo -e "${CYAN}      ###  ########${NC}"
        echo -e "${CYAN} #########      #####${NC}"
        echo -e "${CYAN} ##    ##############    ${BOLD}N E O C O R T E X${NC}"
        echo -e "${CYAN} ##   ###  ######  ##    ${BOLD}v${VERSION}${NC}"
        echo -e "${CYAN} ##   ###    ###   ##${NC}"
        echo -e "${CYAN} ##  ######  ###   ##    ${GREEN}${BOLD}Installation complete!${NC}"
        echo -e "${CYAN} ###############   ##    ${DIM}OrNexus Team${NC}"
        echo -e "${CYAN} #####       ########${NC}"
        echo -e "${CYAN}   ########  ##${NC}"
        echo -e "${CYAN}        #######${NC}"
        echo ""
        echo -e "  ${DIM}Mode:${NC}    Remote (thin client)"
        echo -e "  ${DIM}Status:${NC}  Ready to activate"
        echo ""
        echo -e "  ${DIM}Activate your license:${NC}"
        echo -e "    ${CYAN}neocortex activate YOUR-LICENSE-KEY${NC}"
        echo ""
        echo -e "  ${DIM}Get your key at:${NC} ${CYAN}https://neocortex.sh/portal/login${NC}"
        echo ""
        echo -e "  ${DIM}After activation:${NC}"
        echo -e "    ${CYAN}@neocortex *menu${NC}"
        echo ""
    else
        echo ""
        echo -e "  ${RED}${BOLD}Instalacao com problemas${NC}"
        echo ""
        echo -e "  Execute novamente com ${BOLD}--debug${NC} para detalhes:"
        echo -e "    ${YELLOW}npx @ornexus/neocortex --debug${NC}"
        echo ""
        return 1
    fi
}

# =============================================================================
# CRIACAO DE DIRETORIOS DO PROJETO
# =============================================================================

create_project_dirs() {
    [ "$QUIET_MODE" = true ] && return
    [ "$SKIP_PROJECT_DIRS" = true ] && return

    local should_create=false

    if [ "$CREATE_PROJECT" = true ] || [ "$AUTO_YES" = true ]; then
        should_create=true
    else
        echo -ne "  Instalar estrutura no projeto atual? ${BOLD}[s/N]:${NC} "

        local response="n"
        if read -r -t 30 response </dev/tty 2>/dev/null; then
            :
        else
            echo ""
            response="n"
        fi

        [[ "$response" =~ ^([sS][iI]?[mM]?|[yY][eE]?[sS]?)$ ]] && should_create=true
    fi

    if [ "$should_create" = true ]; then
        local project_dir="$PWD"

        if is_neocortex_source_project "$project_dir"; then
            warn "Recusando --create-project dentro do repo fonte do Neocortex"
            warn "Isso preserva core/ e arquivos de desenvolvimento locais"
            return
        fi

        # P201.08: preserve project-owned/custom files. Managed profile writes
        # below update only explicitly owned destinations.

        # Create base directories
        mkdir -p "$project_dir/.neocortex/specs" \
                 "$project_dir/.neocortex/planning" \
                 "$project_dir/docs/stories" \
                 "$project_dir/docs/epics" \
                 "$project_dir/docs/proposals" 2>/dev/null

        # Copy state template if needed
        if [ ! -f "$project_dir/.neocortex/state.json" ]; then
            [ -f "$SOURCE_DIR/core/data/state-template.json" ] && \
                cp "$SOURCE_DIR/core/data/state-template.json" "$project_dir/.neocortex/state.json"
        fi

        # Thin-client: never copy core/ to project

        # Install target-specific files
        local targets_list
        targets_list=$(echo "$SELECTED_TARGETS" | tr ',' ' ')
        local target_summary=""

        for target in $targets_list; do
            case "$target" in
                claude-code)
                    local claude_target_dir
                    claude_target_dir=$(resolve_target_dir "claude-code") || true
                    if [ -d "$claude_target_dir" ]; then
                        mkdir -p "$project_dir/.claude/agents/neocortex"

                        # Tier 3: copiar apenas 2 stub files
                        managed_copy_file "$claude_target_dir/neocortex.md" "$project_dir/.claude/agents/neocortex/" "project-claude-neocortex" "$project_dir"
                        managed_copy_file "$claude_target_dir/neocortex.agent.yaml" "$project_dir/.claude/agents/neocortex/" "project-claude-neocortex-yaml" "$project_dir"
                        # Cleanup workflow.md from previous versions
                        rm -f "$project_dir/.claude/agents/neocortex/workflow.md" 2>/dev/null

                        # Thin-client: cleanup legacy IP from previous installs
                        auto_cleanup_legacy_project "$project_dir"
                        target_summary="${target_summary}claude-code, "
                    fi
                    ;;
                cursor)
                    # IP Protection: ALWAYS thin-client, zero IP on client
                    local stub_adapter="$SOURCE_DIR/targets-stubs/cursor/install-cursor.sh"
                    if [ -f "$stub_adapter" ]; then
                        . "$stub_adapter"
                        install_cursor "$SOURCE_DIR" "$project_dir"
                        target_summary="${target_summary}cursor, "
                    fi
                    ;;
                vscode)
                    local stub_adapter="$SOURCE_DIR/targets-stubs/vscode/install-vscode.sh"
                    if [ -f "$stub_adapter" ]; then
                        . "$stub_adapter"
                        install_vscode "$SOURCE_DIR" "$project_dir"
                        target_summary="${target_summary}vscode, "
                    fi
                    ;;
                gemini|gemini-cli)
                    local stub_adapter="$SOURCE_DIR/targets-stubs/gemini-cli/install-gemini.sh"
                    if [ -f "$stub_adapter" ]; then
                        . "$stub_adapter"
                        install_gemini "$SOURCE_DIR" "$project_dir"
                        target_summary="${target_summary}gemini, "
                    fi
                    ;;
                codex)
                    local stub_adapter="$SOURCE_DIR/targets-stubs/codex/install-codex.sh"
                    if [ -f "$stub_adapter" ]; then
                        . "$stub_adapter"
                        install_codex "$SOURCE_DIR" "$project_dir"
                        target_summary="${target_summary}codex, "
                    fi
                    ;;
                antigravity)
                    local stub_adapter="$SOURCE_DIR/targets-stubs/antigravity/install-antigravity.sh"
                    if [ -f "$stub_adapter" ]; then
                        . "$stub_adapter"
                        install_antigravity "$SOURCE_DIR" "$project_dir"
                        target_summary="${target_summary}antigravity, "
                    fi
                    ;;
                opencode)
                    local opencode_target="$SOURCE_DIR/targets-stubs/opencode"
                    if [ -f "$opencode_target/neocortex.md" ] && [ -f "$opencode_target/neocortex-root.md" ]; then
                        mkdir -p "$project_dir/.opencode/agents" 2>/dev/null
                        managed_copy_file "$opencode_target/neocortex.md" "$project_dir/.opencode/agents/neocortex.md" "project-opencode-neocortex" "$project_dir"
                        managed_copy_file "$opencode_target/neocortex-root.md" "$project_dir/.opencode/agents/neocortex-root.md" "project-opencode-neocortex-root" "$project_dir"
                        target_summary="${target_summary}opencode, "
                    fi
                    ;;
                kimi)
                    local kimi_target="$SOURCE_DIR/targets-stubs/kimi"
                    if [ -f "$kimi_target/neocortex.md" ]; then
                        mkdir -p "$project_dir/.kimi/skills/neocortex" \
                                 "$project_dir/.kimi-code/agents" \
                                 "$project_dir/.agents/agents" 2>/dev/null
                        managed_copy_file "$kimi_target/neocortex.md" "$project_dir/.kimi/skills/neocortex/SKILL.md" "project-kimi-skill" "$project_dir"
                        managed_copy_file "$kimi_target/neocortex.md" "$project_dir/.kimi-code/agents/neocortex.md" "project-kimi-neocortex" "$project_dir"
                        managed_copy_file "$kimi_target/neocortex.md" "$project_dir/.agents/agents/neocortex.md" "project-kimi-shared-agent" "$project_dir"
                        target_summary="${target_summary}kimi, "
                    fi
                    ;;
                openclaw)
                    local openclaw_target="$SOURCE_DIR/targets-stubs/openclaw"
                    if [ -f "$openclaw_target/SKILL.md" ]; then
                        mkdir -p "$project_dir/.openclaw/skills/neocortex" 2>/dev/null
                        managed_copy_file "$openclaw_target/SKILL.md" "$project_dir/.openclaw/skills/neocortex/SKILL.md" "project-openclaw-skill" "$project_dir"
                        target_summary="${target_summary}openclaw, "
                    fi
                    ;;
            esac
        done

        # P151: bootstrap NEOCORTEX.md first, then repair platform wrappers or
        # semantic symlinks for the selected project targets. This is best-effort
        # during install because older packages may not ship the JS handler yet.
        if [ -f "$SOURCE_DIR/packages/client/dist/cli.js" ]; then
            local memory_targets=""
            for target in $targets_list; do
                case "$target" in
                    gemini) target="gemini-cli" ;;
                esac
                if [ -z "$memory_targets" ]; then
                    memory_targets="$target"
                else
                    memory_targets="$memory_targets,$target"
                fi
            done
            node "$SOURCE_DIR/packages/client/dist/cli.js" refresh-memory --project-root "$project_dir" --targets "$memory_targets" >/dev/null 2>&1 || \
                warn "Nao foi possivel reparar memoria canonica automaticamente; execute: neocortex-client refresh-memory --project-root '$project_dir' --targets '$memory_targets'"
        fi

        echo ""
        ok "Estrutura do projeto instalada ${DIM}(${target_summary%, })${NC}"
        echo ""
        info "Proximo passo: ${CYAN}@neocortex *init @docs/epics.md${NC}"
    fi
}

# =============================================================================
# MIGRACAO
# =============================================================================

show_migration_info() {
    [ "$QUIET_MODE" = true ] && return

    local project_sources
    if project_sources=$(detect_project_migration_needs); then
        echo ""
        warn "${BOLD}Migracao detectada${NC}"
        for src in $project_sources; do
            info "Arquivo antigo: $src"
        done
        info "Execute: ${CYAN}@neocortex *init @docs/epics.md${NC} para migrar"
    fi
}

# =============================================================================
# TIER-BASED PLATFORM GATING (Story 57.4)
# =============================================================================

# Read tier from config.json (defaults to "free" if not found)
get_tier_from_config() {
    local config_file="$HOME/.neocortex/config.json"
    if [ -f "$config_file" ]; then
        local tier
        tier=$(cat "$config_file" 2>/dev/null | grep -o '"tier"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)"$/\1/')
        [ -n "$tier" ] && echo "$tier" || echo "free"
    else
        echo "free"
    fi
}

# Check if a platform is allowed for the current tier
# Returns 0 if allowed, 1 if blocked
check_platform_tier() {
    local platform=$1
    local tier=$2
    case "$platform" in
        claude-code) return 0 ;;  # Always allowed
        cursor|gemini|gemini-cli|codex|opencode)
            if [ "$tier" = "free" ]; then
                warn "$platform requer plano Pro. Plataforma disponivel: Claude Code."
                info "Upgrade: ${CYAN}https://neocortex.sh/portal/dashboard/plans${NC}"
                return 1
            fi
            return 0
            ;;
        vscode|antigravity|kimi)
            if [ "$tier" != "enterprise" ]; then
                warn "$platform requer plano Enterprise."
                info "Upgrade: ${CYAN}https://neocortex.sh/portal/dashboard/plans${NC}"
                return 1
            fi
            return 0
            ;;
    esac
    return 0  # Unknown platforms pass through
}

# =============================================================================
# MAIN
# =============================================================================

TOTAL_STEPS=4

main() {
    show_banner
    warn_linux_root_home_context
    detect_source_dir
    detect_old_installation

    # Determine targets
    if [ -z "$SELECTED_TARGETS" ]; then
        SELECTED_TARGETS="claude-code"
    fi

    debug "Targets: $SELECTED_TARGETS"

    # Read user tier for platform gating
    local user_tier
    user_tier=$(get_tier_from_config)
    debug "User tier: $user_tier"

    # Validate targets (name check + tier check)
    local invalid_targets=""
    local blocked_targets=""
    for target in $(echo "$SELECTED_TARGETS" | tr ',' ' '); do
        echo "$VALID_TARGETS" | grep -wq "$target" || invalid_targets="$invalid_targets $target"
    done

    if [ -n "$invalid_targets" ]; then
        fail "Plataformas invalidas:$invalid_targets"
        fail "Validas: $VALID_TARGETS"
        exit 1
    fi

    # Filter out tier-blocked platforms
    local allowed_targets=""
    for target in $(echo "$SELECTED_TARGETS" | tr ',' ' '); do
        if check_platform_tier "$target" "$user_tier"; then
            if [ -z "$allowed_targets" ]; then
                allowed_targets="$target"
            else
                allowed_targets="$allowed_targets,$target"
            fi
        else
            blocked_targets="$blocked_targets $target"
        fi
    done

    if [ -n "$blocked_targets" ]; then
        info "Plataforma(s) bloqueada(s) por tier:$blocked_targets"
    fi

    # Use only allowed targets
    if [ -n "$allowed_targets" ]; then
        SELECTED_TARGETS="$allowed_targets"
    else
        SELECTED_TARGETS="claude-code"
        info "Usando plataforma padrao: claude-code"
    fi

    if [ "$DRY_RUN" = true ]; then
        show_target_resolution_dry_run "$SELECTED_TARGETS"
        exit 0
    fi

    # Count targets for step total
    local target_count=$(echo "$SELECTED_TARGETS" | tr ',' ' ' | wc -w | tr -d ' ')
    if echo "$SELECTED_TARGETS" | grep -q "claude-code"; then
        TOTAL_STEPS=6  # cleanup + core + skills + targets + mcps + tools
    fi

    # Step 1: Limpeza automatica de versoes anteriores
    step 1 $TOTAL_STEPS "Limpeza de versoes anteriores"
    auto_cleanup_legacy

    # Step 2: Core
    step 2 $TOTAL_STEPS "Instalando core"
    install_core
    local core_result=$?
    if [ $core_result -ne 0 ]; then
        fail "Falha na instalacao do core"
        exit 1
    fi

    # Step 3: Skills
    step 3 $TOTAL_STEPS "Instalando skills"
    install_skills

    # Step 4: Targets
    step 4 $TOTAL_STEPS "Instalando ${BOLD}$target_count${NC} plataforma(s)"
    install_targets "$SELECTED_TARGETS"

    # Step 5-6: Claude Code extras
    if echo "$SELECTED_TARGETS" | grep -q "claude-code"; then
        load_env_file
        prompt_tokens

        step 5 $TOTAL_STEPS "Configurando MCPs"
        install_mcps

        step 6 $TOTAL_STEPS "Verificando ferramentas"
        report_coderabbit_status
    fi

    # Result
    show_result $core_result
    local result_code=$?

    if [ $result_code -eq 0 ]; then
        show_migration_info
        create_project_dirs
        echo -e "  ${DIM}Desenvolvido por OrNexus Team${NC}"
        echo ""
    fi

    exit $result_code
}

main
