use axum::{extract::State, Json}; use chrono::Utc; use std::collections::HashMap; use tracing::{info, warn}; use uuid::Uuid; use crate::{AppState, models::*}; pub async fn generate_report( State(state): State, Json(req): Json, ) -> Json { let cache_key = format!("{}:{:?}", req.tenant_id, req.report_type); if let Some(cached) = state.report_cache.get(&cache_key) { info!("Cache hit for report: {}", cache_key); return Json(cached.clone()); } info!("Generating report: {:?} for tenant: {}", req.report_type, req.tenant_id); let result = match req.report_type { ReportType::FinancialSummary => generate_financial_summary(&req).await, ReportType::SalesPipeline => generate_sales_pipeline(&req).await, ReportType::CustomerAnalytics => generate_customer_analytics(&req).await, ReportType::RevenueForecast => generate_revenue_forecast(&req).await, ReportType::ExpenseBreakdown => generate_expense_breakdown(&req).await, ReportType::CashflowProjection => generate_cashflow_projection(&req).await, ReportType::MrrAnalysis => generate_mrr_analysis(&req).await, ReportType::ChurnAnalysis => generate_churn_analysis(&req).await, ReportType::Custom(ref name) => generate_custom_report(&req, name).await, }; state.report_cache.insert(cache_key, result.clone()); Json(result) } pub async fn analytics_query( State(_state): State, Json(req): Json, ) -> Json { let start = std::time::Instant::now(); info!("Analytics query: {} for tenant: {}", req.query_type, req.tenant_id); let rows: Vec> = match req.query_type.as_str() { "revenue_by_month" => vec![ { let mut row = HashMap::new(); row.insert("month".to_string(), serde_json::json!("2026-07")); row.insert("revenue".to_string(), serde_json::json!(12450.0)); row.insert("expenses".to_string(), serde_json::json!(8230.0)); row }, { let mut row = HashMap::new(); row.insert("month".to_string(), serde_json::json!("2026-06")); row.insert("revenue".to_string(), serde_json::json!(11520.0)); row.insert("expenses".to_string(), serde_json::json!(8480.0)); row }, ], "sales_pipeline" => vec![ { let mut row = HashMap::new(); row.insert("stage".to_string(), serde_json::json!("qualified")); row.insert("count".to_string(), serde_json::json!(12)); row.insert("value".to_string(), serde_json::json!(145000.0)); row }, { let mut row = HashMap::new(); row.insert("stage".to_string(), serde_json::json!("proposal")); row.insert("count".to_string(), serde_json::json!(5)); row.insert("value".to_string(), serde_json::json!(89000.0)); row }, ], _ => vec![], }; let mut totals = HashMap::new(); totals.insert("total_revenue".to_string(), 23970.0); totals.insert("total_expenses".to_string(), 16710.0); Json(AnalyticsResult { query_id: Uuid::new_v4().to_string(), rows, totals, execution_time_ms: start.elapsed().as_millis() as u64, }) } pub async fn batch_analytics( State(state): State, Json(req): Json, ) -> Json> { info!("Batch analytics: {} queries for tenant: {}", req.queries.len(), req.tenant_id); let results: Vec = req.queries .into_iter() .map(|q| { let start = std::time::Instant::now(); AnalyticsResult { query_id: Uuid::new_v4().to_string(), rows: vec![], totals: HashMap::new(), execution_time_ms: start.elapsed().as_millis() as u64, } }) .collect(); Json(results) } pub async fn dashboard_summary( State(_state): State, ) -> Json { Json(DashboardSummary { tenant_id: "default".to_string(), generated_at: Utc::now(), finance: FinanceKPIs { revenue_mtd: 12450.0, expenses_mtd: 8230.0, cash_balance: 45230.0, runway_months: 14.5, outstanding_invoices: 12340.0, tax_due_days: Some(7), }, sales: SalesKPIs { mrr: 8450.0, arr: 101400.0, pipeline_value: 340000.0, win_rate: 0.34, avg_deal_size: 15500.0, sales_cycle_days: 45.0, }, marketing: MarketingKPIs { active_campaigns: 3, impressions_30d: 45230, ctr: 0.058, brand_mentions: 23, ai_spend: 1247.0, }, hr: HRKPIs { headcount: 12, open_positions: 3, avg_time_to_hire_days: 28.0, retention_rate: 0.92, }, }) } async fn generate_financial_summary(req: &ReportRequest) -> ReportResult { let mut key_metrics = HashMap::new(); key_metrics.insert("total_revenue".to_string(), 12450.0); key_metrics.insert("total_expenses".to_string(), 8230.0); key_metrics.insert("net_profit".to_string(), 4220.0); key_metrics.insert("profit_margin".to_string(), 0.34); ReportResult { report_type: "financial_summary".to_string(), tenant_id: req.tenant_id.clone(), generated_at: Utc::now(), data: serde_json::json!({ "revenue_by_category": { "subscriptions": 8450.0, "services": 2800.0, "other": 1200.0, }, "expenses_by_category": { "infrastructure": 3200.0, "personnel": 3800.0, "marketing": 1230.0, }, }), summary: ReportSummary { total_records: 156, period_days: 30, key_metrics, }, } } async fn generate_sales_pipeline(req: &ReportRequest) -> ReportResult { let mut key_metrics = HashMap::new(); key_metrics.insert("pipeline_value".to_string(), 340000.0); key_metrics.insert("win_rate".to_string(), 0.34); key_metrics.insert("avg_deal_size".to_string(), 15500.0); ReportResult { report_type: "sales_pipeline".to_string(), tenant_id: req.tenant_id.clone(), generated_at: Utc::now(), data: serde_json::json!({ "stages": [ {"name": "lead", "count": 45, "value": 0.0}, {"name": "qualified", "count": 12, "value": 145000.0}, {"name": "proposal", "count": 5, "value": 89000.0}, {"name": "negotiation", "count": 3, "value": 67000.0}, {"name": "closed_won", "count": 8, "value": 124000.0}, ], }), summary: ReportSummary { total_records: 73, period_days: 30, key_metrics, }, } } async fn generate_customer_analytics(_req: &ReportRequest) -> ReportResult { ReportResult { report_type: "customer_analytics".to_string(), tenant_id: _req.tenant_id.clone(), generated_at: Utc::now(), data: serde_json::json!({}), summary: ReportSummary { total_records: 0, period_days: 30, key_metrics: HashMap::new(), }, } } async fn generate_revenue_forecast(_req: &ReportRequest) -> ReportResult { ReportResult { report_type: "revenue_forecast".to_string(), tenant_id: _req.tenant_id.clone(), generated_at: Utc::now(), data: serde_json::json!({ "forecast_3m": 38500.0, "forecast_6m": 82000.0, "forecast_12m": 185000.0, }), summary: ReportSummary { total_records: 12, period_days: 365, key_metrics: HashMap::new(), }, } } async fn generate_expense_breakdown(_req: &ReportRequest) -> ReportResult { ReportResult { report_type: "expense_breakdown".to_string(), tenant_id: _req.tenant_id.clone(), generated_at: Utc::now(), data: serde_json::json!({}), summary: ReportSummary { total_records: 0, period_days: 30, key_metrics: HashMap::new(), }, } } async fn generate_cashflow_projection(_req: &ReportRequest) -> ReportResult { ReportResult { report_type: "cashflow_projection".to_string(), tenant_id: _req.tenant_id.clone(), generated_at: Utc::now(), data: serde_json::json!({ "projected_balance_3m": 52000.0, "projected_balance_6m": 61000.0, "burn_rate_monthly": 8230.0, }), summary: ReportSummary { total_records: 6, period_days: 180, key_metrics: HashMap::new(), }, } } async fn generate_mrr_analysis(_req: &ReportRequest) -> ReportResult { let mut key_metrics = HashMap::new(); key_metrics.insert("mrr".to_string(), 8450.0); key_metrics.insert("arr".to_string(), 101400.0); key_metrics.insert("mrr_growth_rate".to_string(), 0.08); ReportResult { report_type: "mrr_analysis".to_string(), tenant_id: _req.tenant_id.clone(), generated_at: Utc::now(), data: serde_json::json!({ "mrr_by_plan": { "starter": 1200.0, "professional": 4800.0, "enterprise": 2450.0, }, }), summary: ReportSummary { total_records: 48, period_days: 30, key_metrics, }, } } async fn generate_churn_analysis(_req: &ReportRequest) -> ReportResult { let mut key_metrics = HashMap::new(); key_metrics.insert("churn_rate".to_string(), 0.05); key_metrics.insert("retention_rate".to_string(), 0.95); ReportResult { report_type: "churn_analysis".to_string(), tenant_id: _req.tenant_id.clone(), generated_at: Utc::now(), data: serde_json::json!({ "churn_by_reason": { "price": 2, "features": 1, "competitor": 0, "other": 1, }, }), summary: ReportSummary { total_records: 48, period_days: 90, key_metrics, }, } } async fn generate_custom_report(req: &ReportRequest, name: &str) -> ReportResult { warn!("Custom report '{}' not fully implemented", name); ReportResult { report_type: format!("custom:{}", name), tenant_id: req.tenant_id.clone(), generated_at: Utc::now(), data: serde_json::json!({"message": "Custom report template"}), summary: ReportSummary { total_records: 0, period_days: 30, key_metrics: HashMap::new(), }, } }