#!/usr/bin/env nu

# syntaxis Installer (Dynamic Binary Discovery)
#
# Dynamically discovers binary crates from workspace Cargo.toml
# and installs them with wrappers, configs, and Leptos deployment
#
# USAGE:
#   install-syntaxis.nu                   # Show help
#   install-syntaxis.nu help              # Show help
#   install-syntaxis.nu all               # Install all binaries (keeps target/)
#   install-syntaxis.nu all --rm-target   # Install all (remove target/)
#   install-syntaxis.nu status            # Show installation status
#   install-syntaxis.nu list              # List available binaries

# Source shared libraries
source syntaxis-lib.nu

def print_help [] {
    print ""
    print "🛠️  syntaxis Binary Installer"
    print ""
    print "Dynamically installs binaries from workspace with configs and Leptos dashboard"
    print ""
    print "USAGE:"
    print "  install-syntaxis.nu                           # Show this help message"
    print "  install-syntaxis.nu help                      # Show this help message"
    print "  install-syntaxis.nu all                       # Install all binaries (keeps target/)"
    print "  install-syntaxis.nu all --rm-target           # Install all and remove target/"
    print "  install-syntaxis.nu status                    # Show installation status"
    print "  install-syntaxis.nu list                      # List available binaries"
    print ""
    print "INSTALL SPECIFIC BINARIES:"
    print "  install-syntaxis.nu cli                       # Install syntaxis-cli"
    print "  install-syntaxis.nu tui                       # Install syntaxis-tui"
    print "  install-syntaxis.nu api                       # Install syntaxis-api"
    print "  install-syntaxis.nu client                    # Install dashboard-client"
    print ""
    print "  # Or use full binary/crate names:"
    print "  install-syntaxis.nu syntaxis-cli              # Install syntaxis-cli"
    print "  install-syntaxis.nu syntaxis-tui              # Install syntaxis-tui"
    print "  install-syntaxis.nu syntaxis-api              # Install syntaxis-api"
    print ""
    print "OPTIONS:"
    print "  --rm-target                # Remove target/ directories after build"
    print "                             # (default: false, keep target/)"
    print ""
    print "WHAT GETS INSTALLED (PER BINARY):"
    print "  1. Binary crate (auto-discovered from Cargo.toml [[bin]] sections)"
    print "  2. Wrapper script (for binaries that need configuration)"
    print "     • Injects environment variables for auto-discovery binaries"
    print "     • Injects default --config path"
    print "  3. Target-specific config file"
    print ""
    print "GLOBAL RESOURCES:"
    print "  • Installation manifest in .syntaxis/manifest.toml"
    print "  • Configuration directories at ~/.config/syntaxis/"
    print ""
}

def discover_binary_crates [] {
    # Load workspace members from root Cargo.toml and find binaries in core/crates/*
    let workspace_toml = "Cargo.toml"

    if not ($workspace_toml | path exists) {
        print "❌ Cargo.toml not found"
        return []
    }
    try {
        # Read root Cargo.toml to get workspace members
        let workspace_content = (open $workspace_toml)

        # Extract all lines with members entries
        let members = (
            ($workspace_content | get -o workspace | get -o members)
            | each { |line|
                # let trimmed = ($line | str trim)
                # Match entries like "core/crates/syntaxis-api",
                if ($line | str starts-with 'core/crates/') {
                    # Extract the path: "core/crates/syntaxis-api", → core/crates/syntaxis-api
                    # Split by quotes and get the middle part
                    ($line | str trim)
                    #let parts = ($line | split row '"')
                    #if ($parts | length) >= 2 {
                    #    $parts | get 1
                    #}
                }
            }
            | compact
        )
        # For each member in core/crates/*, extract metadata
        let binaries = (
            $members
            | each { |member_path|
                let member_toml = $"($member_path)/Cargo.toml"

                if ($member_toml | path exists) {
                    let member_content = (open $member_toml)

                    # Extract package name
                    let package_name = ($member_content | get -o package | get -o name)
                    if ($package_name | is-not-empty) {
                        # Extract metadata from [package.metadata.syntaxis]
                        let metadata = ($member_content | get -o package.metadata | get -o syntaxis)
                        if ($metadata | is-not-empty) {
                            # Extract use_wrapper
                            let use_wrapper = ($metadata | get -o use_wrapper)

                            # Extract install_mode
                            let install_mode = ($metadata | get -o install_mode)

                            # Extract config_src
                            let config_src = ($metadata | get -o config_src)

                            # Extract binary name from [[bin]] section
                            let parts_bin = ($member_content | get -o bin)
                            let bin_name = if ($parts_bin | length) > 0 {
                                $parts_bin | first | get -o name
                            } else {
                               ""
                            }
                            let binary_name = if ($bin_name | is-not-empty) {
                                $bin_name
                            } else {
                                $package_name
                            }

                            # Return record directly
                            {
                                crate_name: $package_name,
                                binary_name: $binary_name,
                                crate_path: $member_path,
                                use_wrapper: $use_wrapper,
                                install_mode: $install_mode,
                                config_src: $config_src
                            }
                        }
                    }
                }
            }
            | compact
        )
        return $binaries
    } catch { |err|
        print $"❌ Failed to discover binaries: ($err)"
        return []
    }
}

