#!/usr/bin/env nu

# Install Full Nushell Distribution Script
# Advanced nu-based installer called after bootstrap gets nushell running
# Provides plugin selection, configuration, and integration with existing nu environment

use lib/common_lib.nu [
    log_info, log_error, log_success, log_warn, log_debug,
    get_current_platform, ensure_dir, copy_file
]

def main [
    --install-dir (-i): string = ""      # Installation directory override
    --config-dir (-c): string = ""       # Configuration directory override
    --plugins (-p): list<string> = []    # Specific plugins to install
    --all-plugins (-a): bool = false     # Install all available plugins
    --no-register: bool = false          # Don't register plugins with nushell
    --register-only: bool = false        # Only register plugins, don't copy binaries
    --verify: bool = false               # Verify installation after completion
    --test: bool = false                 # Run in test mode (dry run)
    --config-backup: bool = true         # Backup existing configuration
    --interactive (-I): bool = false     # Interactive mode for plugin selection
    --update (-u): bool = false          # Update existing installation
] {
    log_info "🚀 Nushell Full Distribution Advanced Installer"
    log_info "=============================================="

    if $test {
        log_warn "🧪 Running in test mode (dry run)"
    }

    # Validate environment
    validate_installation_environment $test

    # Get installation paths
    let paths = get_installation_paths $install_dir $config_dir

    log_info ""
    log_info "📍 Installation Configuration:"
    log_info $"   Binary directory: ($paths.bin_dir)"
    log_info $"   Config directory: ($paths.config_dir)"
    log_info $"   Plugins directory: ($paths.plugins_dir)"

    if $register_only {
        log_info "   Mode: Register existing binaries only"
    } else if $no_register {
        log_info "   Mode: Copy binaries without registration"
    } else {
        log_info "   Mode: Copy binaries and register plugins"
    }

    # Check if this is an update
    let is_update = $update or (check_existing_installation $paths)
    if $is_update {
        log_info "   Type: Update existing installation"
    } else {
        log_info "   Type: New installation"
    }

    # Plugin selection
    let selected_plugins = if $interactive {
        select_plugins_interactive
    } else if ($plugins | length) > 0 {
        $plugins
    } else if $all_plugins {
        get_available_plugins | get name
    } else {
        get_recommended_plugins
    }

    log_info ""
    log_info $"🔌 Selected plugins: (($selected_plugins | length))"
    $selected_plugins | each {|plugin| log_info $"   - ($plugin)"}

    if not $test {
        # Backup existing configuration if requested
        if $config_backup and $is_update {
            backup_existing_config $paths.config_dir
        }

        # Create installation directories
        setup_installation_directories $paths

        # Install binaries
        if not $register_only {
            install_nushell_binary $paths $test
            install_plugin_binaries $selected_plugins $paths $test
        }

        # Install configuration
        install_configuration $paths $is_update $test

        # Register plugins
        if not $no_register {
            register_plugins_with_nushell $selected_plugins $paths $test
        }

        # Verify installation if requested
        if $verify {
            verify_full_installation $paths $selected_plugins
        }

        log_success "✅ Installation completed successfully!"
        print_installation_summary $paths $selected_plugins $is_update
    } else {
        log_info "🧪 Test mode completed - no changes made"
    }
}

# Validate installation environment
def validate_installation_environment [test: bool] {
    log_debug "Validating installation environment..."

    # Check if we're running in nushell
    if ($env.NU_VERSION? | is-empty) {
        log_error "This installer must be run with nushell"
        exit 1
    }

    # Check if we have write permissions to likely installation directories
    let test_dirs = [
        "~/.local/bin",
        "~/.config/nushell",
        "/usr/local/bin"
    ]

    for dir in $test_dirs {
        let expanded_dir = ($dir | path expand)
        if ($expanded_dir | path exists) and not $test {
            try {
                touch $"($expanded_dir)/.write_test"
                rm $"($expanded_dir)/.write_test"
                log_debug $"✅ Write permission confirmed: ($expanded_dir)"
                break
            } catch {
                log_debug $"❌ No write permission: ($expanded_dir)"
            }
        }
    }

    log_success "Environment validation passed"
}

