firecloud-cli 0.2.0

Command-line interface for FireCloud P2P messaging and file sharing
//! Storage provider commands - Manage storage provision and statistics

use anyhow::Result;
use firecloud_storage::QuotaManager;
use std::path::PathBuf;
use tracing::info;
use crate::format::format_bytes;

/// Initialize storage provider configuration
pub async fn init(data_dir: PathBuf, quota_gb: Option<u64>) -> Result<()> {
    println!("🔧 FireCloud Storage Provider Setup");
    println!();
    
    // Ask if user wants to provide storage
    let provide_storage = if let Some(_) = quota_gb {
        true
    } else {
        println!("Do you want to provide storage space for other users?");
        println!("(You can earn credits and help the network)");
        print!("Provide storage? [Y/n]: ");
        
        let mut input = String::new();
        std::io::stdin().read_line(&mut input)?;
        let answer = input.trim().to_lowercase();
        answer.is_empty() || answer == "y" || answer == "yes"
    };
    
    if !provide_storage {
        println!("✓ Storage provider disabled");
        println!("  You can still upload and download files normally.");
        return Ok(());
    }
    
    // Ask for storage quota
    let quota_bytes = if let Some(gb) = quota_gb {
        gb * 1024 * 1024 * 1024
    } else {
        println!();
        println!("How much storage space do you want to provide?");
        println!("  1) 10 GB  (recommended for laptops)");
        println!("  2) 50 GB  (recommended for desktops)");
        println!("  3) 100 GB (recommended for servers)");
        println!("  4) Custom");
        print!("Choice [1-4]: ");
        
        let mut input = String::new();
        std::io::stdin().read_line(&mut input)?;
        let choice = input.trim();
        
        match choice {
            "1" | "" => 10 * 1024 * 1024 * 1024,
            "2" => 50 * 1024 * 1024 * 1024,
            "3" => 100 * 1024 * 1024 * 1024,
            "4" => {
                print!("Enter storage quota in GB: ");
                let mut gb_input = String::new();
                std::io::stdin().read_line(&mut gb_input)?;
                let gb: u64 = gb_input.trim().parse()
                    .unwrap_or(10);
                gb * 1024 * 1024 * 1024
            }
            _ => 10 * 1024 * 1024 * 1024,
        }
    };
    
    // Initialize quota manager
    let quota_dir = data_dir.join("quota");
    std::fs::create_dir_all(&quota_dir)?;
    let quota_manager = QuotaManager::open(&quota_dir)?;
    
    // Set the quota limit
    quota_manager.set_quota_limit(quota_bytes)?;
    
    println!();
    println!("✅ Storage provider configured!");
    println!("   Quota: {}", format_bytes(quota_bytes));
    println!("   Location: {}", data_dir.display());
    println!();
    println!("Next steps:");
    println!("  1. Start your node: firecloud node");
    println!("  2. Your node will announce storage availability to the network");
    println!("  3. Earn credits by storing chunks for other users!");
    
    Ok(())
}

/// Show storage statistics
pub async fn stats(data_dir: PathBuf) -> Result<()> {
    let quota_dir = data_dir.join("quota");
    std::fs::create_dir_all(&quota_dir)?;
    let quota_manager = QuotaManager::open(&quota_dir)?;
    let quota_stats = quota_manager.get_quota()?;
    
    let used = quota_stats.total_bytes;
    let quota = quota_stats.quota_limit;
    let available = if quota > used { quota - used } else { 0 };
    
    println!("💾 Storage Statistics");
    println!();
    println!("Quota:          {}", format_bytes(quota));
    println!("Used:           {} ({:.1}%)", 
        format_bytes(used),
        if quota > 0 {
            (used as f64 / quota as f64) * 100.0
        } else {
            0.0
        }
    );
    println!("Available:      {}", format_bytes(available));
    println!();
    
    if quota_stats.total_chunks > 0 {
        println!("Chunks stored:  {}", quota_stats.total_chunks);
    } else {
        println!("No files stored yet.");
        println!("Upload files with: firecloud upload <file>");
    }
    
    Ok(())
}

/// List available storage providers in the network
pub async fn list_providers(_data_dir: PathBuf) -> Result<()> {
    use firecloud_net::{FireCloudNode, NodeConfig, NodeEvent};
    use std::time::Duration;
    
    println!("🔍 Searching for storage providers in the network...");
    println!();
    
    // Create a temporary node to query the DHT
    let config = NodeConfig {
        port: 0, // Random port
        enable_mdns: true,
        bootstrap_peers: Vec::new(),
        bootstrap_relays: vec![],
    };
    
    let mut node = FireCloudNode::new(config).await?;
    println!("📡 Node started: {}", node.local_peer_id());
    
    // Discover peers first
    println!("🔍 Discovering peers...");
    let mut discovered_peers = Vec::new();
    let discovery_timeout = tokio::time::Instant::now() + Duration::from_secs(5);
    
    while tokio::time::Instant::now() < discovery_timeout {
        tokio::select! {
            _ = tokio::time::sleep(Duration::from_millis(100)) => {}
            event = node.poll_event() => {
                if let Some(event) = event {
                    match event {
                        NodeEvent::PeerDiscovered(peer_id) => {
                            if peer_id != node.local_peer_id() {
                                discovered_peers.push(peer_id);
                            }
                        }
                        NodeEvent::Listening(addr) => {
                            println!("👂 Listening on {}", addr);
                        }
                        _ => {}
                    }
                }
            }
        }
    }
    
    if discovered_peers.is_empty() {
        println!("❌ No peers found on the network.");
        println!();
        println!("To see storage providers:");
        println!("  1. Start a node on another device: firecloud node --port 4001");
        println!("  2. Configure it as a storage provider: firecloud storage init --quota 10GB");
        println!();
        println!("The provider will automatically announce itself via DHT.");
        return Ok(());
    }
    
    println!("✅ Found {} peer(s)", discovered_peers.len());
    println!();
    
    // Query DHT for storage providers
    println!("🔍 Querying DHT for storage providers...");
    let providers = node.find_storage_providers(10).await?;
    
    if providers.is_empty() {
        println!("❌ No storage providers found in the DHT.");
        println!();
        println!("To become a storage provider:");
        println!("  1. firecloud storage init --quota 10GB");
        println!("  2. firecloud node --port 4001");
        println!();
        println!("Your node will automatically announce storage availability.");
    } else {
        println!("✅ Found {} storage provider(s):", providers.len());
        println!();
        println!("{:<20} {:<15} {:<10}", 
            "Peer ID", "Available", "Reputation");
        println!("{}", "".repeat(50));
        
        for provider in &providers {
            let peer_id_short = format!("{}", provider.peer_id)
                .chars()
                .take(16)
                .collect::<String>();
            let available = crate::format::format_bytes(provider.available_space);
            let reputation = format!("{:.1}", provider.reputation);
            
            println!("{:<20} {:<15} {:<10}",
                peer_id_short, available, reputation);
            
            if !provider.listen_addrs.is_empty() {
                let addr_str = provider.listen_addrs.iter()
                    .map(|a| a.to_string())
                    .collect::<Vec<_>>()
                    .join(", ");
                println!("  Addresses: {}", addr_str);
            }
        }
        
        println!();
        println!("💡 Tip: Upload files to distribute chunks across these providers:");
        println!("   firecloud upload <file>");
    }
    
    Ok(())
}