def deploy_libs [binaries: list] {
    # Copy syntaxis-lib.nu and target-specific scripts/libs to ~/.config/syntaxis/scripts/
    # so wrappers can import them via NU_LIB_DIRS
    # Only archives/backs up scripts for the specific binaries being installed (NOT the shared lib)
    let config_scripts_dir = ($env.HOME + "/.config/syntaxis/scripts")
    let config_dir = ($env.HOME + "/.config/syntaxis")
    let scripts_dir = ($env.PWD + "/scripts")

    try {
        # Create config scripts directory
        ^mkdir -p $config_scripts_dir

        # Copy common library WITHOUT backup (shared across all binaries)
        let lib_path = ($scripts_dir + "/syntaxis-lib.nu")
        if ($lib_path | path exists) {
            let lib_dest = ($config_scripts_dir + "/syntaxis-lib.nu")
            # Deploy without backup - syntaxis-lib.nu is shared and shouldn't be archived on every install
            ^cp $lib_path $lib_dest
            print "  ✅ Deployed syntaxis-lib.nu to config (no backup)"
        } else {
            print $"  ⚠️  syntaxis-lib.nu not found at ($lib_path)"
        }

        # Extract binary names from the binaries list to determine which scripts to deploy
        let binary_names = ($binaries | each { |b| $b.binary_name })

        # Copy target-specific libraries and scripts only for binaries being installed
        # For each binary with binary_name "syntaxis-tui", deploy: syntaxis-tui.nu and syntaxis-tui-lib.nu
        # These ARE backed up to archives before overwriting
        let target_files = (glob ($scripts_dir + "/syntaxis-*.nu") | compact)
        if ($target_files | length) > 0 {
            for file_path in $target_files {
                let file_name = ($file_path | path basename)

                # Skip the shared library (already handled above without backup)
                if ($file_name == "syntaxis-lib.nu") {
                    continue
                }

                # Check if this script belongs to any of the binaries being installed
                # binary_name already includes "syntaxis-" prefix (e.g., "syntaxis-tui")
                # so we check for exact filename matches like "syntaxis-tui.nu" and "syntaxis-tui-lib.nu"
                let should_deploy = ($binary_names | any { |bn|
                    ($file_name == $"($bn).nu") or ($file_name == $"($bn)-lib.nu")
                })

                if $should_deploy {
                    let script_dest = ($config_scripts_dir + "/" + $file_name)
                    # Backup existing script if it exists (to parent config_dir archives)
                    backup_if_exists $script_dest $config_dir
                    ^cp $file_path $script_dest
                    print $"  ✅ Deployed ($file_name)"
                }
            }
        } else {
            print "  ℹ️  No target-specific scripts found"
        }
    } catch { |err|
        print $"  ⚠️  Failed to deploy libs: ($err)"
    }
}