# Get installation paths
def get_installation_paths [install_dir_override: string, config_dir_override: string] {
    let home_dir = ($env.HOME | path expand)

    # Determine binary installation directory
    let bin_dir = if ($install_dir_override | str length) > 0 {
        ($install_dir_override | path expand)
    } else if (which nu | length) > 0 {
        # Use directory where current nu is installed
        (which nu | get 0.path | path dirname)
    } else {
        # Default to ~/.local/bin
        $"($home_dir)/.local/bin"
    }

    # Determine configuration directory
    let config_dir = if ($config_dir_override | str length) > 0 {
        ($config_dir_override | path expand)
    } else if ($env.XDG_CONFIG_HOME? | is-not-empty) {
        $"($env.XDG_CONFIG_HOME)/nushell"
    } else {
        $"($home_dir)/.config/nushell"
    }

    # Plugin directory (for registration)
    let plugins_dir = $"($config_dir)/plugins"

    {
        bin_dir: $bin_dir,
        config_dir: $config_dir,
        plugins_dir: $plugins_dir,
        home_dir: $home_dir
    }
}

# Check if installation already exists
def check_existing_installation [paths: record] -> bool {
    let nu_exists = ($"($paths.bin_dir)/nu" | path exists)
    let config_exists = ($"($paths.config_dir)/config.nu" | path exists)

    $nu_exists or $config_exists
}

# Get available plugins from current directory
def get_available_plugins [] {
    let bin_files = if ("./bin" | path exists) {
        ls ./bin | where name =~ "nu_plugin_" | get name | each {|path| ($path | path basename | str replace ".exe" "")}
    } else {
        []
    }

    $bin_files | each {|name| {name: $name, path: $"./bin/($name)"}}
}

# Get recommended plugins for default installation
def get_recommended_plugins [] {
    let available = (get_available_plugins | get name)
    let recommended = [
        "nu_plugin_clipboard",
        "nu_plugin_hashes",
        "nu_plugin_desktop_notifications",
        "nu_plugin_highlight"
    ]

    # Return intersection of recommended and available
    $recommended | where $it in $available
}

# Interactive plugin selection
def select_plugins_interactive [] {
    log_info "🔌 Plugin Selection"
    log_info "=================="

    let available_plugins = get_available_plugins
    if ($available_plugins | length) == 0 {
        log_warn "No plugins found in package"
        return []
    }

    log_info "Available plugins:"
    $available_plugins | enumerate | each {|item|
        log_info $"  (($item.index + 1)). ($item.item.name)"
    }

    log_info ""
    log_info "Selection options:"
    log_info "  - Enter numbers separated by spaces (e.g., '1 3 4')"
    log_info "  - Enter 'all' for all plugins"
    log_info "  - Enter 'recommended' for recommended plugins"
    log_info "  - Press Enter for recommended plugins"

    let selection = (input "Your selection: ")

    match $selection {
        "all" => {$available_plugins | get name},
        "recommended" | "" => {get_recommended_plugins},
        _ => {
            let indices = ($selection | split row " " | each {|x| ($x | into int) - 1})
            $indices | each {|i| $available_plugins | get $i | get name}
        }
    }
}

# Setup installation directories
def setup_installation_directories [paths: record] {
    log_info "📁 Setting up installation directories..."

    ensure_dir $paths.bin_dir
    ensure_dir $paths.config_dir
    ensure_dir $paths.plugins_dir

    log_success "✅ Installation directories ready"
}

