use anyhow::Result;
use console::style;
use inquire::{MultiSelect, Text};
use super::app_manager::AppManager;
pub async fn run_interactive_installer() -> Result<()> {
println!(
"\n{} {} {}",
style("đ").blue(),
style("Vibe App Installer").cyan().bold(),
style("- Install developer tools and applications").dim()
);
let app_manager = AppManager::new().await?;
let available_managers = app_manager.get_available_managers();
if available_managers.is_empty() {
anyhow::bail!("No package managers available. Please install Homebrew or Cargo.");
}
println!("\n{} Checking installed apps...", style("đ").blue());
let mut statuses = app_manager.get_all_status().await?;
statuses.sort_by(|a, b| a.app_name.cmp(&b.app_name));
let (installed, available): (Vec<_>, Vec<_>) = statuses.iter().partition(|s| s.installed);
if !installed.is_empty() {
println!(
"\n{} {} {}",
style("â
").green(),
style("Already Installed").green().bold(),
style(format!("({})", installed.len())).dim()
);
for status in &installed {
let app = app_manager
.list_available()
.iter()
.find(|a| a.name == status.app_name)
.unwrap();
let version_str = status
.version
.as_ref()
.map(|v| format!(" ({v})"))
.unwrap_or_default();
let indicator = if !status.is_managed {
style("â ī¸").yellow()
} else {
style("âĸ").dim()
};
let mut line = format!(
" {} {}{}",
indicator,
style(&app.display_name).green(),
style(&version_str).dim()
);
if !status.is_managed {
if let Some(path) = &status.installed_path {
line.push_str(&format!(" - {}", style(path).dim()));
}
}
println!("{line}");
}
}
if available.is_empty() {
println!(
"\n{} All available apps are already installed!",
style("đ").green()
);
return Ok(());
}
let mut options = Vec::new();
let mut app_map = std::collections::HashMap::new();
for status in &available {
if !status.available_managers.is_empty() {
let app = app_manager
.list_available()
.iter()
.find(|a| a.name == status.app_name)
.unwrap();
let option = format!("{} - {}", app.display_name, app.description);
options.push(option.clone());
app_map.insert(option, &app.name);
}
}
if options.is_empty() {
println!(
"\n{} No apps available to install with current package managers.",
style("â ī¸").yellow()
);
return Ok(());
}
println!(
"\n{} {} {}",
style("đĻ").blue(),
style("Available to Install").blue().bold(),
style(format!("({})", options.len())).dim()
);
let selected = MultiSelect::new(
"Select apps to install (use Space to select, Enter to confirm):",
options.clone(),
)
.with_page_size(15)
.prompt()?;
if selected.is_empty() {
println!(
"\n{} No apps selected for installation.",
style("âšī¸").blue()
);
return Ok(());
}
println!(
"\n{} Selected {} app(s) for installation:",
style("đ").blue(),
style(selected.len()).cyan()
);
let mut app_names = Vec::new();
for selection in &selected {
if let Some(app_name) = app_map.get(selection) {
app_names.push((*app_name).to_string());
let app = app_manager
.list_available()
.iter()
.find(|a| a.name == **app_name)
.unwrap();
println!(" {} {}", style("âĸ").dim(), style(&app.display_name).cyan());
}
}
let confirm = Text::new("\nProceed with installation? (Y/n)")
.with_default("y")
.prompt()?;
if confirm.to_lowercase() == "n" {
println!("{} Installation cancelled.", style("â").red());
return Ok(());
}
println!("\n{} Starting installation...", style("đ").blue());
app_manager.install_multiple(&app_names).await?;
println!(
"\n{} Done! Your selected apps have been installed.",
style("đ").green()
);
Ok(())
}