cstats-cli 0.1.1

Command line interface for cstats
//! Stats command implementation - fetches and displays Anthropic API usage statistics

use colored::*;
use cstats_core::api::{
    AnthropicUsageStats, ApiClient, CostBreakdown, RateLimitInfo, UsageSummary,
};
use cstats_core::{config::Config, Result};

use crate::{OutputFormat, StatsArgs, StatsPeriod};

/// Execute the stats command
pub async fn stats_command(args: StatsArgs, format: OutputFormat, config: Config) -> Result<()> {
    // Check if API key is available (from env or config)
    if !config.has_anthropic_api_key() {
        eprintln!("{}", "Error: No Anthropic API key configured".red());
        eprintln!("Please configure your API key by either:");
        eprintln!("  1. Running: cstats init");
        eprintln!("  2. Running: cstats config set-api-key");
        eprintln!("  3. Setting: export ANTHROPIC_API_KEY=\"sk-ant-...\"");
        return Ok(());
    }

    // Create API client with the loaded configuration
    let client = if args.no_cache {
        ApiClient::new(config.api)?
    } else {
        ApiClient::from_config_with_cache(config).await?
    };

    // Check if Anthropic client is configured
    if client.anthropic().is_none() {
        eprintln!("{}", "Error: Anthropic API client not configured".red());
        return Ok(());
    }

    // Fetch and display stats based on period
    match args.period {
        StatsPeriod::Daily => {
            let stats = client.fetch_daily_usage_stats().await?;
            display_usage_stats(&stats, "Daily Usage (Last 24 hours)", format, args.detailed)?;
        }
        StatsPeriod::Weekly => {
            let stats = client.fetch_weekly_usage_stats().await?;
            display_usage_stats(&stats, "Weekly Usage (Last 7 days)", format, args.detailed)?;
        }
        StatsPeriod::Monthly => {
            let stats = client.fetch_monthly_usage_stats().await?;
            display_usage_stats(
                &stats,
                "Monthly Usage (Last 30 days)",
                format,
                args.detailed,
            )?;
        }
        StatsPeriod::Summary => {
            let summary = client.get_usage_summary().await?;
            display_usage_summary(&summary, format)?;
        }
    }

    // Display rate limit information if requested
    if args.rate_limit {
        match client.fetch_rate_limit_info().await {
            Ok(rate_limit) => display_rate_limit(&rate_limit, format)?,
            Err(e) => eprintln!(
                "{} {}",
                "Warning: Could not fetch rate limit info:".yellow(),
                e
            ),
        }
    }

    // Display billing information if requested
    if args.billing {
        match client.fetch_current_month_billing().await {
            Ok(billing) => display_billing(&billing, format)?,
            Err(e) => eprintln!(
                "{} {}",
                "Warning: Could not fetch billing info:".yellow(),
                e
            ),
        }
    }

    Ok(())
}

/// Display usage statistics
fn display_usage_stats(
    stats: &AnthropicUsageStats,
    title: &str,
    format: OutputFormat,
    detailed: bool,
) -> Result<()> {
    match format {
        OutputFormat::Json => {
            println!("{}", serde_json::to_string_pretty(stats)?);
        }
        OutputFormat::Yaml => {
            // Fallback to JSON for now
            println!("{}", serde_json::to_string_pretty(stats)?);
        }
        OutputFormat::Text => {
            // Compact header
            println!("\n{} {}", "📊".bold(), title.bold().cyan());

            // Tokens and costs on one line
            println!(
                "Tokens: {} ({}{}↑) | Cost: ${:.4}",
                format_number(stats.token_usage.total_tokens).yellow(),
                format_number(stats.token_usage.input_tokens),
                format_number(stats.token_usage.output_tokens),
                stats.costs.total_cost_usd
            );

            // API calls summary on one line
            let success_rate = if stats.api_calls.total_calls > 0 {
                (stats.api_calls.successful_calls as f64 / stats.api_calls.total_calls as f64)
                    * 100.0
            } else {
                0.0
            };

            if stats.api_calls.total_calls > 0 {
                println!(
                    "Calls: {} ({} ok, {} fail) | {:.1}% success | {}ms avg",
                    stats.api_calls.total_calls,
                    stats.api_calls.successful_calls.to_string().green(),
                    stats.api_calls.failed_calls.to_string().red(),
                    success_rate,
                    stats.api_calls.avg_response_time_ms as u64
                );
            }

            // Model breakdown if requested (compact)
            if detailed && !stats.model_usage.is_empty() {
                println!("\nModels:");
                for model in &stats.model_usage {
                    println!(
                        "  {}{} requests, {} tokens, ${:.4}",
                        model.model.yellow(),
                        model.requests,
                        format_number(model.tokens.total_tokens),
                        model.cost.cost_usd
                    );
                }
            }
        }
    }
    Ok(())
}