def install_binary [item: record , rm_target: bool] {
    let display_name = $"(ansi blue)($item.crate_name)(ansi reset)"
    print $"📦 Building ($display_name) [from: ($item.crate_path) wrapper: ($item.use_wrapper) mode: ($item.install_mode)] config: ($item.config_src)] ..."

    try {
        if ($item.install_mode == "cargo") or ($item.install_mode | str starts-with "cargo") {
            # Standard cargo install
            cargo install --path $item.crate_path out+err> /dev/null
            print $"✅ ($display_name): Installed successfully"
        } else if ($item.install_mode == "leptos") or ($item.install_mode | str starts-with "leptos") {
            # Leptos CSR build using build-dashboard.nu script
            print $"  🎨 Building Leptos WASM dashboard..."

            # Call build-dashboard.nu to handle the full build (CSS + WASM)
            # This script must be run from core/ directory
            try {
                cd core
                nu ../scripts/core/build-dashboard.nu build out+err> /dev/null
                cd ..
                print $"  ✅ Leptos WASM build completed"
            } catch { |err|
                cd ..
                print $"  ❌ Leptos WASM build failed: ($err)"
                return {
                    success: false,
                    crate_name: $item.crate_name,
                    binary_name: $item.binary_name,
                    crate_path: $item.crate_path,
                    reason: $"Leptos build failed: ($err)"
                }
            }

            # Deploy artifacts to public folder
            print $"  📦 Deploying Leptos artifacts for ($display_name)..."
            let deployment_ok = (deploy_leptos_artifacts)

            if not $deployment_ok {
                print $"  ⚠️  Leptos artifact deployment had issues (build output may not exist yet)"
                # Don't fail installation - artifacts will be available after first build
            }
        } else {
            print $"⚠️  Unknown install_mode: ($item.install_mode), skipping"
            return {
                success: false,
                crate_name: $item.crate_name,
                binary_name: $item.binary_name,
                crate_path: $item.crate_path,
                reason: $"Unknown install_mode: ($item.install_mode)"
            }
        }

        if $rm_target {
            let target_dir = "target"
            if ($target_dir | path exists) {
                rm -r $target_dir
                print $"  🧹 Cleaned up build artifacts for ($display_name)"
            }
        }

        return {
            success: true,
            crate_name: $item.crate_name,
            binary_name: $item.binary_name,
            crate_path: $item.crate_path
        }
    } catch { |err|
        print $"❌ ($display_name): Build failed - ($err)"
        return {
            success: false,
            crate_name: $item.crate_name,
            binary_name: $item.binary_name,
            crate_path: $item.crate_path,
            reason: ($err | to_string)
        }
    }
}

def deploy_leptos_artifacts [] {
    # Deploy compiled Leptos artifacts to public directory
    # This is called AFTER build-dashboard.nu has generated artifacts in core/target/site/
    let home = $env.HOME
    let public_dir = $"($home)/.config/syntaxis/public"
    let leptos_build_dir = "core/target/site"

    print "    📂 Preparing artifact deployment..."

    # Create public directory if doesn't exist
    try {
        ^mkdir -p $public_dir
    } catch { |err|
        print $"    ❌ Failed to create public directory: ($err)"
        return false
    }

    # Check if Leptos build artifacts exist
    if not ($leptos_build_dir | path exists) {
        print $"    ⚠️  Leptos build artifacts not found at: ($leptos_build_dir)"
        print $"    📌 This is expected if build-dashboard.nu hasn't run yet"
        return false
    }

    # Verify artifact structure before copying
    print "    🔍 Verifying artifact structure..."
    mut all_artifacts_present = true

    if not ("($leptos_build_dir)/index.html" | path exists) {
        print $"    ⚠️  index.html not found"
        $all_artifacts_present = false
    } else {
        print $"    ✅ index.html verified"
    }

    if not ("($leptos_build_dir)/pkg" | path exists) {
        print $"    ⚠️  WASM artifacts (pkg/) not found"
        $all_artifacts_present = false
    } else {
        let wasm_files = (glob $"($leptos_build_dir)/pkg/**/*.wasm" | length)
        print ($"    ✅ WASM artifacts verified " + ($wasm_files | into string) + " files")
    }

    if not ("($leptos_build_dir)/styles/app.css" | path exists) {
        print $"    ⚠️  CSS file (styles/app.css) not found"
        $all_artifacts_present = false
    } else {
        let size_bytes = (stat $"($leptos_build_dir)/styles/app.css" | get size)
        let size_kb = (($size_bytes / 1024) | math round --precision 2)
        print ($"    ✅ CSS verified " + ($size_kb | into string) + "KB")
    }

    if not $all_artifacts_present {
        print $"    ❌ Some artifacts are missing. Cannot deploy."
        return false
    }

    # Copy Leptos artifacts to public directory
    print "    📥 Copying artifacts to ($public_dir)..."
    try {
        # Copy all contents of leptos_build_dir to public_dir
        # Using -r for recursive, and ensuring we copy the contents, not the directory itself
        ^cp -r $"($leptos_build_dir)/." $"($public_dir)/"

        # Verify deployment
        if ("($public_dir)/index.html" | path exists) {
            print $"    ✅ Deployed Leptos artifacts successfully"
            print $"       - index.html: SPA entry point"
            print $"       - pkg/: WASM binaries and JS bindings"
            print $"       - styles/app.css: Generated CSS"
            return true
        } else {
            print $"    ❌ Deployment verification failed"
            return false
        }
    } catch { |err|
        print $"    ❌ Failed to deploy Leptos artifacts: ($err)"
        return false
    }
}

