ipfrs-transport 0.2.0

Transport protocols and zero-copy data exchange for IPFRS distributed system
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
//! Diagnostic utilities for troubleshooting transport issues
//!
//! This module provides comprehensive diagnostic tools to help identify
//! and troubleshoot issues in the transport layer.

use crate::{ConcurrentPeerManager, ConcurrentWantList, Session};
use std::collections::HashMap;
use std::fmt;
use std::time::Duration;

/// Comprehensive diagnostic report for the transport layer
#[derive(Debug, Clone)]
pub struct DiagnosticReport {
    /// Timestamp when the report was generated
    pub timestamp: std::time::SystemTime,
    /// Overall health status
    pub health_status: HealthStatus,
    /// Want list diagnostics
    pub want_list: WantListDiagnostics,
    /// Peer manager diagnostics
    pub peer_manager: PeerManagerDiagnostics,
    /// Session diagnostics (if available)
    pub sessions: Vec<SessionDiagnostics>,
    /// Identified issues
    pub issues: Vec<DiagnosticIssue>,
    /// Recommendations
    pub recommendations: Vec<String>,
}

/// Overall health status of the transport layer
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HealthStatus {
    /// Everything is functioning normally
    Healthy,
    /// Minor issues detected but system is functional
    Degraded,
    /// Significant issues affecting performance
    Warning,
    /// Critical issues requiring immediate attention
    Critical,
}

/// Diagnostic information about the want list
#[derive(Debug, Clone)]
pub struct WantListDiagnostics {
    /// Total number of wants
    pub total_wants: usize,
    /// Wants by priority distribution
    pub priority_distribution: HashMap<String, usize>,
    /// Number of expired wants
    pub expired_wants: usize,
    /// Number of wants awaiting retry
    pub retry_pending: usize,
    /// Average time wants are in the queue
    pub avg_queue_time: Duration,
    /// Oldest want age
    pub oldest_want_age: Option<Duration>,
}

/// Diagnostic information about peer management
#[derive(Debug, Clone)]
pub struct PeerManagerDiagnostics {
    /// Total number of peers
    pub total_peers: usize,
    /// Active peers (available for requests)
    pub active_peers: usize,
    /// Blacklisted peers
    pub blacklisted_peers: usize,
    /// Average peer score
    pub avg_peer_score: f64,
    /// Peers with circuit breaker open
    pub circuit_breaker_open: usize,
    /// Average latency across all peers
    pub avg_latency: Option<Duration>,
    /// Total bandwidth (bytes/sec)
    pub total_bandwidth: u64,
}

/// Diagnostic information about a session
#[derive(Debug, Clone)]
pub struct SessionDiagnostics {
    /// Session ID
    pub session_id: u64,
    /// Current state
    pub state: String,
    /// Number of blocks requested
    pub blocks_requested: usize,
    /// Number of blocks received
    pub blocks_received: usize,
    /// Progress percentage
    pub progress_percent: f64,
    /// Time elapsed since session start
    pub elapsed_time: Duration,
    /// Estimated time remaining
    pub estimated_remaining: Option<Duration>,
    /// Current throughput (bytes/sec)
    pub throughput: u64,
}

/// Identified issue in the transport layer
#[derive(Debug, Clone)]
pub struct DiagnosticIssue {
    /// Issue severity
    pub severity: IssueSeverity,
    /// Issue category
    pub category: IssueCategory,
    /// Human-readable description
    pub description: String,
    /// Detailed information
    pub details: Option<String>,
}

/// Severity of a diagnostic issue
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum IssueSeverity {
    /// Informational, no action required
    Info,
    /// Warning, may need attention
    Warning,
    /// Error, should be addressed
    Error,
    /// Critical, requires immediate action
    Critical,
}

/// Category of diagnostic issue
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IssueCategory {
    /// Issue with want list management
    WantList,
    /// Issue with peer connectivity or selection
    PeerManagement,
    /// Issue with session handling
    Session,
    /// Issue with network performance
    Performance,
    /// Configuration issue
    Configuration,
}

