revoke-cli 0.3.0

Command-line interface for managing Revoke microservices infrastructure
use anyhow::{Result, Context};
use colored::Colorize;
use tabled::{Table, Tabled, settings::Style};
use serde::{Serialize, Deserialize};
use crate::config::Config;
use crate::commands::GatewayCommands;
use crate::utils::{print_success, print_error, print_info};

#[derive(Tabled, Serialize, Deserialize)]
struct RouteRow {
    #[tabled(rename = "ID")]
    id: String,
    #[tabled(rename = "Path")]
    path: String,
    #[tabled(rename = "Service")]
    service: String,
    #[tabled(rename = "Methods")]
    methods: String,
    #[tabled(rename = "Status")]
    status: String,
}

#[derive(Tabled, Serialize, Deserialize)]
struct StatsRow {
    #[tabled(rename = "Service")]
    service: String,
    #[tabled(rename = "Requests")]
    requests: u64,
    #[tabled(rename = "Success Rate")]
    success_rate: String,
    #[tabled(rename = "Avg Latency")]
    avg_latency: String,
    #[tabled(rename = "P99 Latency")]
    p99_latency: String,
}

pub async fn handle_command(command: GatewayCommands, config: &Config) -> Result<()> {
    match command {
        GatewayCommands::Routes { service } => {
            list_routes(service, config).await?
        }
        GatewayCommands::AddRoute { path, service, methods } => {
            add_route(&path, &service, methods, config).await?
        }
        GatewayCommands::RemoveRoute { id } => {
            remove_route(&id, config).await?
        }
        GatewayCommands::Stats { service } => {
            show_stats(service, config).await?
        }
        GatewayCommands::Test { config: config_file } => {
            test_configuration(config_file, config).await?
        }
    }
    
    Ok(())
}

async fn list_routes(service_filter: Option<String>, config: &Config) -> Result<()> {
    // In a real implementation, this would query the gateway API
    let routes = vec![
        RouteRow {
            id: "route-1".to_string(),
            path: "/api/users/*".to_string(),
            service: "user-service".to_string(),
            methods: "GET, POST, PUT".to_string(),
            status: "active".green().to_string(),
        },
        RouteRow {
            id: "route-2".to_string(),
            path: "/api/orders/*".to_string(),
            service: "order-service".to_string(),
            methods: "ALL".to_string(),
            status: "active".green().to_string(),
        },
    ];
    
    let filtered_routes: Vec<_> = if let Some(filter) = service_filter {
        routes.into_iter()
            .filter(|r| r.service.contains(&filter))
            .collect()
    } else {
        routes
    };
    
    if filtered_routes.is_empty() {
        print_info("No routes found");
    } else {
        let table = Table::new(&filtered_routes)
            .with(Style::modern())
            .to_string();
        println!("{}", table);
    }
    
    Ok(())
}

async fn add_route(
    path: &str,
    service: &str,
    methods: Vec<String>,
    config: &Config,
) -> Result<()> {
    // In a real implementation, this would call the gateway API
    let methods_str = if methods.is_empty() {
        "ALL"
    } else {
        &methods.join(", ")
    };
    
    print_success(&format!(
        "Route added successfully:\n  Path: {}\n  Service: {}\n  Methods: {}",
        path, service, methods_str
    ));
    
    Ok(())
}

async fn remove_route(id: &str, config: &Config) -> Result<()> {
    // In a real implementation, this would call the gateway API
    print_success(&format!("Route '{}' removed successfully", id));
    Ok(())
}

async fn show_stats(service_filter: Option<String>, config: &Config) -> Result<()> {
    // In a real implementation, this would query gateway metrics
    let stats = vec![
        StatsRow {
            service: "user-service".to_string(),
            requests: 125432,
            success_rate: "99.8%".green().to_string(),
            avg_latency: "23ms".to_string(),
            p99_latency: "145ms".to_string(),
        },
        StatsRow {
            service: "order-service".to_string(),
            requests: 87654,
            success_rate: "99.2%".green().to_string(),
            avg_latency: "45ms".to_string(),
            p99_latency: "234ms".to_string(),
        },
    ];
    
    let filtered_stats: Vec<_> = if let Some(filter) = service_filter {
        stats.into_iter()
            .filter(|s| s.service.contains(&filter))
            .collect()
    } else {
        stats
    };
    
    if filtered_stats.is_empty() {
        print_info("No statistics found");
    } else {
        println!("{}", "Gateway Statistics".bold());
        println!("{}", "=".repeat(50));
        
        let table = Table::new(&filtered_stats)
            .with(Style::modern())
            .to_string();
        println!("{}", table);
        
        println!("\n{}: {}", 
            "Period".bold(), 
            "Last 24 hours".to_string()
        );
    }
    
    Ok(())
}

async fn test_configuration(
    config_file: Option<String>,
    config: &Config,
) -> Result<()> {
    print_info("Testing gateway configuration...");
    
    // In a real implementation, this would validate the configuration
    let config_path = config_file.unwrap_or_else(|| "gateway.yaml".to_string());
    
    println!("\n{}", "Configuration Test Results".bold());
    println!("{}", "=".repeat(50));
    
    // Simulate validation results
    let checks = vec![
        ("Syntax validation", true, ""),
        ("Service connectivity", true, ""),
        ("Health check endpoints", true, ""),
        ("Rate limit configuration", true, ""),
        ("Circuit breaker settings", true, ""),
    ];
    
    let mut all_passed = true;
    
    for (check, passed, error) in checks {
        if passed {
            println!("{} {}", "".green(), check);
        } else {
            println!("{} {} - {}", "".red(), check, error.red());
            all_passed = false;
        }
    }
    
    println!();
    
    if all_passed {
        print_success("All configuration checks passed!");
    } else {
        print_error("Some configuration checks failed");
        return Err(anyhow::anyhow!("Configuration validation failed"));
    }
    
    Ok(())
}