use anyhow::{Context, Result};

use fyi_core::{EventQuery, Storage, StorageError};

/// Find session_id by PID - looks for most recent session with this PID
fn find_session_by_pid<S: Storage>(storage: &S, pid: i32) -> Result<Option<String>, StorageError> {
    // Get recent events and find one with matching PID
    let events = storage.query(&EventQuery::new().with_limit(1000))?;
    for event in events {
        if event.pid == Some(pid) {
            return Ok(Some(event.session_id));
        }
    }
    Ok(None)
}

pub fn run_session<S: Storage>(storage: &S, session_or_pid: String, json: bool) -> Result<()> {
    // Check if input is a PID (numeric) or session_id
    let session_id = if let Ok(pid) = session_or_pid.parse::<i32>() {
        // Look up session_id by PID
        find_session_by_pid(storage, pid)
            .context("failed to query session by PID")?
            .ok_or_else(|| anyhow::anyhow!("no session found for PID: {}", pid))?
    } else {
        session_or_pid
    };

    let stats = storage
        .api_stats(&session_id)
        .context("failed to get API stats")?;

    match stats {
        Some(stats) => {
            if json {
                print_json(&stats);
            } else {
                print_table(&stats);
            }
        }
        None => {
            if json {
                println!("null");
            } else {
                println!("No API request data found for session: {}", session_id);
                println!();
                println!("Make sure OpenTelemetry is configured and fyi otel is running.");
            }
        }
    }

    Ok(())
}

fn print_json(stats: &fyi_core::ApiStats) {
    #[derive(serde::Serialize)]
    struct JsonStats {
        session_id: String,
        request_count: i64,
        context_latest: i64,
        context_peak: i64,
        tokens_input: i64,
        tokens_output: i64,
        tokens_cache_read: i64,
        tokens_cache_creation: i64,
        tokens_total: i64,
        cost_usd: f64,
        first_request_ts: Option<i64>,
        last_request_ts: Option<i64>,
        duration_ms: Option<i64>,
    }

    let json_stats = JsonStats {
        session_id: stats.session_id.clone(),
        request_count: stats.request_count,
        context_latest: stats.context_latest,
        context_peak: stats.context_peak,
        tokens_input: stats.tokens_input,
        tokens_output: stats.tokens_output,
        tokens_cache_read: stats.tokens_cache_read,
        tokens_cache_creation: stats.tokens_cache_creation,
        tokens_total: stats.total_tokens(),
        cost_usd: stats.cost_usd,
        first_request_ts: stats.first_request_ts.map(|ts| ts.timestamp_millis()),
        last_request_ts: stats.last_request_ts.map(|ts| ts.timestamp_millis()),
        duration_ms: stats.duration().map(|d| d.num_milliseconds()),
    };

    if let Ok(json) = serde_json::to_string_pretty(&json_stats) {
        println!("{}", json);
    }
}

fn print_table(stats: &fyi_core::ApiStats) {
    let total_tokens = stats.total_tokens();

    println!("Session: {}", stats.session_id);
    println!("{}", "-".repeat(50));
    println!();

    println!("API Requests: {}", stats.request_count);
    println!();

    println!("Context Size:");
    println!(
        "  Current:  {:>12} tokens",
        format_number(stats.context_latest)
    );
    println!(
        "  Peak:     {:>12} tokens",
        format_number(stats.context_peak)
    );
    println!();

    println!("Token Usage (cumulative):");
    println!(
        "  Input:          {:>12} ({:>5.1}%)",
        format_number(stats.tokens_input),
        percentage(stats.tokens_input, total_tokens)
    );
    println!(
        "  Output:         {:>12} ({:>5.1}%)",
        format_number(stats.tokens_output),
        percentage(stats.tokens_output, total_tokens)
    );
    println!(
        "  Cache Read:     {:>12} ({:>5.1}%)",
        format_number(stats.tokens_cache_read),
        percentage(stats.tokens_cache_read, total_tokens)
    );
    println!(
        "  Cache Creation: {:>12} ({:>5.1}%)",
        format_number(stats.tokens_cache_creation),
        percentage(stats.tokens_cache_creation, total_tokens)
    );
    println!("  {}", "-".repeat(30));
    println!("  Total:          {:>12}", format_number(total_tokens));
    println!();

    println!("Cost: ${:.4}", stats.cost_usd);

    if let Some(duration) = stats.duration() {
        println!("Duration: {}", format_duration(duration.num_milliseconds()));
    }
}

fn format_number(n: i64) -> String {
    let s = n.to_string();
    let mut result = String::new();
    let chars: Vec<char> = s.chars().collect();

    for (i, c) in chars.iter().enumerate() {
        if i > 0 && (chars.len() - i).is_multiple_of(3) {
            result.push(',');
        }
        result.push(*c);
    }

    result
}

fn percentage(part: i64, total: i64) -> f64 {
    if total == 0 {
        0.0
    } else {
        (part as f64 / total as f64) * 100.0
    }
}

fn format_duration(ms: i64) -> String {
    let seconds = ms / 1000;
    let minutes = seconds / 60;
    let hours = minutes / 60;

    if hours > 0 {
        format!("{}h {}m {}s", hours, minutes % 60, seconds % 60)
    } else if minutes > 0 {
        format!("{}m {}s", minutes, seconds % 60)
    } else {
        format!("{}s", seconds)
    }
}