/// Main diagnostic engine for analyzing transport layer health
pub struct DiagnosticEngine {
    /// Thresholds for issue detection
    config: DiagnosticConfig,
}

/// Configuration for diagnostic analysis
#[derive(Debug, Clone)]
pub struct DiagnosticConfig {
    /// Max acceptable queue time before warning
    pub max_queue_time: Duration,
    /// Minimum acceptable number of active peers
    pub min_active_peers: usize,
    /// Minimum acceptable average peer score
    pub min_avg_score: f64,
    /// Max acceptable expired wants ratio
    pub max_expired_ratio: f64,
    /// Min acceptable session progress rate (blocks/sec)
    pub min_progress_rate: f64,
}

impl Default for DiagnosticConfig {
    fn default() -> Self {
        Self {
            max_queue_time: Duration::from_secs(60),
            min_active_peers: 3,
            min_avg_score: 0.5,
            max_expired_ratio: 0.1, // 10%
            min_progress_rate: 1.0, // At least 1 block per second
        }
    }
}

impl DiagnosticEngine {
    /// Create a new diagnostic engine with default configuration
    pub fn new() -> Self {
        Self {
            config: DiagnosticConfig::default(),
        }
    }

    /// Create a new diagnostic engine with custom configuration
    pub fn with_config(config: DiagnosticConfig) -> Self {
        Self { config }
    }

    /// Generate a comprehensive diagnostic report
    pub fn generate_report(
        &self,
        want_list: &ConcurrentWantList,
        peer_manager: &ConcurrentPeerManager,
        sessions: &[&Session],
    ) -> DiagnosticReport {
        let want_diag = self.diagnose_want_list(want_list);
        let peer_diag = self.diagnose_peer_manager(peer_manager);
        let session_diags: Vec<_> = sessions.iter().map(|s| self.diagnose_session(s)).collect();

        let mut issues = Vec::new();
        issues.extend(self.detect_want_list_issues(&want_diag));
        issues.extend(self.detect_peer_issues(&peer_diag));
        issues.extend(self.detect_session_issues(&session_diags));

        let health_status = self.determine_health_status(&issues);
        let recommendations = self.generate_recommendations(&issues, &want_diag, &peer_diag);

        DiagnosticReport {
            timestamp: std::time::SystemTime::now(),
            health_status,
            want_list: want_diag,
            peer_manager: peer_diag,
            sessions: session_diags,
            issues,
            recommendations,
        }
    }

    /// Diagnose want list health
    fn diagnose_want_list(&self, want_list: &ConcurrentWantList) -> WantListDiagnostics {
        // Get all CIDs and then get batch of entries
        let cids = want_list.cids();
        let all_wants = want_list.get_batch(&cids);
        let total_wants = all_wants.len();

        let mut priority_distribution = HashMap::new();
        let mut expired_count = 0;
        let mut retry_count = 0;
        let mut total_age = Duration::ZERO;
        let mut oldest_age = None;

        let now = std::time::Instant::now();

        for want in &all_wants {
            // Priority distribution
            let priority_label = match want.priority {
                p if p >= 900 => "Critical",
                p if p >= 700 => "Urgent",
                p if p >= 500 => "High",
                p if p >= 300 => "Normal",
                _ => "Low",
            }
            .to_string();
            *priority_distribution.entry(priority_label).or_insert(0) += 1;

            // Check if expired
            if let Some(deadline) = want.deadline {
                if now >= deadline {
                    expired_count += 1;
                }
            }

            // Check retry status
            if want.retry_count > 0 {
                retry_count += 1;
            }

            // Calculate age
            let age = now.duration_since(want.created_at);
            total_age += age;
            oldest_age = Some(oldest_age.map_or(age, |old: Duration| old.max(age)));
        }

        let avg_queue_time = if total_wants > 0 {
            total_age / total_wants as u32
        } else {
            Duration::ZERO
        };

        WantListDiagnostics {
            total_wants,
            priority_distribution,
            expired_wants: expired_count,
            retry_pending: retry_count,
            avg_queue_time,
            oldest_want_age: oldest_age,
        }
    }