# Install nushell binary
def install_nushell_binary [paths: record, test: bool] {
    log_info "🚀 Installing nushell binary..."

    let current_platform = get_current_platform
    let binary_extension = if ($current_platform | str starts-with "windows") { ".exe" } else { "" }
    let nu_binary = $"./bin/nu($binary_extension)"

    if not ($nu_binary | path exists) {
        log_error $"Nushell binary not found: ($nu_binary)"
        exit 1
    }

    let dest_path = $"($paths.bin_dir)/nu($binary_extension)"

    if not $test {
        copy_file $nu_binary $dest_path

        # Make executable on Unix-like systems
        if not ($current_platform | str starts-with "windows") {
            chmod +x $dest_path
        }
    }

    log_success $"✅ Nushell binary installed: ($dest_path)"
}

# Install plugin binaries
def install_plugin_binaries [plugins: list<string>, paths: record, test: bool] {
    log_info $"🔌 Installing plugin binaries... (($plugins | length))"

    let current_platform = get_current_platform
    let binary_extension = if ($current_platform | str starts-with "windows") { ".exe" } else { "" }

    for plugin in $plugins {
        let plugin_binary = $"./bin/($plugin)($binary_extension)"
        let dest_path = $"($paths.bin_dir)/($plugin)($binary_extension)"

        if ($plugin_binary | path exists) {
            if not $test {
                copy_file $plugin_binary $dest_path

                # Make executable on Unix-like systems
                if not ($current_platform | str starts-with "windows") {
                    chmod +x $dest_path
                }
            }
            log_success $"✅ Installed plugin: ($plugin)"
        } else {
            log_warn $"⚠️  Plugin binary not found: ($plugin_binary)"
        }
    }
}

# Install configuration files
def install_configuration [paths: record, is_update: bool, test: bool] {
    log_info "⚙️  Installing configuration files..."

    let config_files = [
        {src: "./config/default_config.nu", dest: "config.nu", required: true},
        {src: "./config/default_env.nu", dest: "env.nu", required: true},
        {src: "./config/distribution_config.toml", dest: "distribution_config.toml", required: false}
    ]

    for file in $config_files {
        let src_path = $file.src
        let dest_path = $"($paths.config_dir)/($file.dest)"

        if ($src_path | path exists) {
            if ($dest_path | path exists) and not $is_update {
                log_info $"⚠️  Configuration file exists, creating backup: ($file.dest)"
                if not $test {
                    cp $dest_path $"($dest_path).backup.(date now | format date '%Y%m%d_%H%M%S')"
                }
            }

            if not $test {
                copy_file $src_path $dest_path
            }
            log_success $"✅ Installed config: ($file.dest)"
        } else if $file.required {
            log_error $"Required configuration file not found: ($src_path)"
            exit 1
        }
    }
}

# Register plugins with nushell
def register_plugins_with_nushell [plugins: list<string>, paths: record, test: bool] {
    log_info $"📝 Registering plugins with nushell... (($plugins | length))"

    let current_platform = get_current_platform
    let binary_extension = if ($current_platform | str starts-with "windows") { ".exe" } else { "" }

    let registration_commands = []

    for plugin in $plugins {
        let plugin_path = $"($paths.bin_dir)/($plugin)($binary_extension)"

        if ($plugin_path | path exists) or $test {
            let register_cmd = $"plugin add ($plugin_path)"
            $registration_commands = ($registration_commands | append $register_cmd)
            log_success $"✅ Will register: ($plugin)"
        } else {
            log_warn $"⚠️  Cannot register plugin (binary not found): ($plugin)"
        }
    }

    if not $test and ($registration_commands | length) > 0 {
        # Create a temporary script to register plugins
        let register_script = $"($paths.config_dir)/register_plugins_temp.nu"

        let script_content = ([
            "# Temporary plugin registration script",
            "# Generated by install_full_nushell.nu",
            ""
        ] | append $registration_commands | append [
            "",
            "print \"✅ All plugins registered successfully\"",
            $"rm ($register_script)"
        ] | str join "\n")

        $script_content | save $register_script

        log_info "Running plugin registration..."
        try {
            nu $register_script
        } catch {|err|
            log_error $"Plugin registration failed: ($err.msg)"
            log_info "You can register plugins manually later with:"
            $registration_commands | each {|cmd| log_info $"  nu -c '($cmd)'"}
        }
    }
}

