// SPDX-License-Identifier: LicenseRef-Commercial
// Analytics Dashboard — historical cost tracking, model usage, token trends.

use serde::Serialize;

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AnalyticsSummary {
    pub total_sessions: i64,
    pub total_cost_usd: f64,
    pub total_tokens: i64,
    pub total_turns: i64,
    pub total_tool_calls: i64,
    pub avg_cost_per_session: f64,
    pub avg_tokens_per_session: f64,
    pub period_days: i64,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DailyStats {
    pub date: String,
    pub session_count: i64,
    pub cost_usd: f64,
    pub tokens: i64,
    pub turns: i64,
    pub tool_calls: i64,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ModelBreakdown {
    pub model: String,
    pub session_count: i64,
    pub total_cost_usd: f64,
    pub total_tokens: i64,
    pub avg_cost_per_session: f64,
}

#[tauri::command]
pub fn pro_analytics_summary(project_id: String, days: Option<i64>) -> Result<AnalyticsSummary, String> {
    let conn = super::open_sessions_db()?;
    let period = days.unwrap_or(30);
    let cutoff = now_epoch() - (period * 86400);

    let mut stmt = conn.prepare(
        "SELECT COUNT(*) AS cnt, COALESCE(SUM(cost_usd), 0) AS total_cost,
                COALESCE(SUM(peak_tokens), 0) AS total_tokens,
                COALESCE(SUM(turn_count), 0) AS total_turns,
                COALESCE(SUM(tool_call_count), 0) AS total_tools
         FROM session_metrics WHERE project_id = ?1 AND end_time >= ?2"
    ).map_err(|e| format!("Query failed: {e}"))?;

    let result = stmt.query_row(rusqlite::params![project_id, cutoff], |row| {
        let count: i64 = row.get("cnt")?;
        let cost: f64 = row.get("total_cost")?;
        let tokens: i64 = row.get("total_tokens")?;
        let turns: i64 = row.get("total_turns")?;
        let tools: i64 = row.get("total_tools")?;
        Ok(AnalyticsSummary {
            total_sessions: count,
            total_cost_usd: cost,
            total_tokens: tokens,
            total_turns: turns,
            total_tool_calls: tools,
            avg_cost_per_session: if count > 0 { cost / count as f64 } else { 0.0 },
            avg_tokens_per_session: if count > 0 { tokens as f64 / count as f64 } else { 0.0 },
            period_days: period,
        })
    }).map_err(|e| format!("Query failed: {e}"))?;

    Ok(result)
}

#[tauri::command]
pub fn pro_analytics_daily(project_id: String, days: Option<i64>) -> Result<Vec<DailyStats>, String> {
    let conn = super::open_sessions_db()?;
    let period = days.unwrap_or(30);
    let cutoff = now_epoch() - (period * 86400);

    let mut stmt = conn.prepare(
        "SELECT date(end_time, 'unixepoch') AS day,
                COUNT(*) AS cnt, COALESCE(SUM(cost_usd), 0) AS total_cost,
                COALESCE(SUM(peak_tokens), 0) AS total_tokens,
                COALESCE(SUM(turn_count), 0) AS total_turns,
                COALESCE(SUM(tool_call_count), 0) AS total_tools
         FROM session_metrics
         WHERE project_id = ?1 AND end_time >= ?2
         GROUP BY day ORDER BY day ASC"
    ).map_err(|e| format!("Query failed: {e}"))?;

    let rows = stmt.query_map(rusqlite::params![project_id, cutoff], |row| {
        Ok(DailyStats {
            date: row.get("day")?,
            session_count: row.get("cnt")?,
            cost_usd: row.get("total_cost")?,
            tokens: row.get("total_tokens")?,
            turns: row.get("total_turns")?,
            tool_calls: row.get("total_tools")?,
        })
    }).map_err(|e| format!("Query failed: {e}"))?
    .collect::<Result<Vec<_>, _>>()
    .map_err(|e| format!("Row read failed: {e}"))?;

    Ok(rows)
}

#[tauri::command]
pub fn pro_analytics_model_breakdown(project_id: String, days: Option<i64>) -> Result<Vec<ModelBreakdown>, String> {
    let conn = super::open_sessions_db()?;
    let period = days.unwrap_or(30);
    let cutoff = now_epoch() - (period * 86400);

    let mut stmt = conn.prepare(
        "SELECT COALESCE(model, 'unknown') AS model_name, COUNT(*) AS cnt,
                COALESCE(SUM(cost_usd), 0) AS total_cost,
                COALESCE(SUM(peak_tokens), 0) AS total_tokens
         FROM session_metrics
         WHERE project_id = ?1 AND end_time >= ?2
         GROUP BY model ORDER BY SUM(cost_usd) DESC"
    ).map_err(|e| format!("Query failed: {e}"))?;

    let rows = stmt.query_map(rusqlite::params![project_id, cutoff], |row| {
        let count: i64 = row.get("cnt")?;
        let cost: f64 = row.get("total_cost")?;
        Ok(ModelBreakdown {
            model: row.get("model_name")?,
            session_count: count,
            total_cost_usd: cost,
            total_tokens: row.get("total_tokens")?,
            avg_cost_per_session: if count > 0 { cost / count as f64 } else { 0.0 },
        })
    }).map_err(|e| format!("Query failed: {e}"))?
    .collect::<Result<Vec<_>, _>>()
    .map_err(|e| format!("Row read failed: {e}"))?;

    Ok(rows)
}

pub(crate) fn now_epoch() -> i64 {
    std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .unwrap_or_default()
        .as_secs() as i64
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_analytics_summary_struct() {
        let s = AnalyticsSummary {
            total_sessions: 5,
            total_cost_usd: 1.25,
            total_tokens: 50000,
            total_turns: 30,
            total_tool_calls: 100,
            avg_cost_per_session: 0.25,
            avg_tokens_per_session: 10000.0,
            period_days: 30,
        };
        let json = serde_json::to_string(&s).unwrap();
        assert!(json.contains("totalSessions"));
        assert!(json.contains("avgCostPerSession"));
    }

    #[test]
    fn test_model_breakdown_serializes_camel_case() {
        let m = ModelBreakdown {
            model: "opus".into(),
            session_count: 3,
            total_cost_usd: 0.75,
            total_tokens: 30000,
            avg_cost_per_session: 0.25,
        };
        let json = serde_json::to_string(&m).unwrap();
        assert!(json.contains("sessionCount"));
        assert!(json.contains("totalCostUsd"));
    }
}