    /// Diagnose peer manager health
    fn diagnose_peer_manager(
        &self,
        peer_manager: &ConcurrentPeerManager,
    ) -> PeerManagerDiagnostics {
        let stats = peer_manager.stats();

        PeerManagerDiagnostics {
            total_peers: stats.total_peers,
            active_peers: stats.connected_peers,
            blacklisted_peers: stats.blacklisted_peers,
            avg_peer_score: stats.avg_score,
            circuit_breaker_open: 0, // Would need to query each peer's circuit breaker
            avg_latency: Some(Duration::from_millis(stats.avg_latency_ms as u64)),
            total_bandwidth: 0, // PeerManagerStats doesn't track total bytes
        }
    }

    /// Diagnose session health
    fn diagnose_session(&self, session: &Session) -> SessionDiagnostics {
        let stats = session.stats();
        let state = format!("{:?}", session.state());

        let progress_percent = if stats.total_blocks > 0 {
            (stats.blocks_received as f64 / stats.total_blocks as f64) * 100.0
        } else {
            0.0
        };

        // Calculate elapsed time from timestamps
        let elapsed_time = if let Some(start) = stats.started_at {
            if let Some(end) = stats.completed_at {
                end.duration_since(start)
            } else {
                std::time::Instant::now().duration_since(start)
            }
        } else {
            Duration::ZERO
        };

        // Estimate remaining time based on current throughput
        let estimated_remaining = if stats.bytes_transferred > 0
            && stats.total_blocks > stats.blocks_received
        {
            let remaining_blocks = stats.total_blocks - stats.blocks_received;
            let blocks_per_sec = stats.blocks_received as f64 / elapsed_time.as_secs_f64().max(1.0);
            if blocks_per_sec > 0.0 {
                Some(Duration::from_secs_f64(
                    remaining_blocks as f64 / blocks_per_sec,
                ))
            } else {
                None
            }
        } else {
            None
        };

        let throughput = if elapsed_time.as_secs() > 0 {
            stats.bytes_transferred / elapsed_time.as_secs()
        } else {
            0
        };

        SessionDiagnostics {
            session_id: session.id(),
            state,
            blocks_requested: stats.total_blocks,
            blocks_received: stats.blocks_received,
            progress_percent,
            elapsed_time,
            estimated_remaining,
            throughput,
        }
    }

    /// Detect issues in want list
    fn detect_want_list_issues(&self, diag: &WantListDiagnostics) -> Vec<DiagnosticIssue> {
        let mut issues = Vec::new();

        // Check for excessive queue time
        if diag.avg_queue_time > self.config.max_queue_time {
            issues.push(DiagnosticIssue {
                severity: IssueSeverity::Warning,
                category: IssueCategory::WantList,
                description: "High average queue time for wants".to_string(),
                details: Some(format!(
                    "Average queue time is {:?}, exceeds threshold of {:?}",
                    diag.avg_queue_time, self.config.max_queue_time
                )),
            });
        }

        // Check for high expired ratio
        if diag.total_wants > 0 {
            let expired_ratio = diag.expired_wants as f64 / diag.total_wants as f64;
            if expired_ratio > self.config.max_expired_ratio {
                issues.push(DiagnosticIssue {
                    severity: IssueSeverity::Error,
                    category: IssueCategory::WantList,
                    description: "High ratio of expired wants".to_string(),
                    details: Some(format!(
                        "{:.1}% of wants have expired (threshold: {:.1}%)",
                        expired_ratio * 100.0,
                        self.config.max_expired_ratio * 100.0
                    )),
                });
            }
        }

        // Check for many retries
        if diag.retry_pending > diag.total_wants / 2 {
            issues.push(DiagnosticIssue {
                severity: IssueSeverity::Warning,
                category: IssueCategory::Performance,
                description: "High number of wants awaiting retry".to_string(),
                details: Some(format!(
                    "{} out of {} wants are awaiting retry",
                    diag.retry_pending, diag.total_wants
                )),
            });
        }

        issues
    }