/// Display usage summary
fn display_usage_summary(summary: &UsageSummary, format: OutputFormat) -> Result<()> {
    match format {
        OutputFormat::Json => {
            println!("{}", serde_json::to_string_pretty(summary)?);
        }
        OutputFormat::Yaml => {
            // Fallback to JSON for now
            println!("{}", serde_json::to_string_pretty(summary)?);
        }
        OutputFormat::Text => {
            println!("\n{} {}", "📊".bold(), "Usage Summary".bold().cyan());

            // Ultra-compact summary - all periods on separate lines
            println!(
                "Daily:   {} tokens | {} calls | ${:.4}",
                format_number(summary.daily.token_usage.total_tokens).yellow(),
                summary.daily.api_calls.total_calls,
                summary.daily.costs.total_cost_usd
            );
            println!(
                "Weekly:  {} tokens | {} calls | ${:.4}",
                format_number(summary.weekly.token_usage.total_tokens).yellow(),
                summary.weekly.api_calls.total_calls,
                summary.weekly.costs.total_cost_usd
            );
            println!(
                "Monthly: {} tokens | {} calls | ${:.4} (est: ${:.4}/mo)",
                format_number(summary.monthly.token_usage.total_tokens).yellow(),
                summary.monthly.api_calls.total_calls,
                summary.monthly.costs.total_cost_usd,
                summary.monthly.costs.estimated_monthly_cost_usd
            );
        }
    }
    Ok(())
}

/// Display rate limit information
fn display_rate_limit(rate_limit: &RateLimitInfo, format: OutputFormat) -> Result<()> {
    match format {
        OutputFormat::Json => {
            println!("{}", serde_json::to_string_pretty(rate_limit)?);
        }
        OutputFormat::Yaml => {
            // Fallback to JSON for now
            println!("{}", serde_json::to_string_pretty(rate_limit)?);
        }
        OutputFormat::Text => {
            print!("\n{} Rate Limits: ", "⏱️ ".bold());
            print!(
                "{}/{} req/min",
                rate_limit.requests_remaining, rate_limit.requests_per_minute
            );
            if let Some(tpm) = rate_limit.tokens_per_minute {
                print!(" | {}", format_number(tpm as u64));
                if let Some(remaining) = rate_limit.tokens_remaining {
                    print!("/{} tokens", format_number(remaining as u64));
                } else {
                    print!(" tokens/min");
                }
            }
            println!();
        }
    }
    Ok(())
}

/// Display billing information
fn display_billing(billing: &CostBreakdown, format: OutputFormat) -> Result<()> {
    match format {
        OutputFormat::Json => {
            println!("{}", serde_json::to_string_pretty(billing)?);
        }
        OutputFormat::Yaml => {
            // Fallback to JSON for now
            println!("{}", serde_json::to_string_pretty(billing)?);
        }
        OutputFormat::Text => {
            println!(
                "\n{} Billing: ${:.4} current | ${:.4} estimated/mo",
                "💳".bold(),
                billing.total_cost_usd,
                billing.estimated_monthly_cost_usd
            );

            if !billing.by_model.is_empty() {
                for (model, cost) in &billing.by_model {
                    println!(
                        "  {} → ${:.4} (in: ${:.4}, out: ${:.4})",
                        model.yellow(),
                        cost.cost_usd,
                        cost.input_cost_usd,
                        cost.output_cost_usd
                    );
                }
            }
        }
    }
    Ok(())
}

/// Format large numbers with commas
fn format_number(n: u64) -> String {
    let s = n.to_string();
    let mut result = String::new();
    for (i, c) in s.chars().rev().enumerate() {
        if i > 0 && i % 3 == 0 {
            result.push(',');
        }
        result.push(c);
    }
    result.chars().rev().collect()
}