def deploy_leptos_dashboard [] {
    # Build and deploy Leptos CSR dashboard
    print ""
    print "🚀 Building and deploying Leptos CSR dashboard..."

    let home = $env.HOME
    let public_dir = $"($home)/.config/syntaxis/public"
    let leptos_build_dir = "core/crates/client/target/site"

    # Create public directory
    ^mkdir -p $public_dir
    print $"📂 Created public directory: ($public_dir)"

    # Build Leptos CSR (if cargo-leptos is available)
    try {
        print "  Building Leptos CSR..."
        cd core/crates/dashboard-client
        cargo build --release out+err> /dev/null
        cd ../../..

        if ($leptos_build_dir | path exists) {
            # Copy compiled site to public directory
            cp -r $"($leptos_build_dir)/*" $public_dir
            print $"  ✅ Deployed Leptos site to ($public_dir)"
        } else {
            print $"  ⚠️  Leptos build directory not found at ($leptos_build_dir)"
            print $"     Ensure cargo-leptos is installed: cargo install cargo-leptos"
        }
    } catch { |err|
        print $"  ⚠️  Leptos build skipped: ($err)"
        print $"     You can manually build with: cargo leptos build --release"
    }
}

def backup_if_exists [path: string, archive_root: string] {
    # Move existing file to timestamped archive folder if it exists
    # archive_root: parent directory to contain archives/YYYY-MM-DD-HH-MM/
    if ($path | path exists) {
        let filename = ($path | path basename)
        try {
            # Create archive directory with timestamp (YYYY-MM-DD-HH-MM)
            let now = (date now | format date "%Y-%m-%d-%H-%M")
            let archive_dir = ($archive_root + "/archives/" + $now)

            # Create archive directory if it doesn't exist
            ^mkdir -p $archive_dir

            # Move file to archive
            let backup_path = ($archive_dir + "/" + $filename)
            ^mv $path $backup_path

            print $"    ⚠️  Previous version of ($filename) exists"
            print $"    📦 Archived to: archives/($now)/"
            print $"    ℹ️  Previous version preserved - you can restore if needed"
        } catch { |err|
            print $"    ⚠️  Failed to archive ($filename): ($err)"
        }
    }
}

def deploy_config_for_binary [crate_name: string, config_src: string] {
    # Deploy configuration from config_src (relative to project root) to ~/.config/syntaxis/
    # Backs up existing configs to archives/YYYY-MM-DD-HH-MM/ before installing new ones
    if ($config_src | str length) == 0 {
        # No config to deploy
        return
    }

    let config_dir = ($env.HOME + "/.config/syntaxis")
    # config_src is relative to project root
    let src_path = $config_src

    # Ensure source exists
    if not ($src_path | path exists) {
        print $"    ⚠️  Config source not found: ($src_path)"
        return
    }

    try {
        # Create config directory if doesn't exist
        ^mkdir -p $config_dir

        # Copy config files from source to config directory
        if ($src_path | path type) == "dir" {
            # For directories, backup existing files in config_dir that come from src
            let files_to_deploy = (glob $"($src_path)/*" | compact)
            for file_path in $files_to_deploy {
                let filename = ($file_path | path basename)
                let dest_path = $"($config_dir)/($filename)"
                # Backup existing file if it exists
                backup_if_exists $dest_path $config_dir
            }
            # Copy all files from source directory
            ^cp -r $"($src_path)/." $"($config_dir)/" out+err> /dev/null
            print $"    ✅ Deployed config from ($src_path)"
        } else if ($src_path | path type) == "file" {
            # Copy single file
            let filename = ($src_path | path basename)
            let dest_path = $"($config_dir)/($filename)"
            # Backup existing file if it exists
            backup_if_exists $dest_path $config_dir
            ^cp $src_path $dest_path
            print $"    ✅ Deployed config: ($filename)"
        }
    } catch { |err|
        print $"    ⚠️  Failed to deploy config: ($err)"
    }
}