    /// Detect issues in peer management
    fn detect_peer_issues(&self, diag: &PeerManagerDiagnostics) -> Vec<DiagnosticIssue> {
        let mut issues = Vec::new();

        // Check for insufficient active peers
        if diag.active_peers < self.config.min_active_peers {
            issues.push(DiagnosticIssue {
                severity: IssueSeverity::Critical,
                category: IssueCategory::PeerManagement,
                description: "Insufficient active peers".to_string(),
                details: Some(format!(
                    "Only {} active peers (minimum recommended: {})",
                    diag.active_peers, self.config.min_active_peers
                )),
            });
        }

        // Check average peer score
        if diag.avg_peer_score < self.config.min_avg_score {
            issues.push(DiagnosticIssue {
                severity: IssueSeverity::Warning,
                category: IssueCategory::PeerManagement,
                description: "Low average peer score".to_string(),
                details: Some(format!(
                    "Average peer score is {:.2} (threshold: {:.2})",
                    diag.avg_peer_score, self.config.min_avg_score
                )),
            });
        }

        // Check for high blacklist ratio
        if diag.total_peers > 0 {
            let blacklist_ratio = diag.blacklisted_peers as f64 / diag.total_peers as f64;
            if blacklist_ratio > 0.3 {
                issues.push(DiagnosticIssue {
                    severity: IssueSeverity::Warning,
                    category: IssueCategory::PeerManagement,
                    description: "High percentage of blacklisted peers".to_string(),
                    details: Some(format!(
                        "{:.1}% of peers are blacklisted",
                        blacklist_ratio * 100.0
                    )),
                });
            }
        }

        issues
    }

    /// Detect issues in sessions
    fn detect_session_issues(&self, sessions: &[SessionDiagnostics]) -> Vec<DiagnosticIssue> {
        let mut issues = Vec::new();

        for session in sessions {
            // Check for stalled sessions
            if session.blocks_requested > 0
                && session.progress_percent < 10.0
                && session.elapsed_time > Duration::from_secs(30)
            {
                issues.push(DiagnosticIssue {
                    severity: IssueSeverity::Warning,
                    category: IssueCategory::Session,
                    description: format!("Session {} appears stalled", session.session_id),
                    details: Some(format!(
                        "Only {:.1}% progress after {:?}",
                        session.progress_percent, session.elapsed_time
                    )),
                });
            }

            // Check for low throughput
            if session.elapsed_time > Duration::from_secs(10) && session.throughput < 10_000 {
                // < 10 KB/s
                issues.push(DiagnosticIssue {
                    severity: IssueSeverity::Warning,
                    category: IssueCategory::Performance,
                    description: format!("Low throughput for session {}", session.session_id),
                    details: Some(format!(
                        "Current throughput: {} bytes/sec",
                        session.throughput
                    )),
                });
            }
        }

        issues
    }

    /// Determine overall health status based on issues
    fn determine_health_status(&self, issues: &[DiagnosticIssue]) -> HealthStatus {
        let mut max_severity = IssueSeverity::Info;

        for issue in issues {
            if issue.severity > max_severity {
                max_severity = issue.severity;
            }
        }

        match max_severity {
            IssueSeverity::Info => HealthStatus::Healthy,
            IssueSeverity::Warning => HealthStatus::Degraded,
            IssueSeverity::Error => HealthStatus::Warning,
            IssueSeverity::Critical => HealthStatus::Critical,
        }
    }

