kaccy_reputation/
integration.rs

1//! Integration module for coordinating all reputation system features
2//!
3//! This module provides high-level orchestration of reputation system
4//! features including SBT minting, AI scoring, disputes, portability,
5//! analytics, audit trails, batch operations, leaderboards, and maintenance.
6
7use sqlx::PgPool;
8use uuid::Uuid;
9
10use crate::ai_scoring::{AIScoringService, EvaluateQualityRequest};
11use crate::dispute::{DisputeService, VoteDisputeRequest};
12use crate::error::ReputationError;
13use crate::portability::{ImportReputationRequest, PortabilityService};
14use crate::sbt::{MintSBTRequest, SBTService};
15use crate::score::ReputationEngine;
16use crate::tier::ReputationTier;
17
18// Phase 6 services
19use crate::analytics::AnalyticsService;
20use crate::audit::{AuditSearch, AuditService};
21use crate::batch::BatchService;
22use crate::leaderboard::LeaderboardService;
23use crate::maintenance::MaintenanceService;
24
25/// Orchestrates all reputation system services (Phases 0-6)
26pub struct ReputationOrchestrator {
27    pub engine: ReputationEngine,
28    pub sbt_service: SBTService,
29    pub ai_service: AIScoringService,
30    pub dispute_service: DisputeService,
31    pub portability_service: PortabilityService,
32    // Phase 6 services
33    pub analytics_service: AnalyticsService,
34    pub audit_service: AuditService,
35    pub batch_service: BatchService,
36    pub leaderboard_service: LeaderboardService,
37    pub maintenance_service: MaintenanceService,
38}
39
40impl ReputationOrchestrator {
41    pub fn new(pool: PgPool) -> Self {
42        Self {
43            engine: ReputationEngine::new(pool.clone()),
44            sbt_service: SBTService::new(pool.clone()),
45            ai_service: AIScoringService::new(pool.clone()),
46            dispute_service: DisputeService::new(pool.clone()),
47            portability_service: PortabilityService::new(pool.clone()),
48            // Phase 6 services
49            analytics_service: AnalyticsService::new(pool.clone()),
50            audit_service: AuditService::new(pool.clone()),
51            batch_service: BatchService::new(pool.clone()),
52            leaderboard_service: LeaderboardService::new(pool.clone()),
53            maintenance_service: MaintenanceService::new(pool),
54        }
55    }
56
57    /// Complete workflow: Issue first token -> mint SBT
58    pub async fn on_first_token_issuance(&self, user_id: Uuid) -> Result<(), ReputationError> {
59        // Check if user already has an SBT
60        if self.sbt_service.has_sbt(user_id).await? {
61            return Ok(());
62        }
63
64        // Get current reputation
65        let score = self.engine.get_score(user_id).await?;
66
67        // Create SBT
68        let initial_score = score
69            .overall_score
70            .to_string()
71            .parse::<i32>()
72            .map_err(|e| {
73                ReputationError::Validation(format!("Failed to parse score as i32: {}", e))
74            })?;
75
76        let sbt_request = MintSBTRequest {
77            user_id,
78            initial_score,
79            initial_tier: score.tier,
80        };
81
82        self.sbt_service.create_sbt(sbt_request).await?;
83
84        Ok(())
85    }
86
87    /// Complete workflow: Commitment submitted -> AI evaluation -> update score
88    pub async fn evaluate_and_score_commitment(
89        &self,
90        commitment_id: Uuid,
91        user_id: Uuid,
92        evidence_url: String,
93        evidence_description: String,
94        commitment_description: String,
95    ) -> Result<i32, ReputationError> {
96        // AI evaluation
97        let eval_request = EvaluateQualityRequest {
98            commitment_id,
99            evidence_url,
100            evidence_description,
101            commitment_description,
102        };
103
104        let evaluation = self.ai_service.evaluate_quality(eval_request).await?;
105
106        // Update SBT metadata if needed
107        if let Some(sbt) = self.sbt_service.get_sbt(user_id).await? {
108            if sbt.minted {
109                let current_score: i32 = evaluation.quality_score;
110                if self
111                    .sbt_service
112                    .needs_update(user_id, current_score, 10)
113                    .await?
114                {
115                    let score = self.engine.get_score(user_id).await?;
116                    let new_score =
117                        score
118                            .overall_score
119                            .to_string()
120                            .parse::<i32>()
121                            .map_err(|e| {
122                                ReputationError::Validation(format!(
123                                    "Failed to parse score as i32: {}",
124                                    e
125                                ))
126                            })?;
127
128                    self.sbt_service
129                        .update_metadata(crate::sbt::UpdateSBTRequest {
130                            user_id,
131                            new_score,
132                            new_tier: score.tier,
133                        })
134                        .await?;
135                }
136            }
137        }
138
139        Ok(evaluation.quality_score)
140    }
141
142    /// Complete workflow: Dispute vote -> resolve -> update reputation
143    pub async fn process_dispute_vote(
144        &self,
145        vote_request: VoteDisputeRequest,
146    ) -> Result<(), ReputationError> {
147        // Submit vote
148        self.dispute_service.vote(vote_request.clone()).await?;
149
150        // Check if voting period ended
151        let dispute = self
152            .dispute_service
153            .get_dispute(vote_request.dispute_id)
154            .await?;
155        if dispute.voting_deadline < chrono::Utc::now() {
156            // Resolve dispute
157            let results = self
158                .dispute_service
159                .resolve_dispute(vote_request.dispute_id)
160                .await?;
161
162            // If upheld, adjust issuer's reputation
163            if results.outcome == crate::dispute::DisputeOutcome::Upheld {
164                // Negative adjustment for failed commitment
165                // This would typically be handled by the commitment module
166            }
167        }
168
169        Ok(())
170    }
171
172    /// Complete workflow: Import external reputation -> boost score -> update SBT
173    pub async fn import_and_apply_external_reputation(
174        &self,
175        import_request: ImportReputationRequest,
176    ) -> Result<i32, ReputationError> {
177        // Import reputation
178        let import_result = self
179            .portability_service
180            .import_reputation(import_request.clone())
181            .await?;
182
183        // After verification, this score can be used to boost user's reputation
184        // For now, just return the imported score
185        Ok(import_result.imported_score)
186    }
187
188    /// Check for fraud across all user activity
189    pub async fn comprehensive_fraud_check(&self, user_id: Uuid) -> Result<bool, ReputationError> {
190        let fraud_result = self
191            .ai_service
192            .detect_fraud(crate::ai_scoring::DetectFraudRequest {
193                user_id,
194                activity_window_days: 30,
195            })
196            .await?;
197
198        Ok(fraud_result.is_some())
199    }
200
201    /// Get complete user reputation profile including all Phase 2 features
202    pub async fn get_complete_profile(
203        &self,
204        user_id: Uuid,
205    ) -> Result<CompleteReputationProfile, ReputationError> {
206        let score = self.engine.get_score(user_id).await?;
207        let sbt = self.sbt_service.get_sbt(user_id).await?;
208        let external_reps = self
209            .portability_service
210            .get_verified_imports(user_id)
211            .await?;
212        let fraud_detections = self.ai_service.get_fraud_detections(user_id).await?;
213
214        Ok(CompleteReputationProfile {
215            score: score.overall_score,
216            tier: score.tier,
217            has_sbt: sbt.is_some(),
218            sbt_minted: sbt.map(|s| s.minted).unwrap_or(false),
219            verified_external_sources: external_reps.len(),
220            fraud_flags: fraud_detections.len(),
221            clean_record: fraud_detections.is_empty(),
222        })
223    }
224
225    // ========================================================================
226    // Phase 6 Workflow Methods
227    // ========================================================================
228
229    /// Comprehensive system health check with audit trail
230    pub async fn run_system_health_check(&self) -> Result<SystemHealthReport, ReputationError> {
231        // Run maintenance health checks
232        let health_report = self.maintenance_service.run_health_checks().await?;
233
234        // Get system statistics
235        let system_stats = self.analytics_service.get_system_stats().await?;
236
237        // Identify any low-score users that need attention
238        let low_score_users = self
239            .batch_service
240            .get_low_score_users(rust_decimal::Decimal::new(200, 0))
241            .await?;
242
243        let total_anomalies = health_report.score_anomalies
244            + health_report.stuck_commitments
245            + health_report.orphaned_records;
246
247        Ok(SystemHealthReport {
248            database_healthy: health_report.database_healthy,
249            total_users: system_stats.total_users as i64,
250            active_users: system_stats.active_users_30d as i64,
251            anomalies_count: total_anomalies as usize,
252            low_score_users_count: low_score_users.len(),
253            health_report,
254        })
255    }
256
257    /// Perform daily batch maintenance operations
258    pub async fn run_daily_maintenance(&self) -> Result<DailyMaintenanceReport, ReputationError> {
259        // Apply time decay to inactive users
260        let decay_result = self.batch_service.apply_time_decay_batch().await?;
261
262        // Expire overdue commitments
263        let expire_result = self.batch_service.expire_overdue_commitments().await?;
264
265        // Recalculate all scores for consistency
266        let recalc_result = self.batch_service.recalculate_scores(vec![]).await?;
267
268        Ok(DailyMaintenanceReport {
269            users_decayed: decay_result.users_decayed as i64,
270            commitments_expired: expire_result.expired_count as i64,
271            scores_recalculated: recalc_result.successful as i64,
272            timestamp: chrono::Utc::now(),
273        })
274    }
275
276    /// Get comprehensive analytics dashboard data
277    pub async fn get_analytics_dashboard(&self) -> Result<AnalyticsDashboard, ReputationError> {
278        let system_stats = self.analytics_service.get_system_stats().await?;
279        let growth_metrics = self.analytics_service.get_growth_metrics(30).await?;
280        let top_performers = self.analytics_service.get_top_performers(10).await?;
281        let score_distribution = self.analytics_service.get_score_distribution().await?;
282        let global_leaderboard = self
283            .leaderboard_service
284            .get_global_leaderboard(20, 0)
285            .await?;
286
287        Ok(AnalyticsDashboard {
288            system_stats,
289            growth_metrics,
290            top_performers,
291            score_distribution,
292            global_leaderboard,
293        })
294    }
295
296    /// Process user achievement awards and update leaderboard
297    pub async fn process_user_achievements(
298        &self,
299        user_id: Uuid,
300    ) -> Result<Vec<crate::leaderboard::AwardedAchievement>, ReputationError> {
301        // Get user's current achievements
302        let achievements = self
303            .leaderboard_service
304            .get_user_achievements(user_id)
305            .await?;
306
307        // Get user's rank for potential rank-based achievements
308        let _rank_info = self.leaderboard_service.get_user_rank(user_id).await?;
309
310        Ok(achievements)
311    }
312
313    /// Get user audit trail with analytics context
314    pub async fn get_user_activity_report(
315        &self,
316        user_id: Uuid,
317        limit: i32,
318    ) -> Result<UserActivityReport, ReputationError> {
319        // Get audit trail (last N records)
320        let audit_records = self
321            .audit_service
322            .get_user_audit_trail(user_id, limit, 0)
323            .await?;
324
325        // Get commitment analytics
326        let commitment_analytics = self
327            .analytics_service
328            .get_user_commitment_analytics(user_id)
329            .await?;
330
331        // Get user rank
332        let rank_info = self.leaderboard_service.get_user_rank(user_id).await?;
333
334        // Get achievements
335        let achievements = self
336            .leaderboard_service
337            .get_user_achievements(user_id)
338            .await?;
339
340        Ok(UserActivityReport {
341            user_id,
342            audit_events_count: audit_records.len(),
343            commitment_success_rate: commitment_analytics.success_rate,
344            total_commitments: commitment_analytics.total_commitments,
345            rank: rank_info.rank,
346            achievements_count: achievements.len(),
347            activity_period_days: limit,
348        })
349    }
350
351    /// Perform bulk user verification with batch processing
352    pub async fn bulk_verify_commitments(
353        &self,
354        admin_id: Uuid,
355        commitment_ids: Vec<Uuid>,
356    ) -> Result<BulkVerificationReport, ReputationError> {
357        let verified_count = 0;
358        let failed_count = 0;
359
360        // This would integrate with the commitment module for actual verification
361        // For now, we'll just track the counts
362        // In production, this would iterate through commitment_ids and verify each one
363
364        Ok(BulkVerificationReport {
365            total_processed: commitment_ids.len(),
366            verified_count,
367            failed_count,
368            admin_id,
369        })
370    }
371
372    /// Search audit trail with advanced filters
373    pub async fn search_audit_trail(
374        &self,
375        search: AuditSearch,
376    ) -> Result<Vec<crate::audit::AuditRecord>, ReputationError> {
377        self.audit_service.search_audit_trail(search).await
378    }
379
380    /// Get system-wide audit summary
381    pub async fn get_audit_summary(
382        &self,
383        start_date: chrono::DateTime<chrono::Utc>,
384        end_date: chrono::DateTime<chrono::Utc>,
385    ) -> Result<crate::audit::AuditSummary, ReputationError> {
386        self.audit_service
387            .get_audit_summary(start_date, end_date)
388            .await
389    }
390
391    // ========================================================================
392    // Phase 8 Advanced Production Workflows
393    // ========================================================================
394
395    /// Complete user onboarding workflow
396    /// Sets up a new user with initial reputation, SBT, and baseline metrics
397    pub async fn onboard_new_user(
398        &self,
399        user_id: Uuid,
400    ) -> Result<UserOnboardingReport, ReputationError> {
401        // Check current reputation score
402        let initial_score = self.engine.get_score(user_id).await?;
403
404        // Mint SBT if first token issuance
405        let sbt_created = if !self.sbt_service.has_sbt(user_id).await? {
406            self.on_first_token_issuance(user_id).await?;
407            true
408        } else {
409            false
410        };
411
412        // Get initial rank
413        let rank_info = self.leaderboard_service.get_user_rank(user_id).await?;
414
415        // Get commitment analytics baseline
416        let commitment_analytics = self
417            .analytics_service
418            .get_user_commitment_analytics(user_id)
419            .await?;
420
421        Ok(UserOnboardingReport {
422            user_id,
423            initial_score: initial_score.overall_score,
424            initial_tier: initial_score.tier,
425            sbt_created,
426            initial_rank: rank_info.rank,
427            total_users: rank_info.total_users,
428            baseline_commitments: commitment_analytics.total_commitments,
429            onboarded_at: chrono::Utc::now(),
430        })
431    }
432
433    /// Crisis management workflow
434    /// Handles system-wide emergencies and returns action plan
435    pub async fn handle_crisis(
436        &self,
437        crisis_type: CrisisType,
438    ) -> Result<CrisisResponse, ReputationError> {
439        let mut actions_taken = Vec::new();
440        let mut affected_users = 0;
441
442        match crisis_type {
443            CrisisType::MassScoreAnomaly => {
444                // Run health checks
445                let health_report = self.maintenance_service.run_health_checks().await?;
446
447                if health_report.score_anomalies > 0 {
448                    // Run weekly maintenance which includes score recalculation
449                    let maintenance_report =
450                        self.maintenance_service.run_weekly_maintenance().await?;
451                    actions_taken.push(format!(
452                        "Recalculated {} scores to fix anomalies",
453                        maintenance_report.scores_recalculated
454                    ));
455                    affected_users += maintenance_report.scores_recalculated;
456                }
457            }
458            CrisisType::DatabaseCorruption => {
459                // Run comprehensive maintenance
460                let maintenance_report = self.maintenance_service.run_weekly_maintenance().await?;
461                actions_taken.push(format!(
462                    "Recalculated {} scores",
463                    maintenance_report.scores_recalculated
464                ));
465                affected_users += maintenance_report.scores_recalculated;
466
467                // Additional score recalculation if needed
468                let recalc_result = self.batch_service.recalculate_scores(vec![]).await?;
469                actions_taken.push(format!(
470                    "Verified {} score calculations",
471                    recalc_result.successful
472                ));
473            }
474            CrisisType::MassFraudDetection => {
475                // Get low score users who might be fraudulent
476                let low_score_users = self
477                    .batch_service
478                    .get_low_score_users(rust_decimal::Decimal::new(100, 0))
479                    .await?;
480
481                affected_users = low_score_users.len() as i32;
482                actions_taken.push(format!(
483                    "Identified {} potentially fraudulent users",
484                    affected_users
485                ));
486            }
487            CrisisType::SystemOverload => {
488                // Apply time decay to reduce load
489                let decay_result = self.batch_service.apply_time_decay_batch().await?;
490                actions_taken.push(format!(
491                    "Applied time decay to {} users",
492                    decay_result.users_decayed
493                ));
494                affected_users += decay_result.users_decayed;
495            }
496        }
497
498        Ok(CrisisResponse {
499            crisis_type,
500            actions_taken,
501            affected_users,
502            severity: if affected_users > 100 {
503                CrisisSeverity::Critical
504            } else if affected_users > 10 {
505                CrisisSeverity::High
506            } else {
507                CrisisSeverity::Medium
508            },
509            handled_at: chrono::Utc::now(),
510        })
511    }
512
513    /// Performance monitoring workflow
514    /// Collects comprehensive performance metrics
515    pub async fn collect_performance_metrics(&self) -> Result<PerformanceMetrics, ReputationError> {
516        let start_time = chrono::Utc::now();
517
518        // Get system stats
519        let system_stats = self.analytics_service.get_system_stats().await?;
520
521        // Get health report
522        let health_report = self.maintenance_service.run_health_checks().await?;
523
524        // Calculate processing metrics
525        let total_commitments = system_stats.total_commitments;
526        let completed_commitments = system_stats.completed_commitments;
527        let processing_efficiency = if total_commitments > 0 {
528            (completed_commitments as f64 / total_commitments as f64) * 100.0
529        } else {
530            0.0
531        };
532
533        let collection_duration = chrono::Utc::now().signed_duration_since(start_time);
534
535        Ok(PerformanceMetrics {
536            total_users: system_stats.total_users,
537            active_users: system_stats.active_users_30d,
538            database_healthy: health_report.database_healthy,
539            score_anomalies: health_report.score_anomalies,
540            stuck_commitments: health_report.stuck_commitments,
541            orphaned_records: health_report.orphaned_records,
542            processing_efficiency,
543            average_score: system_stats.average_score,
544            collection_duration_ms: collection_duration.num_milliseconds(),
545            collected_at: chrono::Utc::now(),
546        })
547    }
548
549    /// Automated intervention workflow
550    /// Automatically fixes common issues without manual intervention
551    pub async fn run_automated_interventions(&self) -> Result<InterventionReport, ReputationError> {
552        let mut interventions = Vec::new();
553        let mut users_affected = 0;
554
555        // 1. Expire overdue commitments
556        let expire_result = self.batch_service.expire_overdue_commitments().await?;
557        if expire_result.expired_count > 0 {
558            interventions.push(InterventionAction {
559                action_type: InterventionType::ExpireCommitments,
560                description: format!(
561                    "Expired {} overdue commitments",
562                    expire_result.expired_count
563                ),
564                users_affected: expire_result.affected_users.len() as i32,
565                success: true,
566            });
567            users_affected += expire_result.affected_users.len() as i32;
568        }
569
570        // 2. Fix score anomalies via recalculation
571        let health_report = self.maintenance_service.run_health_checks().await?;
572        if health_report.score_anomalies > 0 {
573            // Recalculate scores to fix anomalies
574            let recalc_result = self.batch_service.recalculate_scores(vec![]).await?;
575            interventions.push(InterventionAction {
576                action_type: InterventionType::FixScoreAnomalies,
577                description: format!(
578                    "Recalculated {} scores to fix anomalies",
579                    recalc_result.successful
580                ),
581                users_affected: recalc_result.successful,
582                success: true,
583            });
584            users_affected += recalc_result.successful;
585        }
586
587        // 3. Apply time decay for inactive users
588        let decay_result = self.batch_service.apply_time_decay_batch().await?;
589        if decay_result.users_decayed > 0 {
590            interventions.push(InterventionAction {
591                action_type: InterventionType::ApplyTimeDecay,
592                description: format!(
593                    "Applied time decay to {} inactive users",
594                    decay_result.users_decayed
595                ),
596                users_affected: decay_result.users_decayed,
597                success: true,
598            });
599        }
600
601        let interventions_count = interventions.len();
602
603        Ok(InterventionReport {
604            interventions,
605            total_users_affected: users_affected,
606            interventions_count,
607            executed_at: chrono::Utc::now(),
608        })
609    }
610
611    /// Comprehensive user profile with all available data
612    /// Aggregates reputation, achievements, analytics, and external sources
613    pub async fn get_comprehensive_user_profile(
614        &self,
615        user_id: Uuid,
616    ) -> Result<ComprehensiveUserProfile, ReputationError> {
617        // Get base reputation profile
618        let base_profile = self.get_complete_profile(user_id).await?;
619
620        // Get user rank
621        let rank_info = self.leaderboard_service.get_user_rank(user_id).await?;
622
623        // Get achievements
624        let achievements = self
625            .leaderboard_service
626            .get_user_achievements(user_id)
627            .await?;
628
629        // Get commitment analytics
630        let commitment_analytics = self
631            .analytics_service
632            .get_user_commitment_analytics(user_id)
633            .await?;
634
635        // Get recent audit trail (last 50 events)
636        let recent_activity = self
637            .audit_service
638            .get_user_audit_trail(user_id, 50, 0)
639            .await?;
640
641        Ok(ComprehensiveUserProfile {
642            user_id,
643            score: base_profile.score,
644            tier: base_profile.tier,
645            rank: rank_info.rank,
646            percentile: rank_info.percentile,
647            total_users: rank_info.total_users,
648            has_sbt: base_profile.has_sbt,
649            sbt_minted: base_profile.sbt_minted,
650            verified_external_sources: base_profile.verified_external_sources,
651            fraud_flags: base_profile.fraud_flags,
652            clean_record: base_profile.clean_record,
653            total_commitments: commitment_analytics.total_commitments,
654            verified_commitments: commitment_analytics.verified,
655            failed_commitments: commitment_analytics.failed,
656            pending_commitments: commitment_analytics.pending,
657            success_rate: commitment_analytics.success_rate,
658            achievements_count: achievements.len(),
659            recent_activity_count: recent_activity.len(),
660        })
661    }
662}
663
664/// Complete reputation profile for a user
665#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
666pub struct CompleteReputationProfile {
667    pub score: rust_decimal::Decimal,
668    pub tier: ReputationTier,
669    pub has_sbt: bool,
670    pub sbt_minted: bool,
671    pub verified_external_sources: usize,
672    pub fraud_flags: usize,
673    pub clean_record: bool,
674}
675
676/// System health check report combining maintenance and analytics
677#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
678pub struct SystemHealthReport {
679    pub database_healthy: bool,
680    pub total_users: i64,
681    pub active_users: i64,
682    pub anomalies_count: usize,
683    pub low_score_users_count: usize,
684    pub health_report: crate::maintenance::HealthCheckReport,
685}
686
687/// Daily maintenance operations report
688#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
689pub struct DailyMaintenanceReport {
690    pub users_decayed: i64,
691    pub commitments_expired: i64,
692    pub scores_recalculated: i64,
693    pub timestamp: chrono::DateTime<chrono::Utc>,
694}
695
696/// Comprehensive analytics dashboard data
697#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
698pub struct AnalyticsDashboard {
699    pub system_stats: crate::analytics::SystemStats,
700    pub growth_metrics: crate::analytics::GrowthMetrics,
701    pub top_performers: Vec<crate::analytics::UserRanking>,
702    pub score_distribution: crate::analytics::ScoreDistribution,
703    pub global_leaderboard: crate::leaderboard::Leaderboard,
704}
705
706/// User activity report combining audit trail and analytics
707#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
708pub struct UserActivityReport {
709    pub user_id: Uuid,
710    pub audit_events_count: usize,
711    pub commitment_success_rate: f64,
712    pub total_commitments: i32,
713    pub rank: i32,
714    pub achievements_count: usize,
715    pub activity_period_days: i32,
716}
717
718/// Bulk verification report for admin operations
719#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
720pub struct BulkVerificationReport {
721    pub total_processed: usize,
722    pub verified_count: usize,
723    pub failed_count: usize,
724    pub admin_id: Uuid,
725}
726
727// ========================================================================
728// Phase 8 Data Structures
729// ========================================================================
730
731/// User onboarding report
732#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
733pub struct UserOnboardingReport {
734    pub user_id: Uuid,
735    pub initial_score: rust_decimal::Decimal,
736    pub initial_tier: ReputationTier,
737    pub sbt_created: bool,
738    pub initial_rank: i32,
739    pub total_users: i32,
740    pub baseline_commitments: i32,
741    pub onboarded_at: chrono::DateTime<chrono::Utc>,
742}
743
744/// Crisis type enumeration
745#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
746pub enum CrisisType {
747    MassScoreAnomaly,
748    DatabaseCorruption,
749    MassFraudDetection,
750    SystemOverload,
751}
752
753impl std::fmt::Display for CrisisType {
754    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
755        match self {
756            Self::MassScoreAnomaly => write!(f, "Mass Score Anomaly"),
757            Self::DatabaseCorruption => write!(f, "Database Corruption"),
758            Self::MassFraudDetection => write!(f, "Mass Fraud Detection"),
759            Self::SystemOverload => write!(f, "System Overload"),
760        }
761    }
762}
763
764impl std::str::FromStr for CrisisType {
765    type Err = ReputationError;
766
767    fn from_str(s: &str) -> Result<Self, Self::Err> {
768        match s.to_lowercase().replace([' ', '_', '-'], "").as_str() {
769            "massscoreanomaly" => Ok(CrisisType::MassScoreAnomaly),
770            "databasecorruption" => Ok(CrisisType::DatabaseCorruption),
771            "massfrauddetection" => Ok(CrisisType::MassFraudDetection),
772            "systemoverload" => Ok(CrisisType::SystemOverload),
773            _ => Err(ReputationError::Validation(format!(
774                "Invalid crisis type: {}. Valid values: mass_score_anomaly, database_corruption, mass_fraud_detection, system_overload",
775                s
776            ))),
777        }
778    }
779}
780
781/// Crisis severity levels
782#[derive(
783    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
784)]
785pub enum CrisisSeverity {
786    Low,
787    Medium,
788    High,
789    Critical,
790}
791
792impl std::fmt::Display for CrisisSeverity {
793    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
794        match self {
795            Self::Low => write!(f, "Low"),
796            Self::Medium => write!(f, "Medium"),
797            Self::High => write!(f, "High"),
798            Self::Critical => write!(f, "Critical"),
799        }
800    }
801}
802
803impl std::str::FromStr for CrisisSeverity {
804    type Err = ReputationError;
805
806    fn from_str(s: &str) -> Result<Self, Self::Err> {
807        match s.to_lowercase().as_str() {
808            "low" => Ok(CrisisSeverity::Low),
809            "medium" | "med" => Ok(CrisisSeverity::Medium),
810            "high" => Ok(CrisisSeverity::High),
811            "critical" | "crit" => Ok(CrisisSeverity::Critical),
812            _ => Err(ReputationError::Validation(format!(
813                "Invalid crisis severity: {}. Valid values: low, medium, high, critical",
814                s
815            ))),
816        }
817    }
818}
819
820/// Crisis response report
821#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
822pub struct CrisisResponse {
823    pub crisis_type: CrisisType,
824    pub actions_taken: Vec<String>,
825    pub affected_users: i32,
826    pub severity: CrisisSeverity,
827    pub handled_at: chrono::DateTime<chrono::Utc>,
828}
829
830/// Performance metrics for monitoring
831#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
832pub struct PerformanceMetrics {
833    pub total_users: i32,
834    pub active_users: i32,
835    pub database_healthy: bool,
836    pub score_anomalies: i32,
837    pub stuck_commitments: i32,
838    pub orphaned_records: i32,
839    pub processing_efficiency: f64,
840    pub average_score: rust_decimal::Decimal,
841    pub collection_duration_ms: i64,
842    pub collected_at: chrono::DateTime<chrono::Utc>,
843}
844
845/// Intervention type enumeration
846#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
847pub enum InterventionType {
848    ExpireCommitments,
849    FixScoreAnomalies,
850    ApplyTimeDecay,
851    RebuildTierHistory,
852}
853
854impl std::fmt::Display for InterventionType {
855    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
856        match self {
857            Self::ExpireCommitments => write!(f, "Expire Commitments"),
858            Self::FixScoreAnomalies => write!(f, "Fix Score Anomalies"),
859            Self::ApplyTimeDecay => write!(f, "Apply Time Decay"),
860            Self::RebuildTierHistory => write!(f, "Rebuild Tier History"),
861        }
862    }
863}
864
865impl std::str::FromStr for InterventionType {
866    type Err = ReputationError;
867
868    fn from_str(s: &str) -> Result<Self, Self::Err> {
869        match s.to_lowercase().replace([' ', '_', '-'], "").as_str() {
870            "expirecommitments" => Ok(InterventionType::ExpireCommitments),
871            "fixscoreanomalies" => Ok(InterventionType::FixScoreAnomalies),
872            "applytimedecay" => Ok(InterventionType::ApplyTimeDecay),
873            "rebuildtierhistory" => Ok(InterventionType::RebuildTierHistory),
874            _ => Err(ReputationError::Validation(format!(
875                "Invalid intervention type: {}. Valid values: expire_commitments, fix_score_anomalies, apply_time_decay, rebuild_tier_history",
876                s
877            ))),
878        }
879    }
880}
881
882/// Individual intervention action
883#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
884pub struct InterventionAction {
885    pub action_type: InterventionType,
886    pub description: String,
887    pub users_affected: i32,
888    pub success: bool,
889}
890
891/// Intervention report
892#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
893pub struct InterventionReport {
894    pub interventions: Vec<InterventionAction>,
895    pub total_users_affected: i32,
896    pub interventions_count: usize,
897    pub executed_at: chrono::DateTime<chrono::Utc>,
898}
899
900/// Comprehensive user profile with all data
901#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
902pub struct ComprehensiveUserProfile {
903    pub user_id: Uuid,
904    pub score: rust_decimal::Decimal,
905    pub tier: ReputationTier,
906    pub rank: i32,
907    pub percentile: f64,
908    pub total_users: i32,
909    pub has_sbt: bool,
910    pub sbt_minted: bool,
911    pub verified_external_sources: usize,
912    pub fraud_flags: usize,
913    pub clean_record: bool,
914    pub total_commitments: i32,
915    pub verified_commitments: i32,
916    pub failed_commitments: i32,
917    pub pending_commitments: i32,
918    pub success_rate: f64,
919    pub achievements_count: usize,
920    pub recent_activity_count: usize,
921}
922
923#[cfg(test)]
924mod tests {
925    use super::*;
926
927    #[test]
928    fn test_complete_profile_structure() {
929        let profile = CompleteReputationProfile {
930            score: rust_decimal::Decimal::new(750, 0),
931            tier: ReputationTier::Gold,
932            has_sbt: true,
933            sbt_minted: true,
934            verified_external_sources: 2,
935            fraud_flags: 0,
936            clean_record: true,
937        };
938
939        assert_eq!(profile.tier, ReputationTier::Gold);
940        assert!(profile.clean_record);
941        assert_eq!(profile.verified_external_sources, 2);
942    }
943
944    #[test]
945    fn test_system_health_report_structure() {
946        let health_report = crate::maintenance::HealthCheckReport {
947            healthy: true,
948            database_healthy: true,
949            score_anomalies: 0,
950            stuck_commitments: 0,
951            orphaned_records: 0,
952            checked_at: chrono::Utc::now(),
953        };
954
955        let report = SystemHealthReport {
956            database_healthy: true,
957            total_users: 1000,
958            active_users: 750,
959            anomalies_count: 0,
960            low_score_users_count: 5,
961            health_report,
962        };
963
964        assert!(report.database_healthy);
965        assert_eq!(report.total_users, 1000);
966        assert_eq!(report.anomalies_count, 0);
967    }
968
969    #[test]
970    fn test_daily_maintenance_report_structure() {
971        let report = DailyMaintenanceReport {
972            users_decayed: 50,
973            commitments_expired: 10,
974            scores_recalculated: 1000,
975            timestamp: chrono::Utc::now(),
976        };
977
978        assert_eq!(report.users_decayed, 50);
979        assert_eq!(report.commitments_expired, 10);
980        assert_eq!(report.scores_recalculated, 1000);
981    }
982
983    #[test]
984    fn test_user_activity_report_structure() {
985        let report = UserActivityReport {
986            user_id: Uuid::new_v4(),
987            audit_events_count: 25,
988            commitment_success_rate: 85.5,
989            total_commitments: 100,
990            rank: 42,
991            achievements_count: 5,
992            activity_period_days: 30,
993        };
994
995        assert_eq!(report.audit_events_count, 25);
996        assert_eq!(report.rank, 42);
997        assert_eq!(report.achievements_count, 5);
998        assert_eq!(report.total_commitments, 100);
999    }
1000
1001    #[test]
1002    fn test_bulk_verification_report_structure() {
1003        let report = BulkVerificationReport {
1004            total_processed: 100,
1005            verified_count: 95,
1006            failed_count: 5,
1007            admin_id: Uuid::new_v4(),
1008        };
1009
1010        assert_eq!(report.total_processed, 100);
1011        assert_eq!(report.verified_count, 95);
1012        assert_eq!(report.failed_count, 5);
1013    }
1014
1015    // Phase 8 tests
1016    #[test]
1017    fn test_user_onboarding_report_structure() {
1018        let report = UserOnboardingReport {
1019            user_id: Uuid::new_v4(),
1020            initial_score: rust_decimal::Decimal::new(500, 0),
1021            initial_tier: ReputationTier::Bronze,
1022            sbt_created: true,
1023            initial_rank: 150,
1024            total_users: 1000,
1025            baseline_commitments: 0,
1026            onboarded_at: chrono::Utc::now(),
1027        };
1028
1029        assert_eq!(report.initial_tier, ReputationTier::Bronze);
1030        assert!(report.sbt_created);
1031        assert_eq!(report.initial_rank, 150);
1032        assert_eq!(report.total_users, 1000);
1033    }
1034
1035    #[test]
1036    fn test_crisis_type_variants() {
1037        let crisis1 = CrisisType::MassScoreAnomaly;
1038        let crisis2 = CrisisType::DatabaseCorruption;
1039        let crisis3 = CrisisType::MassFraudDetection;
1040        let crisis4 = CrisisType::SystemOverload;
1041
1042        assert_eq!(crisis1, CrisisType::MassScoreAnomaly);
1043        assert_eq!(crisis2, CrisisType::DatabaseCorruption);
1044        assert_eq!(crisis3, CrisisType::MassFraudDetection);
1045        assert_eq!(crisis4, CrisisType::SystemOverload);
1046    }
1047
1048    #[test]
1049    fn test_crisis_severity_ordering() {
1050        assert!(CrisisSeverity::Low < CrisisSeverity::Medium);
1051        assert!(CrisisSeverity::Medium < CrisisSeverity::High);
1052        assert!(CrisisSeverity::High < CrisisSeverity::Critical);
1053    }
1054
1055    #[test]
1056    fn test_crisis_response_structure() {
1057        let response = CrisisResponse {
1058            crisis_type: CrisisType::MassScoreAnomaly,
1059            actions_taken: vec!["Fixed 10 anomalies".to_string()],
1060            affected_users: 10,
1061            severity: CrisisSeverity::Medium,
1062            handled_at: chrono::Utc::now(),
1063        };
1064
1065        assert_eq!(response.crisis_type, CrisisType::MassScoreAnomaly);
1066        assert_eq!(response.affected_users, 10);
1067        assert_eq!(response.severity, CrisisSeverity::Medium);
1068        assert_eq!(response.actions_taken.len(), 1);
1069    }
1070
1071    #[test]
1072    fn test_performance_metrics_structure() {
1073        let metrics = PerformanceMetrics {
1074            total_users: 1000,
1075            active_users: 750,
1076            database_healthy: true,
1077            score_anomalies: 2,
1078            stuck_commitments: 1,
1079            orphaned_records: 0,
1080            processing_efficiency: 95.5,
1081            average_score: rust_decimal::Decimal::new(650, 0),
1082            collection_duration_ms: 125,
1083            collected_at: chrono::Utc::now(),
1084        };
1085
1086        assert_eq!(metrics.total_users, 1000);
1087        assert_eq!(metrics.active_users, 750);
1088        assert!(metrics.database_healthy);
1089        assert_eq!(metrics.processing_efficiency, 95.5);
1090    }
1091
1092    #[test]
1093    fn test_intervention_type_variants() {
1094        let int1 = InterventionType::ExpireCommitments;
1095        let int2 = InterventionType::FixScoreAnomalies;
1096        let int3 = InterventionType::ApplyTimeDecay;
1097        let int4 = InterventionType::RebuildTierHistory;
1098
1099        assert_eq!(int1, InterventionType::ExpireCommitments);
1100        assert_eq!(int2, InterventionType::FixScoreAnomalies);
1101        assert_eq!(int3, InterventionType::ApplyTimeDecay);
1102        assert_eq!(int4, InterventionType::RebuildTierHistory);
1103    }
1104
1105    #[test]
1106    fn test_intervention_action_structure() {
1107        let action = InterventionAction {
1108            action_type: InterventionType::ExpireCommitments,
1109            description: "Expired 5 overdue commitments".to_string(),
1110            users_affected: 5,
1111            success: true,
1112        };
1113
1114        assert_eq!(action.action_type, InterventionType::ExpireCommitments);
1115        assert!(action.success);
1116        assert_eq!(action.users_affected, 5);
1117    }
1118
1119    #[test]
1120    fn test_intervention_report_structure() {
1121        let action = InterventionAction {
1122            action_type: InterventionType::FixScoreAnomalies,
1123            description: "Fixed 3 anomalies".to_string(),
1124            users_affected: 3,
1125            success: true,
1126        };
1127
1128        let report = InterventionReport {
1129            interventions: vec![action],
1130            total_users_affected: 3,
1131            interventions_count: 1,
1132            executed_at: chrono::Utc::now(),
1133        };
1134
1135        assert_eq!(report.interventions_count, 1);
1136        assert_eq!(report.total_users_affected, 3);
1137    }
1138
1139    #[test]
1140    fn test_comprehensive_user_profile_structure() {
1141        let profile = ComprehensiveUserProfile {
1142            user_id: Uuid::new_v4(),
1143            score: rust_decimal::Decimal::new(750, 0),
1144            tier: ReputationTier::Gold,
1145            rank: 42,
1146            percentile: 95.5,
1147            total_users: 1000,
1148            has_sbt: true,
1149            sbt_minted: true,
1150            verified_external_sources: 2,
1151            fraud_flags: 0,
1152            clean_record: true,
1153            total_commitments: 50,
1154            verified_commitments: 45,
1155            failed_commitments: 3,
1156            pending_commitments: 2,
1157            success_rate: 90.0,
1158            achievements_count: 7,
1159            recent_activity_count: 15,
1160        };
1161
1162        assert_eq!(profile.tier, ReputationTier::Gold);
1163        assert_eq!(profile.rank, 42);
1164        assert_eq!(profile.percentile, 95.5);
1165        assert_eq!(profile.total_commitments, 50);
1166        assert_eq!(profile.success_rate, 90.0);
1167        assert!(profile.clean_record);
1168    }
1169
1170    #[test]
1171    fn test_crisis_type_display() {
1172        assert_eq!(
1173            CrisisType::MassScoreAnomaly.to_string(),
1174            "Mass Score Anomaly"
1175        );
1176        assert_eq!(
1177            CrisisType::DatabaseCorruption.to_string(),
1178            "Database Corruption"
1179        );
1180        assert_eq!(
1181            CrisisType::MassFraudDetection.to_string(),
1182            "Mass Fraud Detection"
1183        );
1184        assert_eq!(CrisisType::SystemOverload.to_string(), "System Overload");
1185    }
1186
1187    #[test]
1188    fn test_crisis_severity_display() {
1189        assert_eq!(CrisisSeverity::Low.to_string(), "Low");
1190        assert_eq!(CrisisSeverity::Medium.to_string(), "Medium");
1191        assert_eq!(CrisisSeverity::High.to_string(), "High");
1192        assert_eq!(CrisisSeverity::Critical.to_string(), "Critical");
1193    }
1194
1195    #[test]
1196    fn test_intervention_type_display() {
1197        assert_eq!(
1198            InterventionType::ExpireCommitments.to_string(),
1199            "Expire Commitments"
1200        );
1201        assert_eq!(
1202            InterventionType::FixScoreAnomalies.to_string(),
1203            "Fix Score Anomalies"
1204        );
1205        assert_eq!(
1206            InterventionType::ApplyTimeDecay.to_string(),
1207            "Apply Time Decay"
1208        );
1209        assert_eq!(
1210            InterventionType::RebuildTierHistory.to_string(),
1211            "Rebuild Tier History"
1212        );
1213    }
1214
1215    #[test]
1216    fn test_crisis_type_from_str_valid() {
1217        assert_eq!(
1218            "mass_score_anomaly".parse::<CrisisType>().unwrap(),
1219            CrisisType::MassScoreAnomaly
1220        );
1221        assert_eq!(
1222            "database_corruption".parse::<CrisisType>().unwrap(),
1223            CrisisType::DatabaseCorruption
1224        );
1225        assert_eq!(
1226            "mass_fraud_detection".parse::<CrisisType>().unwrap(),
1227            CrisisType::MassFraudDetection
1228        );
1229        assert_eq!(
1230            "system_overload".parse::<CrisisType>().unwrap(),
1231            CrisisType::SystemOverload
1232        );
1233    }
1234
1235    #[test]
1236    fn test_crisis_type_from_str_flexible() {
1237        // Test with spaces
1238        assert_eq!(
1239            "mass score anomaly".parse::<CrisisType>().unwrap(),
1240            CrisisType::MassScoreAnomaly
1241        );
1242        assert_eq!(
1243            "database corruption".parse::<CrisisType>().unwrap(),
1244            CrisisType::DatabaseCorruption
1245        );
1246        // Test with hyphens
1247        assert_eq!(
1248            "mass-fraud-detection".parse::<CrisisType>().unwrap(),
1249            CrisisType::MassFraudDetection
1250        );
1251        // Test without separators
1252        assert_eq!(
1253            "systemoverload".parse::<CrisisType>().unwrap(),
1254            CrisisType::SystemOverload
1255        );
1256    }
1257
1258    #[test]
1259    fn test_crisis_type_from_str_case_insensitive() {
1260        assert_eq!(
1261            "MASS_SCORE_ANOMALY".parse::<CrisisType>().unwrap(),
1262            CrisisType::MassScoreAnomaly
1263        );
1264        assert_eq!(
1265            "Database Corruption".parse::<CrisisType>().unwrap(),
1266            CrisisType::DatabaseCorruption
1267        );
1268    }
1269
1270    #[test]
1271    fn test_crisis_type_from_str_invalid() {
1272        assert!("invalid".parse::<CrisisType>().is_err());
1273        assert!("unknown_crisis".parse::<CrisisType>().is_err());
1274    }
1275
1276    #[test]
1277    fn test_crisis_severity_from_str_valid() {
1278        assert_eq!(
1279            "low".parse::<CrisisSeverity>().unwrap(),
1280            CrisisSeverity::Low
1281        );
1282        assert_eq!(
1283            "medium".parse::<CrisisSeverity>().unwrap(),
1284            CrisisSeverity::Medium
1285        );
1286        assert_eq!(
1287            "high".parse::<CrisisSeverity>().unwrap(),
1288            CrisisSeverity::High
1289        );
1290        assert_eq!(
1291            "critical".parse::<CrisisSeverity>().unwrap(),
1292            CrisisSeverity::Critical
1293        );
1294    }
1295
1296    #[test]
1297    fn test_crisis_severity_from_str_variants() {
1298        assert_eq!(
1299            "med".parse::<CrisisSeverity>().unwrap(),
1300            CrisisSeverity::Medium
1301        );
1302        assert_eq!(
1303            "crit".parse::<CrisisSeverity>().unwrap(),
1304            CrisisSeverity::Critical
1305        );
1306    }
1307
1308    #[test]
1309    fn test_crisis_severity_from_str_case_insensitive() {
1310        assert_eq!(
1311            "LOW".parse::<CrisisSeverity>().unwrap(),
1312            CrisisSeverity::Low
1313        );
1314        assert_eq!(
1315            "Medium".parse::<CrisisSeverity>().unwrap(),
1316            CrisisSeverity::Medium
1317        );
1318    }
1319
1320    #[test]
1321    fn test_crisis_severity_from_str_invalid() {
1322        assert!("invalid".parse::<CrisisSeverity>().is_err());
1323        assert!("moderate".parse::<CrisisSeverity>().is_err());
1324    }
1325
1326    #[test]
1327    fn test_intervention_type_from_str_valid() {
1328        assert_eq!(
1329            "expire_commitments".parse::<InterventionType>().unwrap(),
1330            InterventionType::ExpireCommitments
1331        );
1332        assert_eq!(
1333            "fix_score_anomalies".parse::<InterventionType>().unwrap(),
1334            InterventionType::FixScoreAnomalies
1335        );
1336        assert_eq!(
1337            "apply_time_decay".parse::<InterventionType>().unwrap(),
1338            InterventionType::ApplyTimeDecay
1339        );
1340        assert_eq!(
1341            "rebuild_tier_history".parse::<InterventionType>().unwrap(),
1342            InterventionType::RebuildTierHistory
1343        );
1344    }
1345
1346    #[test]
1347    fn test_intervention_type_from_str_flexible() {
1348        // Test with spaces
1349        assert_eq!(
1350            "expire commitments".parse::<InterventionType>().unwrap(),
1351            InterventionType::ExpireCommitments
1352        );
1353        // Test with hyphens
1354        assert_eq!(
1355            "fix-score-anomalies".parse::<InterventionType>().unwrap(),
1356            InterventionType::FixScoreAnomalies
1357        );
1358    }
1359
1360    #[test]
1361    fn test_intervention_type_from_str_case_insensitive() {
1362        assert_eq!(
1363            "EXPIRE_COMMITMENTS".parse::<InterventionType>().unwrap(),
1364            InterventionType::ExpireCommitments
1365        );
1366    }
1367
1368    #[test]
1369    fn test_intervention_type_from_str_invalid() {
1370        assert!("invalid".parse::<InterventionType>().is_err());
1371    }
1372}