def deploy_configurations [] {
    # Create config directories and default configs
    let config_dir = ($env.HOME + "/.config/syntaxis")
    ^mkdir -p $config_dir
    ^mkdir -p $"($config_dir)/public"
    ^mkdir -p $"($config_dir)/features"

    print ""
    print "📋 Deploying global configuration structure..."

    # Create default main config if doesn't exist
    let default_main_config = $"($config_dir)/syntaxis.toml"
    if not ($default_main_config | path exists) {
        let config_content = "# Syntaxis Global Configuration
# Main config file for all syntaxis applications

[server]
host = \"127.0.0.1\"
port = 8080

[api]
host = \"127.0.0.1\"
port = 8080

[database]
url = \"sqlite:///($env.HOME)/.local/share/syntaxis/syntaxis.db\"

[logging]
level = \"info\"
format = \"compact\"
"
        $config_content | save $default_main_config
        print $"  ✅ Created: syntaxis.toml"
    }

    print ""
    print $"📂 Configuration directories created at: ($config_dir)"
}

def create_wrapper_for_binary [binary_name: string, use_wrapper: bool, config_src: string] {
    # Create wrapper script for a single binary
    # Backs up existing wrapper scripts to archives/YYYY-MM-DD-HH-MM/ before installing new ones
    if not $use_wrapper {
        return
    }

    let cargo_bin = ($env.HOME + "/.cargo/bin")
    let config_dir = ($env.HOME + "/.config/syntaxis")
    let binary_path = ($cargo_bin + "/" + $binary_name)
    let real_binary_path = ($cargo_bin + "/" + $binary_name + ".real")

    # Check if binary exists
    if ($binary_path | path exists) {
        let display_name = $"(ansi blue)($binary_name)(ansi reset)"
        try {
            # Rename actual binary to .real (don't backup the binary, it's rebuilt each time)
            ^mv $binary_path $real_binary_path

            # Create bash wrapper script that calls the NuShell wrapper
            # The bash script sets NU_LIB_DIRS and calls the .nu script
            let bash_wrapper = $"#!/bin/bash
# Bash wrapper for ($binary_name)
# Calls the NuShell wrapper with proper library paths

export NU_LIB_DIRS=\"\$HOME/.config/syntaxis/scripts\"
exec nu \"\$HOME/.config/syntaxis/scripts/($binary_name).nu\" \"\$@\""

            $bash_wrapper | save --force $binary_path
            ^chmod +x $binary_path

            print $"    ✅ Created wrapper for ($display_name)"
            print $"       Location: ($binary_path)"
        } catch { |err|
            print $"    ⚠️  Failed to create wrapper for ($display_name): ($err)"
        }
    }
}

def needs_wrapper [has_clap: bool, has_wasm: bool, has_leptos: bool] {
    # RULE: Determine if binary needs a wrapper script
    # - If HAS clap → NO wrapper (user provides args via CLI)
    # - If NO clap AND NO wasm AND NO leptos → WRAPPER needed (auto-discovery)
    # - If has wasm or leptos → WASM/frontend, no wrapper needed

    if $has_clap {
        # Has Clap: user provides arguments explicitly
        return false
    } else if ($has_wasm or $has_leptos) {
        # WASM frontend: no CLI wrapper needed
        return false
    } else {
        # No Clap, no WASM/Leptos: needs wrapper for auto-discovery config
        return true
    }
}

