1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6use super::{ExportData, ExportResult};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(rename_all = "snake_case")]
11pub enum ReportType {
12 Cost,
13 Usage,
14 Forecast,
15 Audit,
16 Budget,
17 Summary,
18}
19
20impl std::fmt::Display for ReportType {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 match self {
23 ReportType::Cost => write!(f, "cost"),
24 ReportType::Usage => write!(f, "usage"),
25 ReportType::Forecast => write!(f, "forecast"),
26 ReportType::Audit => write!(f, "audit"),
27 ReportType::Budget => write!(f, "budget"),
28 ReportType::Summary => write!(f, "summary"),
29 }
30 }
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct ReportRequest {
36 pub report_type: ReportType,
37 pub start_date: DateTime<Utc>,
38 pub end_date: DateTime<Utc>,
39 pub organization_id: Option<String>,
40 pub filters: ReportFilters,
41}
42
43#[derive(Debug, Clone, Default, Serialize, Deserialize)]
44pub struct ReportFilters {
45 pub provider: Option<String>,
46 pub model: Option<String>,
47 pub user_id: Option<String>,
48 pub resource_type: Option<String>,
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct ReportResponse {
54 pub id: String,
55 pub report_type: ReportType,
56 pub generated_at: DateTime<Utc>,
57 pub data: ExportData,
58 pub summary: ReportSummary,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct ReportSummary {
63 pub total_records: usize,
64 pub date_range: DateRange,
65 pub aggregates: std::collections::HashMap<String, serde_json::Value>,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct DateRange {
70 pub start: DateTime<Utc>,
71 pub end: DateTime<Utc>,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct CostReport {
77 pub provider: String,
78 pub model: String,
79 pub organization_id: String,
80 pub total_cost: f64,
81 pub currency: String,
82 pub period_start: DateTime<Utc>,
83 pub period_end: DateTime<Utc>,
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize)]
88pub struct UsageReport {
89 pub provider: String,
90 pub model: String,
91 pub organization_id: String,
92 pub total_tokens: u64,
93 pub total_requests: u64,
94 pub period_start: DateTime<Utc>,
95 pub period_end: DateTime<Utc>,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct ForecastReport {
101 pub forecast_date: DateTime<Utc>,
102 pub predicted_cost: f64,
103 pub confidence_interval: (f64, f64),
104 pub trend: String,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct AuditReport {
110 pub event_type: String,
111 pub user_id: String,
112 pub resource_type: String,
113 pub action: String,
114 pub timestamp: DateTime<Utc>,
115 pub status: String,
116}
117
118pub struct ReportGenerator;
120
121impl ReportGenerator {
122 pub fn new() -> Self {
123 Self
124 }
125
126 pub async fn generate(&self, request: ReportRequest) -> ExportResult<ReportResponse> {
127 match request.report_type {
128 ReportType::Cost => self.generate_cost_report(request).await,
129 ReportType::Usage => self.generate_usage_report(request).await,
130 ReportType::Forecast => self.generate_forecast_report(request).await,
131 ReportType::Audit => self.generate_audit_report(request).await,
132 ReportType::Budget => self.generate_budget_report(request).await,
133 ReportType::Summary => self.generate_summary_report(request).await,
134 }
135 }
136
137 async fn generate_cost_report(&self, request: ReportRequest) -> ExportResult<ReportResponse> {
138 let mut data = ExportData::new(vec![
139 "Date".to_string(),
140 "Provider".to_string(),
141 "Model".to_string(),
142 "Organization".to_string(),
143 "Cost (USD)".to_string(),
144 "Requests".to_string(),
145 ]);
146
147 data.add_metadata("report_type", "cost");
149 data.add_metadata("generated_at", Utc::now().to_rfc3339());
150
151 let summary = ReportSummary {
152 total_records: data.row_count(),
153 date_range: DateRange {
154 start: request.start_date,
155 end: request.end_date,
156 },
157 aggregates: std::collections::HashMap::new(),
158 };
159
160 Ok(ReportResponse {
161 id: uuid::Uuid::new_v4().to_string(),
162 report_type: ReportType::Cost,
163 generated_at: Utc::now(),
164 data,
165 summary,
166 })
167 }
168
169 async fn generate_usage_report(&self, request: ReportRequest) -> ExportResult<ReportResponse> {
170 let mut data = ExportData::new(vec![
171 "Date".to_string(),
172 "Provider".to_string(),
173 "Model".to_string(),
174 "Total Tokens".to_string(),
175 "Input Tokens".to_string(),
176 "Output Tokens".to_string(),
177 "Requests".to_string(),
178 ]);
179
180 data.add_metadata("report_type", "usage");
181 data.add_metadata("generated_at", Utc::now().to_rfc3339());
182
183 let summary = ReportSummary {
184 total_records: data.row_count(),
185 date_range: DateRange {
186 start: request.start_date,
187 end: request.end_date,
188 },
189 aggregates: std::collections::HashMap::new(),
190 };
191
192 Ok(ReportResponse {
193 id: uuid::Uuid::new_v4().to_string(),
194 report_type: ReportType::Usage,
195 generated_at: Utc::now(),
196 data,
197 summary,
198 })
199 }
200
201 async fn generate_forecast_report(
202 &self,
203 request: ReportRequest,
204 ) -> ExportResult<ReportResponse> {
205 let mut data = ExportData::new(vec![
206 "Forecast Date".to_string(),
207 "Predicted Cost".to_string(),
208 "Lower Bound".to_string(),
209 "Upper Bound".to_string(),
210 "Confidence".to_string(),
211 "Trend".to_string(),
212 ]);
213
214 data.add_metadata("report_type", "forecast");
215 data.add_metadata("generated_at", Utc::now().to_rfc3339());
216
217 let summary = ReportSummary {
218 total_records: data.row_count(),
219 date_range: DateRange {
220 start: request.start_date,
221 end: request.end_date,
222 },
223 aggregates: std::collections::HashMap::new(),
224 };
225
226 Ok(ReportResponse {
227 id: uuid::Uuid::new_v4().to_string(),
228 report_type: ReportType::Forecast,
229 generated_at: Utc::now(),
230 data,
231 summary,
232 })
233 }
234
235 async fn generate_audit_report(&self, request: ReportRequest) -> ExportResult<ReportResponse> {
236 let mut data = ExportData::new(vec![
237 "Timestamp".to_string(),
238 "Event Type".to_string(),
239 "User ID".to_string(),
240 "Resource Type".to_string(),
241 "Action".to_string(),
242 "Status".to_string(),
243 "IP Address".to_string(),
244 ]);
245
246 data.add_metadata("report_type", "audit");
247 data.add_metadata("generated_at", Utc::now().to_rfc3339());
248
249 let summary = ReportSummary {
250 total_records: data.row_count(),
251 date_range: DateRange {
252 start: request.start_date,
253 end: request.end_date,
254 },
255 aggregates: std::collections::HashMap::new(),
256 };
257
258 Ok(ReportResponse {
259 id: uuid::Uuid::new_v4().to_string(),
260 report_type: ReportType::Audit,
261 generated_at: Utc::now(),
262 data,
263 summary,
264 })
265 }
266
267 async fn generate_budget_report(&self, request: ReportRequest) -> ExportResult<ReportResponse> {
268 let mut data = ExportData::new(vec![
269 "Organization".to_string(),
270 "Budget Limit".to_string(),
271 "Current Spend".to_string(),
272 "Remaining".to_string(),
273 "Utilization %".to_string(),
274 "Status".to_string(),
275 ]);
276
277 data.add_metadata("report_type", "budget");
278 data.add_metadata("generated_at", Utc::now().to_rfc3339());
279
280 let summary = ReportSummary {
281 total_records: data.row_count(),
282 date_range: DateRange {
283 start: request.start_date,
284 end: request.end_date,
285 },
286 aggregates: std::collections::HashMap::new(),
287 };
288
289 Ok(ReportResponse {
290 id: uuid::Uuid::new_v4().to_string(),
291 report_type: ReportType::Budget,
292 generated_at: Utc::now(),
293 data,
294 summary,
295 })
296 }
297
298 async fn generate_summary_report(
299 &self,
300 request: ReportRequest,
301 ) -> ExportResult<ReportResponse> {
302 let mut data = ExportData::new(vec![
303 "Metric".to_string(),
304 "Value".to_string(),
305 "Change".to_string(),
306 "Trend".to_string(),
307 ]);
308
309 data.add_metadata("report_type", "summary");
310 data.add_metadata("generated_at", Utc::now().to_rfc3339());
311
312 let summary = ReportSummary {
313 total_records: data.row_count(),
314 date_range: DateRange {
315 start: request.start_date,
316 end: request.end_date,
317 },
318 aggregates: std::collections::HashMap::new(),
319 };
320
321 Ok(ReportResponse {
322 id: uuid::Uuid::new_v4().to_string(),
323 report_type: ReportType::Summary,
324 generated_at: Utc::now(),
325 data,
326 summary,
327 })
328 }
329}
330
331impl Default for ReportGenerator {
332 fn default() -> Self {
333 Self::new()
334 }
335}
336
337#[cfg(test)]
338mod tests {
339 use super::*;
340
341 #[tokio::test]
342 async fn test_generate_cost_report() {
343 let generator = ReportGenerator::new();
344 let request = ReportRequest {
345 report_type: ReportType::Cost,
346 start_date: Utc::now() - chrono::Duration::days(7),
347 end_date: Utc::now(),
348 organization_id: Some("test_org".to_string()),
349 filters: ReportFilters::default(),
350 };
351
352 let result = generator.generate(request).await;
353 assert!(result.is_ok());
354
355 let response = result.unwrap();
356 assert_eq!(response.report_type, ReportType::Cost);
357 }
358
359 #[tokio::test]
360 async fn test_generate_usage_report() {
361 let generator = ReportGenerator::new();
362 let request = ReportRequest {
363 report_type: ReportType::Usage,
364 start_date: Utc::now() - chrono::Duration::days(30),
365 end_date: Utc::now(),
366 organization_id: None,
367 filters: ReportFilters::default(),
368 };
369
370 let result = generator.generate(request).await;
371 assert!(result.is_ok());
372 }
373}