use anyhow::{Context, Result};
use colored::Colorize;
use dialoguer::{Confirm, Input};
use std::path::PathBuf;
use firecloud_net::FireCloudNode;
use crate::config::FireCloudConfig;
pub async fn run(data_dir: PathBuf) -> Result<()> {
println!("\n{}", "🚀 Welcome to FireCloud".bold().cyan());
println!("{}", "─".repeat(50).dimmed());
let config_path = data_dir.join("config.toml");
if config_path.exists() {
let reinit = Confirm::new()
.with_prompt("FireCloud is already initialized. Re-initialize?")
.default(false)
.interact()?;
if !reinit {
println!("\n{}", "✓ Setup cancelled".yellow());
return Ok(());
}
}
std::fs::create_dir_all(&data_dir)
.context("Failed to create data directory")?;
println!("\n{} Initializing FireCloud...", "âš™".cyan());
std::fs::create_dir_all(data_dir.join("chunks"))?;
std::fs::create_dir_all(data_dir.join("manifests"))?;
use firecloud_crypto::KeyPair;
let keypair = KeyPair::generate();
let public_key = keypair.public_key_bytes();
let identity_path = data_dir.join("identity.key");
if !identity_path.exists() {
std::fs::write(&identity_path, keypair.to_bytes())?;
}
println!("{} Identity created: {}", "✓".green(), hex::encode(public_key).yellow());
println!("{} Data directory created: {}", "✓".green(), data_dir.display().to_string().dimmed());
println!("\n{}", "Storage Provider Setup".bold());
let provide_storage = Confirm::new()
.with_prompt("Would you like to provide storage to the network?")
.default(false)
.interact()?;
let mut config = FireCloudConfig::default();
if provide_storage {
let quota_str: String = Input::new()
.with_prompt("How much storage would you like to provide? (e.g., 10GB, 500MB)")
.default("10GB".to_string())
.interact_text()?;
let quota_bytes = parse_size("a_str)
.context("Invalid storage size format. Use formats like: 10GB, 500MB, 1TB")?;
config.storage_quota = Some(quota_bytes);
println!("\n{} Storage reservation: {} allocated",
"✓".green(),
format_size(quota_bytes).yellow());
match check_disk_space(&data_dir, quota_bytes) {
Ok(available) => {
println!("{} Available disk space: {}",
"ℹ".blue(),
format_size(available).dimmed());
}
Err(e) => {
eprintln!("{} Warning: Could not check disk space: {}", "âš ".yellow(), e);
}
}
} else {
println!("\n{} You can enable storage sharing later using: {}",
"ℹ".blue(),
"firecloud mod".cyan());
}
config.save(&config_path)
.context("Failed to save configuration")?;
println!("\n{}", "─".repeat(50).dimmed());
println!("{}", "🎉 Setup complete!".bold().green());
println!("\n{}", "Next steps:".bold());
println!(" • Upload a file: {}", "firecloud upload <file>".cyan());
println!(" • View network: {}", "firecloud network".cyan());
if provide_storage {
println!(" • Check usage: {}", "firecloud usage".cyan());
}
println!();
Ok(())
}
fn parse_size(s: &str) -> Result<u64> {
let s = s.trim().to_uppercase();
let (num_str, multiplier) = if s.ends_with("TB") {
(&s[..s.len()-2], 1_000_000_000_000u64)
} else if s.ends_with("GB") {
(&s[..s.len()-2], 1_000_000_000u64)
} else if s.ends_with("MB") {
(&s[..s.len()-2], 1_000_000u64)
} else if s.ends_with("KB") {
(&s[..s.len()-2], 1_000u64)
} else if s.ends_with('B') {
(&s[..s.len()-1], 1u64)
} else {
(s.as_str(), 1u64)
};
let num: f64 = num_str.trim().parse()
.context("Invalid number in size specification")?;
Ok((num * multiplier as f64) as u64)
}
fn format_size(bytes: u64) -> String {
const KB: u64 = 1_000;
const MB: u64 = 1_000_000;
const GB: u64 = 1_000_000_000;
const TB: u64 = 1_000_000_000_000;
if bytes >= TB {
format!("{:.2} TB", bytes as f64 / TB as f64)
} else if bytes >= GB {
format!("{:.2} GB", bytes as f64 / GB as f64)
} else if bytes >= MB {
format!("{:.2} MB", bytes as f64 / MB as f64)
} else if bytes >= KB {
format!("{:.2} KB", bytes as f64 / KB as f64)
} else {
format!("{} B", bytes)
}
}
fn check_disk_space(path: &PathBuf, _required: u64) -> Result<u64> {
#[cfg(unix)]
{
use std::os::unix::fs::MetadataExt;
let metadata = std::fs::metadata(path)
.or_else(|_| std::fs::metadata(path.parent().unwrap_or(path)))?;
Ok(metadata.blocks() * 512) }
#[cfg(not(unix))]
{
Ok(1_000_000_000_000) }
}