def register_installations [installed_binaries: list] {
    # Create manifest file tracking with version and integrity info
    let manifest_path = ".syntaxis/manifest.toml"
    ^mkdir -p ".syntaxis"

    print ""
    print "📊 Registering installations..."

    let now = (date now | format date "%Y-%m-%d %H:%M:%S")
    let home = $env.HOME
    let config_dir = $"($home)/.config/syntaxis"

    # Build binaries section
    let binaries_toml = (
        $installed_binaries
        | each { |binary|
            let section = $"[binaries.($binary.crate_name)]
installed = true
version = \"0.1.0\"
installed_at = \"($now)\"
"
            $section
        }
        | str join "\n"
    )

    # Create comprehensive manifest
    let manifest_content = $"# syntaxis Installation Manifest
# Auto-generated during installation
# Use this to track versions, verify integrity, and enable rollback
# Generated: ($now)

[installation]
created_at = \"($now)\"
last_updated = \"($now)\"
installation_root = \"(pwd)\"
ecosystem_version = \"0.1.0\"

($binaries_toml)

[configurations]
config_dir = \"($config_dir)\"
public_dir = \"($config_dir)/public\"
data_dir = \"($home)/.local/share/syntaxis\"

[leptos_dashboard]
# Leptos WASM dashboard configuration
deployed_at = \"($now)\"
artifacts_location = \"($config_dir)/public\"
wasm_target = \"wasm32-unknown-unknown\"
build_tool = \"trunk\"
css_generator = \"unocss\"
api_url_config = \"auto-detect [window.SYNTAXIS_CONFIG]\"

[environment]
SYNTAXIS_CONFIG_DIR = \"($config_dir)\"
SYNTAXIS_DATA_DIR = \"($home)/.local/share/syntaxis\"
SYNTAXIS_PUBLIC_DIR = \"($config_dir)/public\"

[verification]
all_binaries_installed = true
all_configs_deployed = true
wrappers_created = true
leptos_dashboard_deployed = true
manifest_version = \"1.0\"
"

    try {
        $manifest_content | save --force $manifest_path
        print $"  ✅ Registered installations in manifest"
        print $"  📝 Manifest saved to: .syntaxis/manifest.toml"
    } catch {
        print $"  ⚠️  Failed to create manifest"
    }
}

def show_status [] {
    print ""
    print "📊 Installation Status"
    print ""

    let cargo_bin = ($env.HOME + "/.cargo/bin")
    let binaries = (discover_binary_crates)

    if ($binaries | length) == 0 {
        print "⚠️  No binary crates found"
        return
    }

    let status_rows = (
        $binaries | each { |binary|
            let bin_path = ($cargo_bin + "/" + $binary.binary_name)
            let status = if ($bin_path | path exists) {
                "✅ Installed"
            } else {
                "⚠️ Not installed"
            }
            {
                binary: $binary.binary_name,
                crate: $binary.crate_name,
                status: $status,
                path: $binary.crate_path,
                wrapper: $binary.use_wrapper,
                mode: $binary.install_mode,
                config: $binary.config_src
            }
        }
    )

    print ($status_rows | table -i false | ansi strip)

    print ""
    print "📝 Note: Add ~/.cargo/bin to your PATH if binaries show as not installed"
    print $"   export PATH=\"\$HOME/.cargo/bin:\$PATH\""
    print ""
}

def resolve_binary_from_action [action: string, all_binaries: list] {
    # Resolve binary from action, supporting:
    # 1. Full binary name: "syntaxis-cli"
    # 2. Short name: "cli" (extracts last component from crate_path core/crates/cli)
    # 3. Crate name: "syntaxis-cli"

    let matching = (
        $all_binaries | where { |b|
            (($b.binary_name == $action) or
             ($b.crate_name == $action) or
             (($b.crate_path | path basename) == $action))
        }
    )

    return $matching
}

def list_binaries [] {
    print ""
    print "📦 Available Binaries (Auto-discovered)"
    print ""

    let binaries = (discover_binary_crates)

    if ($binaries | length) == 0 {
        print "⚠️  No binary crates found in workspace"
        return
    }

    print ($binaries | table -i false | ansi strip)
    #$binaries | each { |binary|
    #    print $"  ($binary.crate_name)  → ($binary.crate_path)"
    #}

    print ""
}

