mockforge_analytics/
pillar_usage.rs

1//! Pillar usage tracking
2//!
3//! Tracks usage of MockForge pillars (Reality, Contracts, DevX, Cloud, AI)
4//! to help users understand platform adoption and identify under-utilized features.
5
6use crate::database::AnalyticsDatabase;
7use crate::error::Result;
8use chrono::{DateTime, Utc};
9use serde::{Deserialize, Serialize};
10use serde_json::Value;
11use std::collections::HashMap;
12
13/// Pillar name
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash)]
15#[serde(rename_all = "lowercase")]
16pub enum Pillar {
17    /// Reality pillar - everything that makes mocks feel like a real, evolving backend
18    Reality,
19    /// Contracts pillar - schema, drift, validation, and safety nets
20    Contracts,
21    /// DevX pillar - SDKs, generators, playgrounds, ergonomics
22    DevX,
23    /// Cloud pillar - registry, orgs, governance, monetization, marketplace
24    Cloud,
25    /// AI pillar - LLM/voice flows, AI diff/assist, generative behaviors
26    Ai,
27}
28
29impl Pillar {
30    /// Convert to string
31    pub fn as_str(&self) -> &'static str {
32        match self {
33            Pillar::Reality => "reality",
34            Pillar::Contracts => "contracts",
35            Pillar::DevX => "devx",
36            Pillar::Cloud => "cloud",
37            Pillar::Ai => "ai",
38        }
39    }
40
41    /// Parse from string
42    pub fn from_str(s: &str) -> Option<Self> {
43        match s.to_lowercase().as_str() {
44            "reality" => Some(Pillar::Reality),
45            "contracts" => Some(Pillar::Contracts),
46            "devx" => Some(Pillar::DevX),
47            "cloud" => Some(Pillar::Cloud),
48            "ai" => Some(Pillar::Ai),
49            _ => None,
50        }
51    }
52}
53
54impl std::fmt::Display for Pillar {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        write!(f, "{}", self.as_str())
57    }
58}
59
60/// Pillar usage event
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct PillarUsageEvent {
63    /// Workspace ID (optional)
64    pub workspace_id: Option<String>,
65    /// Organization ID (optional)
66    pub org_id: Option<String>,
67    /// Pillar name
68    pub pillar: Pillar,
69    /// Metric name (e.g., "blended_reality_ratio", "smart_personas_usage", "validation_mode")
70    pub metric_name: String,
71    /// Metric value (JSON)
72    pub metric_value: Value,
73    /// Timestamp
74    pub timestamp: DateTime<Utc>,
75}
76
77/// Pillar usage metrics
78#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct PillarUsageMetrics {
80    /// Workspace ID
81    pub workspace_id: Option<String>,
82    /// Organization ID
83    pub org_id: Option<String>,
84    /// Time range
85    pub time_range: String,
86    /// Reality pillar metrics
87    pub reality: Option<RealityPillarMetrics>,
88    /// Contracts pillar metrics
89    pub contracts: Option<ContractsPillarMetrics>,
90    /// DevX pillar metrics
91    pub devx: Option<DevXPillarMetrics>,
92    /// Cloud pillar metrics
93    pub cloud: Option<CloudPillarMetrics>,
94    /// AI pillar metrics
95    pub ai: Option<AiPillarMetrics>,
96}
97
98/// Reality pillar metrics
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct RealityPillarMetrics {
101    /// Percentage of requests using blended reality (reality continuum)
102    pub blended_reality_percent: f64,
103    /// Percentage of scenarios using Smart Personas
104    pub smart_personas_percent: f64,
105    /// Percentage of scenarios using static fixtures
106    pub static_fixtures_percent: f64,
107    /// Average reality level (1-5)
108    pub avg_reality_level: f64,
109    /// Number of scenarios with chaos enabled
110    pub chaos_enabled_count: u64,
111    /// Total number of scenarios
112    pub total_scenarios: u64,
113}
114
115/// Contracts pillar metrics
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct ContractsPillarMetrics {
118    /// Percentage of requests with validation disabled
119    pub validation_disabled_percent: f64,
120    /// Percentage of requests with validation in warn mode
121    pub validation_warn_percent: f64,
122    /// Percentage of requests with validation in enforce mode
123    pub validation_enforce_percent: f64,
124    /// Number of endpoints with drift budgets configured
125    pub drift_budget_configured_count: u64,
126    /// Number of drift incidents
127    pub drift_incidents_count: u64,
128    /// Number of contract sync cycles
129    pub contract_sync_cycles: u64,
130}
131
132/// DevX pillar metrics
133#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct DevXPillarMetrics {
135    /// Number of SDK installations
136    pub sdk_installations: u64,
137    /// Number of client code generations
138    pub client_generations: u64,
139    /// Number of playground sessions
140    pub playground_sessions: u64,
141    /// Number of CLI commands executed
142    pub cli_commands: u64,
143}
144
145/// Cloud pillar metrics
146#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct CloudPillarMetrics {
148    /// Number of shared scenarios
149    pub shared_scenarios_count: u64,
150    /// Number of marketplace downloads
151    pub marketplace_downloads: u64,
152    /// Number of org templates used
153    pub org_templates_used: u64,
154    /// Number of collaborative workspaces
155    pub collaborative_workspaces: u64,
156}
157
158/// AI pillar metrics
159#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct AiPillarMetrics {
161    /// Number of AI-generated mocks
162    pub ai_generated_mocks: u64,
163    /// Number of AI contract diffs
164    pub ai_contract_diffs: u64,
165    /// Number of voice commands
166    pub voice_commands: u64,
167    /// Number of LLM-assisted operations
168    pub llm_assisted_operations: u64,
169}
170
171impl AnalyticsDatabase {
172    /// Record a pillar usage event
173    pub async fn record_pillar_usage(&self, event: &PillarUsageEvent) -> Result<()> {
174        let timestamp = event.timestamp.timestamp();
175        let metric_value_json = serde_json::to_string(&event.metric_value)?;
176
177        sqlx::query(
178            r#"
179            INSERT INTO pillar_usage_events (
180                workspace_id, org_id, pillar, metric_name, metric_value, timestamp
181            )
182            VALUES ($1, $2, $3, $4, $5, $6)
183            "#,
184        )
185        .bind(event.workspace_id.as_deref())
186        .bind(event.org_id.as_deref())
187        .bind(event.pillar.as_str())
188        .bind(&event.metric_name)
189        .bind(&metric_value_json)
190        .bind(timestamp)
191        .execute(self.pool())
192        .await?;
193
194        Ok(())
195    }
196
197    /// Get pillar usage metrics for a workspace
198    pub async fn get_workspace_pillar_metrics(
199        &self,
200        workspace_id: &str,
201        duration_seconds: i64,
202    ) -> Result<PillarUsageMetrics> {
203        let end_time = Utc::now().timestamp();
204        let start_time = end_time - duration_seconds;
205
206        // Get reality pillar metrics
207        let reality = self
208            .get_reality_pillar_metrics(Some(workspace_id), None, start_time, end_time)
209            .await?;
210
211        // Get contracts pillar metrics
212        let contracts = self
213            .get_contracts_pillar_metrics(Some(workspace_id), None, start_time, end_time)
214            .await?;
215
216        // Get DevX pillar metrics
217        let devx = self
218            .get_devx_pillar_metrics(Some(workspace_id), None, start_time, end_time)
219            .await?;
220
221        // Get Cloud pillar metrics
222        let cloud = self
223            .get_cloud_pillar_metrics(Some(workspace_id), None, start_time, end_time)
224            .await?;
225
226        // Get AI pillar metrics
227        let ai = self
228            .get_ai_pillar_metrics(Some(workspace_id), None, start_time, end_time)
229            .await?;
230
231        Ok(PillarUsageMetrics {
232            workspace_id: Some(workspace_id.to_string()),
233            org_id: None,
234            time_range: format!("{}s", duration_seconds),
235            reality: Some(reality),
236            contracts: Some(contracts),
237            devx: Some(devx),
238            cloud: Some(cloud),
239            ai: Some(ai),
240        })
241    }
242
243    /// Get pillar usage metrics for an organization
244    pub async fn get_org_pillar_metrics(
245        &self,
246        org_id: &str,
247        duration_seconds: i64,
248    ) -> Result<PillarUsageMetrics> {
249        let end_time = Utc::now().timestamp();
250        let start_time = end_time - duration_seconds;
251
252        // Get reality pillar metrics
253        let reality = self
254            .get_reality_pillar_metrics(None, Some(org_id), start_time, end_time)
255            .await?;
256
257        // Get contracts pillar metrics
258        let contracts = self
259            .get_contracts_pillar_metrics(None, Some(org_id), start_time, end_time)
260            .await?;
261
262        // Get DevX pillar metrics
263        let devx = self.get_devx_pillar_metrics(None, Some(org_id), start_time, end_time).await?;
264
265        // Get Cloud pillar metrics
266        let cloud = self.get_cloud_pillar_metrics(None, Some(org_id), start_time, end_time).await?;
267
268        // Get AI pillar metrics
269        let ai = self.get_ai_pillar_metrics(None, Some(org_id), start_time, end_time).await?;
270
271        Ok(PillarUsageMetrics {
272            workspace_id: None,
273            org_id: Some(org_id.to_string()),
274            time_range: format!("{}s", duration_seconds),
275            reality: Some(reality),
276            contracts: Some(contracts),
277            devx: Some(devx),
278            cloud: Some(cloud),
279            ai: Some(ai),
280        })
281    }
282
283    /// Get reality pillar metrics
284    async fn get_reality_pillar_metrics(
285        &self,
286        workspace_id: Option<&str>,
287        org_id: Option<&str>,
288        start_time: i64,
289        end_time: i64,
290    ) -> Result<RealityPillarMetrics> {
291        // Query blended reality usage
292        let blended_reality_query = if let Some(ws_id) = workspace_id {
293            sqlx::query_scalar::<_, f64>(
294                r#"
295                SELECT AVG(CAST(json_extract(metric_value, '$.ratio') AS REAL)) * 100.0
296                FROM pillar_usage_events
297                WHERE pillar = 'reality'
298                AND metric_name = 'blended_reality_ratio'
299                AND workspace_id = $1
300                AND timestamp >= $2 AND timestamp <= $3
301                "#,
302            )
303            .bind(ws_id)
304            .bind(start_time)
305            .bind(end_time)
306        } else if let Some(org) = org_id {
307            sqlx::query_scalar::<_, f64>(
308                r#"
309                SELECT AVG(CAST(json_extract(metric_value, '$.ratio') AS REAL)) * 100.0
310                FROM pillar_usage_events
311                WHERE pillar = 'reality'
312                AND metric_name = 'blended_reality_ratio'
313                AND org_id = $1
314                AND timestamp >= $2 AND timestamp <= $3
315                "#,
316            )
317            .bind(org)
318            .bind(start_time)
319            .bind(end_time)
320        } else {
321            return Err(crate::error::AnalyticsError::InvalidInput(
322                "Either workspace_id or org_id must be provided".to_string(),
323            ));
324        };
325
326        let blended_reality_percent =
327            blended_reality_query.fetch_one(self.pool()).await.unwrap_or(0.0);
328
329        // Query Smart Personas vs static fixtures
330        let smart_personas_query = if let Some(ws_id) = workspace_id {
331            sqlx::query_scalar::<_, i64>(
332                r#"
333                SELECT COUNT(*) FROM pillar_usage_events
334                WHERE pillar = 'reality'
335                AND metric_name = 'persona_usage'
336                AND json_extract(metric_value, '$.type') = 'smart'
337                AND workspace_id = $1
338                AND timestamp >= $2 AND timestamp <= $3
339                "#,
340            )
341            .bind(ws_id)
342            .bind(start_time)
343            .bind(end_time)
344        } else if let Some(org) = org_id {
345            sqlx::query_scalar::<_, i64>(
346                r#"
347                SELECT COUNT(*) FROM pillar_usage_events
348                WHERE pillar = 'reality'
349                AND metric_name = 'persona_usage'
350                AND json_extract(metric_value, '$.type') = 'smart'
351                AND org_id = $1
352                AND timestamp >= $2 AND timestamp <= $3
353                "#,
354            )
355            .bind(org)
356            .bind(start_time)
357            .bind(end_time)
358        } else {
359            return Err(crate::error::AnalyticsError::InvalidInput(
360                "Either workspace_id or org_id must be provided".to_string(),
361            ));
362        };
363
364        let smart_personas_count = smart_personas_query.fetch_one(self.pool()).await.unwrap_or(0);
365
366        let static_fixtures_query = if let Some(ws_id) = workspace_id {
367            sqlx::query_scalar::<_, i64>(
368                r#"
369                SELECT COUNT(*) FROM pillar_usage_events
370                WHERE pillar = 'reality'
371                AND metric_name = 'persona_usage'
372                AND json_extract(metric_value, '$.type') = 'static'
373                AND workspace_id = $1
374                AND timestamp >= $2 AND timestamp <= $3
375                "#,
376            )
377            .bind(ws_id)
378            .bind(start_time)
379            .bind(end_time)
380        } else if let Some(org) = org_id {
381            sqlx::query_scalar::<_, i64>(
382                r#"
383                SELECT COUNT(*) FROM pillar_usage_events
384                WHERE pillar = 'reality'
385                AND metric_name = 'persona_usage'
386                AND json_extract(metric_value, '$.type') = 'static'
387                AND org_id = $1
388                AND timestamp >= $2 AND timestamp <= $3
389                "#,
390            )
391            .bind(org)
392            .bind(start_time)
393            .bind(end_time)
394        } else {
395            return Err(crate::error::AnalyticsError::InvalidInput(
396                "Either workspace_id or org_id must be provided".to_string(),
397            ));
398        };
399
400        let static_fixtures_count = static_fixtures_query.fetch_one(self.pool()).await.unwrap_or(0);
401
402        let total = smart_personas_count + static_fixtures_count;
403        let smart_personas_percent = if total > 0 {
404            (smart_personas_count as f64 / total as f64) * 100.0
405        } else {
406            0.0
407        };
408        let static_fixtures_percent = if total > 0 {
409            (static_fixtures_count as f64 / total as f64) * 100.0
410        } else {
411            0.0
412        };
413
414        // Query average reality level
415        let avg_reality_level = if let Some(ws_id) = workspace_id {
416            sqlx::query_scalar::<_, f64>(
417                r#"
418                SELECT AVG(CAST(json_extract(metric_value, '$.level') AS REAL))
419                FROM pillar_usage_events
420                WHERE pillar = 'reality'
421                AND metric_name = 'reality_level'
422                AND workspace_id = $1
423                AND timestamp >= $2 AND timestamp <= $3
424                "#,
425            )
426            .bind(ws_id)
427            .bind(start_time)
428            .bind(end_time)
429            .fetch_one(self.pool())
430            .await
431            .unwrap_or(0.0)
432        } else if let Some(org) = org_id {
433            sqlx::query_scalar::<_, f64>(
434                r#"
435                SELECT AVG(CAST(json_extract(metric_value, '$.level') AS REAL))
436                FROM pillar_usage_events
437                WHERE pillar = 'reality'
438                AND metric_name = 'reality_level'
439                AND org_id = $1
440                AND timestamp >= $2 AND timestamp <= $3
441                "#,
442            )
443            .bind(org)
444            .bind(start_time)
445            .bind(end_time)
446            .fetch_one(self.pool())
447            .await
448            .unwrap_or(0.0)
449        } else {
450            0.0
451        };
452
453        // Query chaos enabled count
454        let chaos_enabled_count = if let Some(ws_id) = workspace_id {
455            sqlx::query_scalar::<_, i64>(
456                r#"
457                SELECT COUNT(DISTINCT json_extract(metadata, '$.scenario_id'))
458                FROM pillar_usage_events
459                WHERE pillar = 'reality'
460                AND metric_name = 'chaos_injection'
461                AND json_extract(metric_value, '$.enabled') = 1
462                AND workspace_id = $1
463                AND timestamp >= $2 AND timestamp <= $3
464                "#,
465            )
466            .bind(ws_id)
467            .bind(start_time)
468            .bind(end_time)
469            .fetch_one(self.pool())
470            .await
471            .unwrap_or(0)
472        } else if let Some(org) = org_id {
473            sqlx::query_scalar::<_, i64>(
474                r#"
475                SELECT COUNT(DISTINCT json_extract(metadata, '$.scenario_id'))
476                FROM pillar_usage_events
477                WHERE pillar = 'reality'
478                AND metric_name = 'chaos_injection'
479                AND json_extract(metric_value, '$.enabled') = 1
480                AND org_id = $1
481                AND timestamp >= $2 AND timestamp <= $3
482                "#,
483            )
484            .bind(org)
485            .bind(start_time)
486            .bind(end_time)
487            .fetch_one(self.pool())
488            .await
489            .unwrap_or(0)
490        } else {
491            0
492        };
493
494        Ok(RealityPillarMetrics {
495            blended_reality_percent,
496            smart_personas_percent,
497            static_fixtures_percent,
498            avg_reality_level,
499            chaos_enabled_count: chaos_enabled_count as u64,
500            total_scenarios: total as u64,
501        })
502    }
503
504    /// Get contracts pillar metrics
505    async fn get_contracts_pillar_metrics(
506        &self,
507        workspace_id: Option<&str>,
508        org_id: Option<&str>,
509        start_time: i64,
510        end_time: i64,
511    ) -> Result<ContractsPillarMetrics> {
512        // Query validation mode usage
513        let validation_disabled_query = if let Some(ws_id) = workspace_id {
514            sqlx::query_scalar::<_, i64>(
515                r#"
516                SELECT COUNT(*) FROM pillar_usage_events
517                WHERE pillar = 'contracts'
518                AND metric_name = 'validation_mode'
519                AND json_extract(metric_value, '$.mode') = 'disabled'
520                AND workspace_id = $1
521                AND timestamp >= $2 AND timestamp <= $3
522                "#,
523            )
524            .bind(ws_id)
525            .bind(start_time)
526            .bind(end_time)
527        } else if let Some(org) = org_id {
528            sqlx::query_scalar::<_, i64>(
529                r#"
530                SELECT COUNT(*) FROM pillar_usage_events
531                WHERE pillar = 'contracts'
532                AND metric_name = 'validation_mode'
533                AND json_extract(metric_value, '$.mode') = 'disabled'
534                AND org_id = $1
535                AND timestamp >= $2 AND timestamp <= $3
536                "#,
537            )
538            .bind(org)
539            .bind(start_time)
540            .bind(end_time)
541        } else {
542            return Err(crate::error::AnalyticsError::InvalidInput(
543                "Either workspace_id or org_id must be provided".to_string(),
544            ));
545        };
546
547        let validation_disabled_count =
548            validation_disabled_query.fetch_one(self.pool()).await.unwrap_or(0);
549
550        let validation_warn_query = if let Some(ws_id) = workspace_id {
551            sqlx::query_scalar::<_, i64>(
552                r#"
553                SELECT COUNT(*) FROM pillar_usage_events
554                WHERE pillar = 'contracts'
555                AND metric_name = 'validation_mode'
556                AND json_extract(metric_value, '$.mode') = 'warn'
557                AND workspace_id = $1
558                AND timestamp >= $2 AND timestamp <= $3
559                "#,
560            )
561            .bind(ws_id)
562            .bind(start_time)
563            .bind(end_time)
564        } else if let Some(org) = org_id {
565            sqlx::query_scalar::<_, i64>(
566                r#"
567                SELECT COUNT(*) FROM pillar_usage_events
568                WHERE pillar = 'contracts'
569                AND metric_name = 'validation_mode'
570                AND json_extract(metric_value, '$.mode') = 'warn'
571                AND org_id = $1
572                AND timestamp >= $2 AND timestamp <= $3
573                "#,
574            )
575            .bind(org)
576            .bind(start_time)
577            .bind(end_time)
578        } else {
579            return Err(crate::error::AnalyticsError::InvalidInput(
580                "Either workspace_id or org_id must be provided".to_string(),
581            ));
582        };
583
584        let validation_warn_count = validation_warn_query.fetch_one(self.pool()).await.unwrap_or(0);
585
586        let validation_enforce_query = if let Some(ws_id) = workspace_id {
587            sqlx::query_scalar::<_, i64>(
588                r#"
589                SELECT COUNT(*) FROM pillar_usage_events
590                WHERE pillar = 'contracts'
591                AND metric_name = 'validation_mode'
592                AND json_extract(metric_value, '$.mode') = 'enforce'
593                AND workspace_id = $1
594                AND timestamp >= $2 AND timestamp <= $3
595                "#,
596            )
597            .bind(ws_id)
598            .bind(start_time)
599            .bind(end_time)
600        } else if let Some(org) = org_id {
601            sqlx::query_scalar::<_, i64>(
602                r#"
603                SELECT COUNT(*) FROM pillar_usage_events
604                WHERE pillar = 'contracts'
605                AND metric_name = 'validation_mode'
606                AND json_extract(metric_value, '$.mode') = 'enforce'
607                AND org_id = $1
608                AND timestamp >= $2 AND timestamp <= $3
609                "#,
610            )
611            .bind(org)
612            .bind(start_time)
613            .bind(end_time)
614        } else {
615            return Err(crate::error::AnalyticsError::InvalidInput(
616                "Either workspace_id or org_id must be provided".to_string(),
617            ));
618        };
619
620        let validation_enforce_count =
621            validation_enforce_query.fetch_one(self.pool()).await.unwrap_or(0);
622
623        let total_validation_events =
624            validation_disabled_count + validation_warn_count + validation_enforce_count;
625        let validation_disabled_percent = if total_validation_events > 0 {
626            (validation_disabled_count as f64 / total_validation_events as f64) * 100.0
627        } else {
628            0.0
629        };
630        let validation_warn_percent = if total_validation_events > 0 {
631            (validation_warn_count as f64 / total_validation_events as f64) * 100.0
632        } else {
633            0.0
634        };
635        let validation_enforce_percent = if total_validation_events > 0 {
636            (validation_enforce_count as f64 / total_validation_events as f64) * 100.0
637        } else {
638            0.0
639        };
640
641        // Query drift budget configured count
642        let drift_budget_configured_count = if let Some(ws_id) = workspace_id {
643            sqlx::query_scalar::<_, i64>(
644                r#"
645                SELECT COUNT(DISTINCT json_extract(metadata, '$.endpoint'))
646                FROM pillar_usage_events
647                WHERE pillar = 'contracts'
648                AND metric_name = 'drift_budget_configured'
649                AND workspace_id = $1
650                AND timestamp >= $2 AND timestamp <= $3
651                "#,
652            )
653            .bind(ws_id)
654            .bind(start_time)
655            .bind(end_time)
656            .fetch_one(self.pool())
657            .await
658            .unwrap_or(0)
659        } else if let Some(org) = org_id {
660            sqlx::query_scalar::<_, i64>(
661                r#"
662                SELECT COUNT(DISTINCT json_extract(metadata, '$.endpoint'))
663                FROM pillar_usage_events
664                WHERE pillar = 'contracts'
665                AND metric_name = 'drift_budget_configured'
666                AND org_id = $1
667                AND timestamp >= $2 AND timestamp <= $3
668                "#,
669            )
670            .bind(org)
671            .bind(start_time)
672            .bind(end_time)
673            .fetch_one(self.pool())
674            .await
675            .unwrap_or(0)
676        } else {
677            0
678        };
679
680        // Query drift incidents count
681        let drift_incidents_count = if let Some(ws_id) = workspace_id {
682            sqlx::query_scalar::<_, i64>(
683                r#"
684                SELECT COUNT(*)
685                FROM pillar_usage_events
686                WHERE pillar = 'contracts'
687                AND metric_name = 'drift_detection'
688                AND json_extract(metric_value, '$.incident') = 1
689                AND workspace_id = $1
690                AND timestamp >= $2 AND timestamp <= $3
691                "#,
692            )
693            .bind(ws_id)
694            .bind(start_time)
695            .bind(end_time)
696            .fetch_one(self.pool())
697            .await
698            .unwrap_or(0)
699        } else if let Some(org) = org_id {
700            sqlx::query_scalar::<_, i64>(
701                r#"
702                SELECT COUNT(*)
703                FROM pillar_usage_events
704                WHERE pillar = 'contracts'
705                AND metric_name = 'drift_detection'
706                AND json_extract(metric_value, '$.incident') = 1
707                AND org_id = $1
708                AND timestamp >= $2 AND timestamp <= $3
709                "#,
710            )
711            .bind(org)
712            .bind(start_time)
713            .bind(end_time)
714            .fetch_one(self.pool())
715            .await
716            .unwrap_or(0)
717        } else {
718            0
719        };
720
721        // Query contract sync cycles
722        let contract_sync_cycles = if let Some(ws_id) = workspace_id {
723            sqlx::query_scalar::<_, i64>(
724                r#"
725                SELECT COUNT(DISTINCT json_extract(metadata, '$.sync_id'))
726                FROM pillar_usage_events
727                WHERE pillar = 'contracts'
728                AND metric_name = 'contract_sync'
729                AND workspace_id = $1
730                AND timestamp >= $2 AND timestamp <= $3
731                "#,
732            )
733            .bind(ws_id)
734            .bind(start_time)
735            .bind(end_time)
736            .fetch_one(self.pool())
737            .await
738            .unwrap_or(0)
739        } else if let Some(org) = org_id {
740            sqlx::query_scalar::<_, i64>(
741                r#"
742                SELECT COUNT(DISTINCT json_extract(metadata, '$.sync_id'))
743                FROM pillar_usage_events
744                WHERE pillar = 'contracts'
745                AND metric_name = 'contract_sync'
746                AND org_id = $1
747                AND timestamp >= $2 AND timestamp <= $3
748                "#,
749            )
750            .bind(org)
751            .bind(start_time)
752            .bind(end_time)
753            .fetch_one(self.pool())
754            .await
755            .unwrap_or(0)
756        } else {
757            0
758        };
759
760        Ok(ContractsPillarMetrics {
761            validation_disabled_percent,
762            validation_warn_percent,
763            validation_enforce_percent,
764            drift_budget_configured_count: drift_budget_configured_count as u64,
765            drift_incidents_count: drift_incidents_count as u64,
766            contract_sync_cycles: contract_sync_cycles as u64,
767        })
768    }
769
770    /// Get DevX pillar metrics
771    async fn get_devx_pillar_metrics(
772        &self,
773        workspace_id: Option<&str>,
774        org_id: Option<&str>,
775        start_time: i64,
776        end_time: i64,
777    ) -> Result<DevXPillarMetrics> {
778        // Query SDK installations
779        let sdk_installations = if let Some(ws_id) = workspace_id {
780            sqlx::query_scalar::<_, i64>(
781                r#"
782                SELECT COUNT(DISTINCT json_extract(metadata, '$.sdk_type'))
783                FROM pillar_usage_events
784                WHERE pillar = 'devx'
785                AND metric_name = 'sdk_installation'
786                AND workspace_id = $1
787                AND timestamp >= $2 AND timestamp <= $3
788                "#,
789            )
790            .bind(ws_id)
791            .bind(start_time)
792            .bind(end_time)
793            .fetch_one(self.pool())
794            .await
795            .unwrap_or(0)
796        } else if let Some(org) = org_id {
797            sqlx::query_scalar::<_, i64>(
798                r#"
799                SELECT COUNT(DISTINCT json_extract(metadata, '$.sdk_type'))
800                FROM pillar_usage_events
801                WHERE pillar = 'devx'
802                AND metric_name = 'sdk_installation'
803                AND org_id = $1
804                AND timestamp >= $2 AND timestamp <= $3
805                "#,
806            )
807            .bind(org)
808            .bind(start_time)
809            .bind(end_time)
810            .fetch_one(self.pool())
811            .await
812            .unwrap_or(0)
813        } else {
814            0
815        };
816
817        // Query client generations
818        let client_generations = if let Some(ws_id) = workspace_id {
819            sqlx::query_scalar::<_, i64>(
820                r#"
821                SELECT COUNT(*)
822                FROM pillar_usage_events
823                WHERE pillar = 'devx'
824                AND metric_name = 'client_generation'
825                AND workspace_id = $1
826                AND timestamp >= $2 AND timestamp <= $3
827                "#,
828            )
829            .bind(ws_id)
830            .bind(start_time)
831            .bind(end_time)
832            .fetch_one(self.pool())
833            .await
834            .unwrap_or(0)
835        } else if let Some(org) = org_id {
836            sqlx::query_scalar::<_, i64>(
837                r#"
838                SELECT COUNT(*)
839                FROM pillar_usage_events
840                WHERE pillar = 'devx'
841                AND metric_name = 'client_generation'
842                AND org_id = $1
843                AND timestamp >= $2 AND timestamp <= $3
844                "#,
845            )
846            .bind(org)
847            .bind(start_time)
848            .bind(end_time)
849            .fetch_one(self.pool())
850            .await
851            .unwrap_or(0)
852        } else {
853            0
854        };
855
856        // Query playground sessions
857        let playground_sessions = if let Some(ws_id) = workspace_id {
858            sqlx::query_scalar::<_, i64>(
859                r#"
860                SELECT COUNT(DISTINCT json_extract(metadata, '$.session_id'))
861                FROM pillar_usage_events
862                WHERE pillar = 'devx'
863                AND metric_name = 'playground_session'
864                AND workspace_id = $1
865                AND timestamp >= $2 AND timestamp <= $3
866                "#,
867            )
868            .bind(ws_id)
869            .bind(start_time)
870            .bind(end_time)
871            .fetch_one(self.pool())
872            .await
873            .unwrap_or(0)
874        } else if let Some(org) = org_id {
875            sqlx::query_scalar::<_, i64>(
876                r#"
877                SELECT COUNT(DISTINCT json_extract(metadata, '$.session_id'))
878                FROM pillar_usage_events
879                WHERE pillar = 'devx'
880                AND metric_name = 'playground_session'
881                AND org_id = $1
882                AND timestamp >= $2 AND timestamp <= $3
883                "#,
884            )
885            .bind(org)
886            .bind(start_time)
887            .bind(end_time)
888            .fetch_one(self.pool())
889            .await
890            .unwrap_or(0)
891        } else {
892            0
893        };
894
895        // Query CLI commands
896        let cli_commands = if let Some(ws_id) = workspace_id {
897            sqlx::query_scalar::<_, i64>(
898                r#"
899                SELECT COUNT(*)
900                FROM pillar_usage_events
901                WHERE pillar = 'devx'
902                AND metric_name = 'cli_command'
903                AND workspace_id = $1
904                AND timestamp >= $2 AND timestamp <= $3
905                "#,
906            )
907            .bind(ws_id)
908            .bind(start_time)
909            .bind(end_time)
910            .fetch_one(self.pool())
911            .await
912            .unwrap_or(0)
913        } else if let Some(org) = org_id {
914            sqlx::query_scalar::<_, i64>(
915                r#"
916                SELECT COUNT(*)
917                FROM pillar_usage_events
918                WHERE pillar = 'devx'
919                AND metric_name = 'cli_command'
920                AND org_id = $1
921                AND timestamp >= $2 AND timestamp <= $3
922                "#,
923            )
924            .bind(org)
925            .bind(start_time)
926            .bind(end_time)
927            .fetch_one(self.pool())
928            .await
929            .unwrap_or(0)
930        } else {
931            0
932        };
933
934        Ok(DevXPillarMetrics {
935            sdk_installations: sdk_installations as u64,
936            client_generations: client_generations as u64,
937            playground_sessions: playground_sessions as u64,
938            cli_commands: cli_commands as u64,
939        })
940    }
941
942    /// Get Cloud pillar metrics
943    async fn get_cloud_pillar_metrics(
944        &self,
945        workspace_id: Option<&str>,
946        org_id: Option<&str>,
947        start_time: i64,
948        end_time: i64,
949    ) -> Result<CloudPillarMetrics> {
950        // Query shared scenarios count
951        let shared_scenarios_count = if let Some(ws_id) = workspace_id {
952            sqlx::query_scalar::<_, i64>(
953                r#"
954                SELECT COUNT(DISTINCT json_extract(metadata, '$.scenario_id'))
955                FROM pillar_usage_events
956                WHERE pillar = 'cloud'
957                AND metric_name = 'scenario_shared'
958                AND workspace_id = $1
959                AND timestamp >= $2 AND timestamp <= $3
960                "#,
961            )
962            .bind(ws_id)
963            .bind(start_time)
964            .bind(end_time)
965            .fetch_one(self.pool())
966            .await
967            .unwrap_or(0)
968        } else if let Some(org) = org_id {
969            sqlx::query_scalar::<_, i64>(
970                r#"
971                SELECT COUNT(DISTINCT json_extract(metadata, '$.scenario_id'))
972                FROM pillar_usage_events
973                WHERE pillar = 'cloud'
974                AND metric_name = 'scenario_shared'
975                AND org_id = $1
976                AND timestamp >= $2 AND timestamp <= $3
977                "#,
978            )
979            .bind(org)
980            .bind(start_time)
981            .bind(end_time)
982            .fetch_one(self.pool())
983            .await
984            .unwrap_or(0)
985        } else {
986            0
987        };
988
989        // Query marketplace downloads
990        let marketplace_downloads = if let Some(ws_id) = workspace_id {
991            sqlx::query_scalar::<_, i64>(
992                r#"
993                SELECT COUNT(*)
994                FROM pillar_usage_events
995                WHERE pillar = 'cloud'
996                AND metric_name = 'marketplace_download'
997                AND workspace_id = $1
998                AND timestamp >= $2 AND timestamp <= $3
999                "#,
1000            )
1001            .bind(ws_id)
1002            .bind(start_time)
1003            .bind(end_time)
1004            .fetch_one(self.pool())
1005            .await
1006            .unwrap_or(0)
1007        } else if let Some(org) = org_id {
1008            sqlx::query_scalar::<_, i64>(
1009                r#"
1010                SELECT COUNT(*)
1011                FROM pillar_usage_events
1012                WHERE pillar = 'cloud'
1013                AND metric_name = 'marketplace_download'
1014                AND org_id = $1
1015                AND timestamp >= $2 AND timestamp <= $3
1016                "#,
1017            )
1018            .bind(org)
1019            .bind(start_time)
1020            .bind(end_time)
1021            .fetch_one(self.pool())
1022            .await
1023            .unwrap_or(0)
1024        } else {
1025            0
1026        };
1027
1028        // Query org templates used
1029        let org_templates_used = if let Some(ws_id) = workspace_id {
1030            sqlx::query_scalar::<_, i64>(
1031                r#"
1032                SELECT COUNT(DISTINCT json_extract(metadata, '$.template_id'))
1033                FROM pillar_usage_events
1034                WHERE pillar = 'cloud'
1035                AND metric_name = 'template_use'
1036                AND workspace_id = $1
1037                AND timestamp >= $2 AND timestamp <= $3
1038                "#,
1039            )
1040            .bind(ws_id)
1041            .bind(start_time)
1042            .bind(end_time)
1043            .fetch_one(self.pool())
1044            .await
1045            .unwrap_or(0)
1046        } else if let Some(org) = org_id {
1047            sqlx::query_scalar::<_, i64>(
1048                r#"
1049                SELECT COUNT(DISTINCT json_extract(metadata, '$.template_id'))
1050                FROM pillar_usage_events
1051                WHERE pillar = 'cloud'
1052                AND metric_name = 'template_use'
1053                AND org_id = $1
1054                AND timestamp >= $2 AND timestamp <= $3
1055                "#,
1056            )
1057            .bind(org)
1058            .bind(start_time)
1059            .bind(end_time)
1060            .fetch_one(self.pool())
1061            .await
1062            .unwrap_or(0)
1063        } else {
1064            0
1065        };
1066
1067        // Query collaborative workspaces
1068        let collaborative_workspaces = if let Some(ws_id) = workspace_id {
1069            sqlx::query_scalar::<_, i64>(
1070                r#"
1071                SELECT COUNT(DISTINCT json_extract(metadata, '$.workspace_id'))
1072                FROM pillar_usage_events
1073                WHERE pillar = 'cloud'
1074                AND metric_name = 'workspace_creation'
1075                AND json_extract(metric_value, '$.collaborative') = 1
1076                AND workspace_id = $1
1077                AND timestamp >= $2 AND timestamp <= $3
1078                "#,
1079            )
1080            .bind(ws_id)
1081            .bind(start_time)
1082            .bind(end_time)
1083            .fetch_one(self.pool())
1084            .await
1085            .unwrap_or(0)
1086        } else if let Some(org) = org_id {
1087            sqlx::query_scalar::<_, i64>(
1088                r#"
1089                SELECT COUNT(DISTINCT json_extract(metadata, '$.workspace_id'))
1090                FROM pillar_usage_events
1091                WHERE pillar = 'cloud'
1092                AND metric_name = 'workspace_creation'
1093                AND json_extract(metric_value, '$.collaborative') = 1
1094                AND org_id = $1
1095                AND timestamp >= $2 AND timestamp <= $3
1096                "#,
1097            )
1098            .bind(org)
1099            .bind(start_time)
1100            .bind(end_time)
1101            .fetch_one(self.pool())
1102            .await
1103            .unwrap_or(0)
1104        } else {
1105            0
1106        };
1107
1108        Ok(CloudPillarMetrics {
1109            shared_scenarios_count: shared_scenarios_count as u64,
1110            marketplace_downloads: marketplace_downloads as u64,
1111            org_templates_used: org_templates_used as u64,
1112            collaborative_workspaces: collaborative_workspaces as u64,
1113        })
1114    }
1115
1116    /// Get AI pillar metrics
1117    async fn get_ai_pillar_metrics(
1118        &self,
1119        workspace_id: Option<&str>,
1120        org_id: Option<&str>,
1121        start_time: i64,
1122        end_time: i64,
1123    ) -> Result<AiPillarMetrics> {
1124        // Query AI-generated mocks
1125        let ai_generated_mocks = if let Some(ws_id) = workspace_id {
1126            sqlx::query_scalar::<_, i64>(
1127                r#"
1128                SELECT COUNT(*)
1129                FROM pillar_usage_events
1130                WHERE pillar = 'ai'
1131                AND metric_name = 'ai_generation'
1132                AND json_extract(metric_value, '$.type') = 'mock'
1133                AND workspace_id = $1
1134                AND timestamp >= $2 AND timestamp <= $3
1135                "#,
1136            )
1137            .bind(ws_id)
1138            .bind(start_time)
1139            .bind(end_time)
1140            .fetch_one(self.pool())
1141            .await
1142            .unwrap_or(0)
1143        } else if let Some(org) = org_id {
1144            sqlx::query_scalar::<_, i64>(
1145                r#"
1146                SELECT COUNT(*)
1147                FROM pillar_usage_events
1148                WHERE pillar = 'ai'
1149                AND metric_name = 'ai_generation'
1150                AND json_extract(metric_value, '$.type') = 'mock'
1151                AND org_id = $1
1152                AND timestamp >= $2 AND timestamp <= $3
1153                "#,
1154            )
1155            .bind(org)
1156            .bind(start_time)
1157            .bind(end_time)
1158            .fetch_one(self.pool())
1159            .await
1160            .unwrap_or(0)
1161        } else {
1162            0
1163        };
1164
1165        // Query AI contract diffs
1166        let ai_contract_diffs = if let Some(ws_id) = workspace_id {
1167            sqlx::query_scalar::<_, i64>(
1168                r#"
1169                SELECT COUNT(*)
1170                FROM pillar_usage_events
1171                WHERE pillar = 'ai'
1172                AND metric_name = 'ai_generation'
1173                AND json_extract(metric_value, '$.type') = 'contract_diff'
1174                AND workspace_id = $1
1175                AND timestamp >= $2 AND timestamp <= $3
1176                "#,
1177            )
1178            .bind(ws_id)
1179            .bind(start_time)
1180            .bind(end_time)
1181            .fetch_one(self.pool())
1182            .await
1183            .unwrap_or(0)
1184        } else if let Some(org) = org_id {
1185            sqlx::query_scalar::<_, i64>(
1186                r#"
1187                SELECT COUNT(*)
1188                FROM pillar_usage_events
1189                WHERE pillar = 'ai'
1190                AND metric_name = 'ai_generation'
1191                AND json_extract(metric_value, '$.type') = 'contract_diff'
1192                AND org_id = $1
1193                AND timestamp >= $2 AND timestamp <= $3
1194                "#,
1195            )
1196            .bind(org)
1197            .bind(start_time)
1198            .bind(end_time)
1199            .fetch_one(self.pool())
1200            .await
1201            .unwrap_or(0)
1202        } else {
1203            0
1204        };
1205
1206        // Query AI refinements
1207        let ai_refinements = if let Some(ws_id) = workspace_id {
1208            sqlx::query_scalar::<_, i64>(
1209                r#"
1210                SELECT COUNT(*)
1211                FROM pillar_usage_events
1212                WHERE pillar = 'ai'
1213                AND metric_name = 'ai_refinement'
1214                AND workspace_id = $1
1215                AND timestamp >= $2 AND timestamp <= $3
1216                "#,
1217            )
1218            .bind(ws_id)
1219            .bind(start_time)
1220            .bind(end_time)
1221            .fetch_one(self.pool())
1222            .await
1223            .unwrap_or(0)
1224        } else if let Some(org) = org_id {
1225            sqlx::query_scalar::<_, i64>(
1226                r#"
1227                SELECT COUNT(*)
1228                FROM pillar_usage_events
1229                WHERE pillar = 'ai'
1230                AND metric_name = 'ai_refinement'
1231                AND org_id = $1
1232                AND timestamp >= $2 AND timestamp <= $3
1233                "#,
1234            )
1235            .bind(org)
1236            .bind(start_time)
1237            .bind(end_time)
1238            .fetch_one(self.pool())
1239            .await
1240            .unwrap_or(0)
1241        } else {
1242            0
1243        };
1244
1245        // Query voice commands
1246        let voice_commands = if let Some(ws_id) = workspace_id {
1247            sqlx::query_scalar::<_, i64>(
1248                r#"
1249                SELECT COUNT(*)
1250                FROM pillar_usage_events
1251                WHERE pillar = 'ai'
1252                AND metric_name = 'voice_command'
1253                AND workspace_id = $1
1254                AND timestamp >= $2 AND timestamp <= $3
1255                "#,
1256            )
1257            .bind(ws_id)
1258            .bind(start_time)
1259            .bind(end_time)
1260            .fetch_one(self.pool())
1261            .await
1262            .unwrap_or(0)
1263        } else if let Some(org) = org_id {
1264            sqlx::query_scalar::<_, i64>(
1265                r#"
1266                SELECT COUNT(*)
1267                FROM pillar_usage_events
1268                WHERE pillar = 'ai'
1269                AND metric_name = 'voice_command'
1270                AND org_id = $1
1271                AND timestamp >= $2 AND timestamp <= $3
1272                "#,
1273            )
1274            .bind(org)
1275            .bind(start_time)
1276            .bind(end_time)
1277            .fetch_one(self.pool())
1278            .await
1279            .unwrap_or(0)
1280        } else {
1281            0
1282        };
1283
1284        // LLM-assisted operations includes all AI generations, diffs, and refinements
1285        let llm_assisted_operations = ai_generated_mocks + ai_contract_diffs + ai_refinements;
1286
1287        Ok(AiPillarMetrics {
1288            ai_generated_mocks: ai_generated_mocks as u64,
1289            ai_contract_diffs: ai_contract_diffs as u64,
1290            voice_commands: voice_commands as u64,
1291            llm_assisted_operations: llm_assisted_operations as u64,
1292        })
1293    }
1294}