    /// Generate recommendations based on detected issues
    fn generate_recommendations(
        &self,
        issues: &[DiagnosticIssue],
        want_diag: &WantListDiagnostics,
        peer_diag: &PeerManagerDiagnostics,
    ) -> Vec<String> {
        let mut recommendations = Vec::new();

        // Recommendations based on issues
        for issue in issues {
            match issue.category {
                IssueCategory::PeerManagement
                    if peer_diag.active_peers < self.config.min_active_peers =>
                {
                    recommendations.push(
                        "Consider connecting to more peers to improve redundancy and performance"
                            .to_string(),
                    );
                }
                IssueCategory::WantList if want_diag.expired_wants > 0 => {
                    recommendations.push(
                        "Consider increasing timeout duration or improving network connectivity"
                            .to_string(),
                    );
                }
                IssueCategory::Performance => {
                    recommendations.push(
                        "Check network conditions and consider adjusting batch sizes or concurrency limits".to_string()
                    );
                }
                _ => {}
            }
        }

        // General recommendations based on stats
        if peer_diag.avg_peer_score < 0.7 {
            recommendations.push(
                "Peer quality is suboptimal. Consider finding better peers or adjusting scoring weights".to_string()
            );
        }

        if want_diag.total_wants > 1000 {
            recommendations.push(
                "Large number of pending wants. Consider increasing max_concurrent_blocks or adding more peers".to_string()
            );
        }

        recommendations.dedup();
        recommendations
    }
}

impl Default for DiagnosticEngine {
    fn default() -> Self {
        Self::new()
    }
}

impl fmt::Display for DiagnosticReport {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "=== Transport Diagnostic Report ===")?;
        writeln!(f, "Generated: {:?}", self.timestamp)?;
        writeln!(f, "Health Status: {:?}", self.health_status)?;
        writeln!(f)?;

        writeln!(f, "Want List:")?;
        writeln!(f, "  Total wants: {}", self.want_list.total_wants)?;
        writeln!(f, "  Expired: {}", self.want_list.expired_wants)?;
        writeln!(f, "  Retry pending: {}", self.want_list.retry_pending)?;
        writeln!(f, "  Avg queue time: {:?}", self.want_list.avg_queue_time)?;
        writeln!(f)?;

        writeln!(f, "Peer Manager:")?;
        writeln!(f, "  Total peers: {}", self.peer_manager.total_peers)?;
        writeln!(f, "  Active: {}", self.peer_manager.active_peers)?;
        writeln!(f, "  Blacklisted: {}", self.peer_manager.blacklisted_peers)?;
        writeln!(f, "  Avg score: {:.2}", self.peer_manager.avg_peer_score)?;
        writeln!(f)?;

        if !self.sessions.is_empty() {
            writeln!(f, "Sessions:")?;
            for session in &self.sessions {
                writeln!(
                    f,
                    "  Session {}: {:.1}% complete ({}/{})",
                    session.session_id,
                    session.progress_percent,
                    session.blocks_received,
                    session.blocks_requested
                )?;
            }
            writeln!(f)?;
        }

        if !self.issues.is_empty() {
            writeln!(f, "Issues:")?;
            for issue in &self.issues {
                writeln!(
                    f,
                    "  [{:?}] {}: {}",
                    issue.severity, issue.category, issue.description
                )?;
                if let Some(details) = &issue.details {
                    writeln!(f, "      {}", details)?;
                }
            }
            writeln!(f)?;
        }

        if !self.recommendations.is_empty() {
            writeln!(f, "Recommendations:")?;
            for (i, rec) in self.recommendations.iter().enumerate() {
                writeln!(f, "  {}. {}", i + 1, rec)?;
            }
        }

        Ok(())
    }
}

impl fmt::Display for IssueCategory {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            IssueCategory::WantList => write!(f, "WantList"),
            IssueCategory::PeerManagement => write!(f, "PeerManagement"),
            IssueCategory::Session => write!(f, "Session"),
            IssueCategory::Performance => write!(f, "Performance"),
            IssueCategory::Configuration => write!(f, "Configuration"),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{Priority, WantListConfig};
    use ipfrs_core::Cid;
    use std::time::Duration;

    #[test]
    fn test_diagnostic_engine_creation() {
        let engine = DiagnosticEngine::new();
        assert_eq!(engine.config.min_active_peers, 3);

        let custom_config = DiagnosticConfig {
            min_active_peers: 5,
            ..Default::default()
        };
        let engine = DiagnosticEngine::with_config(custom_config);
        assert_eq!(engine.config.min_active_peers, 5);
    }

