1use 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
18use crate::analytics::AnalyticsService;
20use crate::audit::{AuditSearch, AuditService};
21use crate::batch::BatchService;
22use crate::leaderboard::LeaderboardService;
23use crate::maintenance::MaintenanceService;
24
25pub 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 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 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 pub async fn on_first_token_issuance(&self, user_id: Uuid) -> Result<(), ReputationError> {
59 if self.sbt_service.has_sbt(user_id).await? {
61 return Ok(());
62 }
63
64 let score = self.engine.get_score(user_id).await?;
66
67 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 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 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 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 pub async fn process_dispute_vote(
144 &self,
145 vote_request: VoteDisputeRequest,
146 ) -> Result<(), ReputationError> {
147 self.dispute_service.vote(vote_request.clone()).await?;
149
150 let dispute = self
152 .dispute_service
153 .get_dispute(vote_request.dispute_id)
154 .await?;
155 if dispute.voting_deadline < chrono::Utc::now() {
156 let results = self
158 .dispute_service
159 .resolve_dispute(vote_request.dispute_id)
160 .await?;
161
162 if results.outcome == crate::dispute::DisputeOutcome::Upheld {
164 }
167 }
168
169 Ok(())
170 }
171
172 pub async fn import_and_apply_external_reputation(
174 &self,
175 import_request: ImportReputationRequest,
176 ) -> Result<i32, ReputationError> {
177 let import_result = self
179 .portability_service
180 .import_reputation(import_request.clone())
181 .await?;
182
183 Ok(import_result.imported_score)
186 }
187
188 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 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 pub async fn run_system_health_check(&self) -> Result<SystemHealthReport, ReputationError> {
231 let health_report = self.maintenance_service.run_health_checks().await?;
233
234 let system_stats = self.analytics_service.get_system_stats().await?;
236
237 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 pub async fn run_daily_maintenance(&self) -> Result<DailyMaintenanceReport, ReputationError> {
259 let decay_result = self.batch_service.apply_time_decay_batch().await?;
261
262 let expire_result = self.batch_service.expire_overdue_commitments().await?;
264
265 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 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 pub async fn process_user_achievements(
298 &self,
299 user_id: Uuid,
300 ) -> Result<Vec<crate::leaderboard::AwardedAchievement>, ReputationError> {
301 let achievements = self
303 .leaderboard_service
304 .get_user_achievements(user_id)
305 .await?;
306
307 let _rank_info = self.leaderboard_service.get_user_rank(user_id).await?;
309
310 Ok(achievements)
311 }
312
313 pub async fn get_user_activity_report(
315 &self,
316 user_id: Uuid,
317 limit: i32,
318 ) -> Result<UserActivityReport, ReputationError> {
319 let audit_records = self
321 .audit_service
322 .get_user_audit_trail(user_id, limit, 0)
323 .await?;
324
325 let commitment_analytics = self
327 .analytics_service
328 .get_user_commitment_analytics(user_id)
329 .await?;
330
331 let rank_info = self.leaderboard_service.get_user_rank(user_id).await?;
333
334 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 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 Ok(BulkVerificationReport {
365 total_processed: commitment_ids.len(),
366 verified_count,
367 failed_count,
368 admin_id,
369 })
370 }
371
372 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 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 pub async fn onboard_new_user(
398 &self,
399 user_id: Uuid,
400 ) -> Result<UserOnboardingReport, ReputationError> {
401 let initial_score = self.engine.get_score(user_id).await?;
403
404 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 let rank_info = self.leaderboard_service.get_user_rank(user_id).await?;
414
415 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 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 let health_report = self.maintenance_service.run_health_checks().await?;
446
447 if health_report.score_anomalies > 0 {
448 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 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 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 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 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 pub async fn collect_performance_metrics(&self) -> Result<PerformanceMetrics, ReputationError> {
516 let start_time = chrono::Utc::now();
517
518 let system_stats = self.analytics_service.get_system_stats().await?;
520
521 let health_report = self.maintenance_service.run_health_checks().await?;
523
524 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 pub async fn run_automated_interventions(&self) -> Result<InterventionReport, ReputationError> {
552 let mut interventions = Vec::new();
553 let mut users_affected = 0;
554
555 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 let health_report = self.maintenance_service.run_health_checks().await?;
572 if health_report.score_anomalies > 0 {
573 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 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 pub async fn get_comprehensive_user_profile(
614 &self,
615 user_id: Uuid,
616 ) -> Result<ComprehensiveUserProfile, ReputationError> {
617 let base_profile = self.get_complete_profile(user_id).await?;
619
620 let rank_info = self.leaderboard_service.get_user_rank(user_id).await?;
622
623 let achievements = self
625 .leaderboard_service
626 .get_user_achievements(user_id)
627 .await?;
628
629 let commitment_analytics = self
631 .analytics_service
632 .get_user_commitment_analytics(user_id)
633 .await?;
634
635 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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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 #[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 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 assert_eq!(
1248 "mass-fraud-detection".parse::<CrisisType>().unwrap(),
1249 CrisisType::MassFraudDetection
1250 );
1251 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 assert_eq!(
1350 "expire commitments".parse::<InterventionType>().unwrap(),
1351 InterventionType::ExpireCommitments
1352 );
1353 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}