def main [
    action?: string                    # Action: "all", "status", "list", or binary name
    --rm-target                        # Remove target directories after build (default: false)
] {
    show_logo
    # Show help if no arguments or help requested
    if ($action == null or $action == "help") {
        print_help
        return
    }

    # Handle status command
    if ($action == "status") {
        show_status
        return
    }

    # Handle list command
    if ($action == "list") {
        list_binaries
        return
    }

    # Discover available binaries
    let all_binaries = (discover_binary_crates)

    if ($all_binaries | length) == 0 {
        print "❌ No binary crates found in workspace"
        return
    }

    # Determine which binaries to install
    let binaries_to_install = if ($action == "all") {
        $all_binaries
    } else {
        # Find matching binary by binary_name, crate_name, or path
        let matching = (resolve_binary_from_action $action $all_binaries)

        if ($matching | length) == 0 {
            print $"❌ Unknown binary: ($action)"
            print ""
            print "Available binaries:"
            let binaries_list = $all_binaries
            $binaries_list | each { |b|
                let short_name = ($b.crate_path | path basename)
                let display = $"  - ($short_name) \(or ($b.binary_name), or ($b.crate_name)\)"
                print $display
            }
            return
        }

        $matching
    }

    print ""
    let binaries_count = ($binaries_to_install | length)
    print $"🔨 Installing ($binaries_count) binary crate\(s\)... Remove target: ($rm_target)"
    print ""

    # Deploy library files first, before creating wrappers
    # This ensures wrappers can import the libs when needed
    print "📋 Deploying library files..."
    deploy_libs $binaries_to_install
    print ""

    # Install each binary with immediate wrapper and config setup
    # Stop on first failure
    let install_result = (
        $binaries_to_install | reduce {|binary, acc|
            if $acc.failed {
                # Already failed, skip remaining binaries
                $acc
            } else {
                let display_name = $"(ansi blue)($binary.crate_name)(ansi reset)"
                print $"📦 Step 1/3: Building ($display_name)..."
                let result = (install_binary $binary $rm_target)

                if $result.success {
                    # Build succeeded - immediately set up wrapper and config
                    let config_dir = ($env.HOME + "/.config/syntaxis")
                    let cargo_bin = ($env.HOME + "/.cargo/bin")

                    print $"📦 Step 2/3: Setting up wrapper and configuration..."
                    print $"   Config Path: ($config_dir)"
                    print $"   Wrapper Path: ($cargo_bin)"

                    # Create wrapper if applicable
                    if $binary.use_wrapper {
                        create_wrapper_for_binary $binary.binary_name $binary.use_wrapper $binary.config_src
                    }

                    # Deploy config if applicable
                    if ($binary.config_src | str length) > 0 {
                        deploy_config_for_binary $binary.crate_name $binary.config_src
                    }

                    print $"📦 Step 3/3: Registered ($display_name)"
                    print ""
                    {
                        installed: ($acc.installed | append $result)
                        failed: false
                    }
                } else {
                    # Installation failed - stop processing
                    print "❌ Installation failed - stopping further installations"
                    print ""
                    {
                        installed: $acc.installed
                        failed: true
                    }
                }
            }
        } -f {installed: [], failed: false}
    )

    let installed = $install_result.installed
    let installation_failed = $install_result.failed

    # Deploy additional global resources only if any binaries were installed
    if ($installed | length) > 0 {
        # deploy_leptos_dashboard

        # Deploy default configuration (may already exist per-binary)
        print ""
        print "📋 Deploying global configuration..."
        let config_dir = ($env.HOME + "/.config/syntaxis")
        ^mkdir -p $config_dir
        ^mkdir -p $"($config_dir)/features"
        print $"  ✅ Ensured config directory: ($config_dir)"

        register_installations $installed
    }

    # Summary
    print ""
    print "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    let installed_count = ($installed | length)
    let total_count = ($binaries_to_install | length)
    print $"✨ Summary: ($installed_count)/($total_count) binaries installed"

    if $installation_failed {
        print "❌ Installation stopped due to failure"
    } else if $installed_count == $total_count {
        print "🎉 All binaries installed successfully!"
    } else if $installed_count > 0 {
        let failed = ($total_count - $installed_count)
        print $"⚠️  ($failed) installation(s) failed"
    } else {
        print "❌ No binaries were installed"
    }

    print ""
    if ($installed | length) > 0 {
        print "Next steps:"
        $installed | each { |binary|
            print $"  ($binary.crate_name) --help        # Get help"
        }
    }
    print ""
}