    #[test]
    fn test_empty_report() {
        let engine = DiagnosticEngine::new();
        let want_list = ConcurrentWantList::new(WantListConfig::default());
        let peer_manager = ConcurrentPeerManager::new(Default::default());

        let report = engine.generate_report(&want_list, &peer_manager, &[]);

        assert_eq!(report.want_list.total_wants, 0);
        assert_eq!(report.peer_manager.total_peers, 0);
        assert_eq!(report.sessions.len(), 0);
    }

    #[test]
    fn test_detect_low_peer_count() {
        let engine = DiagnosticEngine::new();
        let want_list = ConcurrentWantList::new(WantListConfig::default());
        let peer_manager = ConcurrentPeerManager::new(Default::default());

        let report = engine.generate_report(&want_list, &peer_manager, &[]);

        // Should detect insufficient peers
        assert!(report
            .issues
            .iter()
            .any(|i| matches!(i.category, IssueCategory::PeerManagement)));
        assert_eq!(report.health_status, HealthStatus::Critical);
    }

    #[test]
    fn test_want_list_diagnostics() {
        let engine = DiagnosticEngine::new();
        let want_list = ConcurrentWantList::new(WantListConfig::default());

        // Add a want (note: Cid::default() creates the same CID each time)
        let cid1 = Cid::default();
        want_list.add_simple(cid1, Priority::High as i32);

        let peer_manager = ConcurrentPeerManager::new(Default::default());
        let report = engine.generate_report(&want_list, &peer_manager, &[]);

        assert_eq!(report.want_list.total_wants, 1);
        assert!(!report.want_list.priority_distribution.is_empty());
    }

    #[test]
    fn test_health_status_determination() {
        let engine = DiagnosticEngine::new();

        // No issues - healthy
        let issues = vec![];
        assert_eq!(
            engine.determine_health_status(&issues),
            HealthStatus::Healthy
        );

        // Warning issue
        let issues = vec![DiagnosticIssue {
            severity: IssueSeverity::Warning,
            category: IssueCategory::Performance,
            description: "Test".to_string(),
            details: None,
        }];
        assert_eq!(
            engine.determine_health_status(&issues),
            HealthStatus::Degraded
        );

        // Critical issue
        let issues = vec![DiagnosticIssue {
            severity: IssueSeverity::Critical,
            category: IssueCategory::PeerManagement,
            description: "Test".to_string(),
            details: None,
        }];
        assert_eq!(
            engine.determine_health_status(&issues),
            HealthStatus::Critical
        );
    }

    #[test]
    fn test_report_display() {
        let engine = DiagnosticEngine::new();
        let want_list = ConcurrentWantList::new(WantListConfig::default());
        let peer_manager = ConcurrentPeerManager::new(Default::default());

        let report = engine.generate_report(&want_list, &peer_manager, &[]);
        let display = format!("{}", report);

        assert!(display.contains("Transport Diagnostic Report"));
        assert!(display.contains("Health Status"));
        assert!(display.contains("Want List"));
        assert!(display.contains("Peer Manager"));
    }

    #[test]
    fn test_issue_severity_ordering() {
        assert!(IssueSeverity::Critical > IssueSeverity::Error);
        assert!(IssueSeverity::Error > IssueSeverity::Warning);
        assert!(IssueSeverity::Warning > IssueSeverity::Info);
    }

    #[test]
    fn test_recommendations_generation() {
        let engine = DiagnosticEngine::new();
        let want_list = ConcurrentWantList::new(WantListConfig::default());
        let peer_manager = ConcurrentPeerManager::new(Default::default());

        let report = engine.generate_report(&want_list, &peer_manager, &[]);

        // Should have recommendations due to low peer count
        assert!(!report.recommendations.is_empty());
    }

    #[test]
    fn test_diagnostic_config_default() {
        let config = DiagnosticConfig::default();
        assert_eq!(config.min_active_peers, 3);
        assert_eq!(config.max_queue_time, Duration::from_secs(60));
        assert_eq!(config.min_avg_score, 0.5);
    }
}