# Backup existing configuration
def backup_existing_config [config_dir: string] {
    log_info "💾 Backing up existing configuration..."

    let backup_dir = $"($config_dir)_backup_(date now | format date '%Y%m%d_%H%M%S')"

    if ($config_dir | path exists) {
        cp -r $config_dir $backup_dir
        log_success $"✅ Configuration backed up to: ($backup_dir)"
    } else {
        log_info "No existing configuration to backup"
    }
}

# Verify installation
def verify_full_installation [paths: record, plugins: list<string>] {
    log_info "🔍 Verifying installation..."

    let verification_results = {}

    # Verify nushell binary
    let current_platform = get_current_platform
    let binary_extension = if ($current_platform | str starts-with "windows") { ".exe" } else { "" }
    let nu_path = $"($paths.bin_dir)/nu($binary_extension)"

    let nu_check = if ($nu_path | path exists) {
        try {
            let version = (run-external $nu_path "--version")
            {status: "ok", message: $"Nushell installed: ($version)"}
        } catch {|err|
            {status: "error", message: $"Nushell binary exists but not executable: ($err.msg)"}
        }
    } else {
        {status: "error", message: "Nushell binary not found"}
    }

    $verification_results = ($verification_results | insert "nushell" $nu_check)

    # Verify plugins
    for plugin in $plugins {
        let plugin_path = $"($paths.bin_dir)/($plugin)($binary_extension)"
        let plugin_check = if ($plugin_path | path exists) {
            {status: "ok", message: $"Plugin binary installed: ($plugin)"}
        } else {
            {status: "error", message: $"Plugin binary not found: ($plugin)"}
        }
        $verification_results = ($verification_results | insert $plugin $plugin_check)
    }

    # Verify configuration
    let config_files = ["config.nu", "env.nu"]
    for config_file in $config_files {
        let config_path = $"($paths.config_dir)/($config_file)"
        let config_check = if ($config_path | path exists) {
            {status: "ok", message: $"Configuration installed: ($config_file)"}
        } else {
            {status: "error", message: $"Configuration not found: ($config_file)"}
        }
        $verification_results = ($verification_results | insert $config_file $config_check)
    }

    # Display verification results
    log_info "📊 Verification Results:"
    log_info "======================"

    let all_ok = true
    for item in ($verification_results | items) {
        let status_icon = if $item.value.status == "ok" { "✅" } else { "❌"; $all_ok = false }
        log_info $"  ($status_icon) ($item.key): ($item.value.message)"
    }

    if $all_ok {
        log_success "🎉 All verification checks passed!"
    } else {
        log_warn "⚠️  Some verification checks failed"
    }
}

# Print installation summary
def print_installation_summary [paths: record, plugins: list<string>, is_update: bool] {
    let action = if $is_update { "Updated" } else { "Installed" }

    log_info ""
    log_info $"📊 Installation Summary"
    log_info "======================"
    log_info $"Action: ($action) Nushell Full Distribution"
    log_info $"Binary directory: ($paths.bin_dir)"
    log_info $"Config directory: ($paths.config_dir)"
    log_info $"Plugins installed: (($plugins | length))"

    if ($plugins | length) > 0 {
        $plugins | each {|plugin| log_info $"  - ($plugin)"}
    }

    log_info ""
    log_info "🚀 Next Steps:"
    log_info "============="
    log_info $"1. Add ($paths.bin_dir) to your PATH if not already present"
    log_info "2. Restart your terminal or run: source ~/.bashrc (or equivalent)"
    log_info "3. Test installation: nu --version"
    log_info "4. Start using nushell: nu"

    # Check if PATH update is needed
    let path_entries = ($env.PATH | split row ":")
    if not ($paths.bin_dir in $path_entries) {
        log_info ""
        log_warn "⚠️  PATH Update Required"
        log_info $"Add this line to your shell profile (~/.bashrc, ~/.zshrc, etc.):"
        log_info $"export PATH=\"($paths.bin_dir):$PATH\""
    }
}