// Tests for defect prediction
// Extracted for file health compliance (CB-040)

use super::*;

mod tests {
    use super::*;
    use crate::services::defect_probability::RiskLevel;
    use std::time::Duration;

    // Helper function to create mock DefectScore for testing
    fn create_mock_defect_score(
        probability: f32,
        confidence: f32,
        risk_level: RiskLevel,
    ) -> DefectScore {
        DefectScore {
            probability,
            confidence,
            risk_level,
            contributing_factors: vec![
                ("complexity".to_string(), 0.25),
                ("churn".to_string(), 0.20),
                ("duplication".to_string(), 0.15),
                ("coupling".to_string(), 0.10),
            ],
            recommendations: vec![
                "Consider refactoring this file".to_string(),
                "Increase test coverage".to_string(),
            ],
        }
    }

    fn create_high_risk_score() -> DefectScore {
        create_mock_defect_score(0.85, 0.90, RiskLevel::High)
    }

    fn create_medium_risk_score() -> DefectScore {
        create_mock_defect_score(0.50, 0.85, RiskLevel::Medium)
    }

    fn create_low_risk_score() -> DefectScore {
        create_mock_defect_score(0.20, 0.80, RiskLevel::Low)
    }

    fn create_test_predictions() -> Vec<(String, DefectScore)> {
        vec![
            ("src/high_risk.rs".to_string(), create_high_risk_score()),
            ("src/medium_risk.rs".to_string(), create_medium_risk_score()),
            ("src/low_risk.rs".to_string(), create_low_risk_score()),
        ]
    }

    // ==================== Test create_defect_prediction_config ====================

    #[test]
    fn test_create_defect_prediction_config_default_values() {
        let config = create_defect_prediction_config(0.5, 10, false, false, true, None, None);

        assert!((config.confidence_threshold - 0.5).abs() < f32::EPSILON);
        assert_eq!(config.min_lines, 10);
        assert!(!config.include_low_confidence);
        assert!(!config.high_risk_only);
        assert!(config.include_recommendations);
        assert!(config.include.is_none());
        assert!(config.exclude.is_none());
    }

    #[test]
    fn test_create_defect_prediction_config_with_patterns() {
        let config = create_defect_prediction_config(
            0.7,
            50,
            true,
            true,
            false,
            Some("src/".to_string()),
            Some("test/".to_string()),
        );

        assert!((config.confidence_threshold - 0.7).abs() < f32::EPSILON);
        assert_eq!(config.min_lines, 50);
        assert!(config.include_low_confidence);
        assert!(config.high_risk_only);
        assert!(!config.include_recommendations);
        assert_eq!(config.include, Some("src/".to_string()));
        assert_eq!(config.exclude, Some("test/".to_string()));
    }

    #[test]
    fn test_create_defect_prediction_config_edge_threshold() {
        let config = create_defect_prediction_config(0.0, 0, false, false, false, None, None);
        assert!((config.confidence_threshold).abs() < f32::EPSILON);
        assert_eq!(config.min_lines, 0);

        let config = create_defect_prediction_config(1.0, 10000, true, true, true, None, None);
        assert!((config.confidence_threshold - 1.0).abs() < f32::EPSILON);
        assert_eq!(config.min_lines, 10000);
    }

    // ==================== Test calculate_risk_statistics ====================

    #[test]
    fn test_calculate_risk_statistics_all_categories() {
        let predictions = create_test_predictions();
        let stats = calculate_risk_statistics(&predictions);

        assert_eq!(stats.high_risk, 1);
        assert_eq!(stats.medium_risk, 1);
        assert_eq!(stats.low_risk, 1);
    }

    #[test]
    fn test_calculate_risk_statistics_empty() {
        let predictions: Vec<(String, DefectScore)> = vec![];
        let stats = calculate_risk_statistics(&predictions);

        assert_eq!(stats.high_risk, 0);
        assert_eq!(stats.medium_risk, 0);
        assert_eq!(stats.low_risk, 0);
    }

    #[test]
    fn test_calculate_risk_statistics_all_high_risk() {
        let predictions = vec![
            ("file1.rs".to_string(), create_high_risk_score()),
            ("file2.rs".to_string(), create_high_risk_score()),
            ("file3.rs".to_string(), create_high_risk_score()),
        ];
        let stats = calculate_risk_statistics(&predictions);

        assert_eq!(stats.high_risk, 3);
        assert_eq!(stats.medium_risk, 0);
        assert_eq!(stats.low_risk, 0);
    }

    #[test]
    fn test_calculate_risk_statistics_all_medium_risk() {
        let predictions = vec![
            ("file1.rs".to_string(), create_medium_risk_score()),
            ("file2.rs".to_string(), create_medium_risk_score()),
        ];
        let stats = calculate_risk_statistics(&predictions);

        assert_eq!(stats.high_risk, 0);
        assert_eq!(stats.medium_risk, 2);
        assert_eq!(stats.low_risk, 0);
    }

    #[test]
    fn test_calculate_risk_statistics_all_low_risk() {
        let predictions = vec![
            ("file1.rs".to_string(), create_low_risk_score()),
            ("file2.rs".to_string(), create_low_risk_score()),
            ("file3.rs".to_string(), create_low_risk_score()),
            ("file4.rs".to_string(), create_low_risk_score()),
        ];
        let stats = calculate_risk_statistics(&predictions);

        assert_eq!(stats.high_risk, 0);
        assert_eq!(stats.medium_risk, 0);
        assert_eq!(stats.low_risk, 4);
    }

    #[test]
    fn test_calculate_risk_statistics_boundary_values() {
        // Test boundary at 0.7 (high/medium)
        let high_boundary = create_mock_defect_score(0.7, 0.9, RiskLevel::Medium);
        // Test boundary at 0.3 (medium/low)
        let low_boundary = create_mock_defect_score(0.3, 0.9, RiskLevel::Low);

        let predictions = vec![
            ("high_boundary.rs".to_string(), high_boundary),
            ("low_boundary.rs".to_string(), low_boundary),
        ];
        let stats = calculate_risk_statistics(&predictions);

        // 0.7 is NOT > 0.7, so it's medium
        assert_eq!(stats.high_risk, 0);
        assert_eq!(stats.medium_risk, 1); // 0.7 is in (0.3, 0.7]
        assert_eq!(stats.low_risk, 1); // 0.3 is <= 0.3
    }

    // ==================== Test get_risk_icon ====================

    #[test]
    fn test_get_risk_icon_high() {
        assert_eq!(get_risk_icon(&RiskLevel::High), "\u{1f534}");
    }

    #[test]
    fn test_get_risk_icon_medium() {
        assert_eq!(get_risk_icon(&RiskLevel::Medium), "\u{1f7e1}");
    }

    #[test]
    fn test_get_risk_icon_low() {
        assert_eq!(get_risk_icon(&RiskLevel::Low), "\u{1f7e2}");
    }

    // ==================== Test format_risk_level_display ====================

    #[test]
    fn test_format_risk_level_display_high() {
        let display = format_risk_level_display(&RiskLevel::High);
        assert!(display.contains("HIGH"));
    }

    #[test]
    fn test_format_risk_level_display_medium() {
        let display = format_risk_level_display(&RiskLevel::Medium);
        assert!(display.contains("MEDIUM"));
    }

    #[test]
    fn test_format_risk_level_display_low() {
        let display = format_risk_level_display(&RiskLevel::Low);
        assert!(display.contains("LOW"));
    }

    // ==================== Test filter_and_sort_predictions ====================

    #[test]
    fn test_filter_and_sort_predictions_high_risk_only() {
        let predictions = create_test_predictions();
        let filtered = filter_and_sort_predictions(
            predictions,
            true,  // high_risk_only
            false, // include_low_confidence
            0.5,   // confidence_threshold
            10,    // top_files
        );

        assert_eq!(filtered.len(), 1);
        assert_eq!(filtered[0].0, "src/high_risk.rs");
    }

    #[test]
    fn test_filter_and_sort_predictions_low_confidence_filtered() {
        let mut predictions = create_test_predictions();
        // Add a low confidence prediction
        predictions.push((
            "low_conf.rs".to_string(),
            create_mock_defect_score(0.9, 0.3, RiskLevel::High),
        ));

        let filtered = filter_and_sort_predictions(
            predictions,
            false, // high_risk_only
            false, // include_low_confidence (filter out low confidence)
            0.5,   // confidence_threshold
            10,    // top_files
        );

        // Should filter out the low confidence file
        assert_eq!(filtered.len(), 3);
        assert!(filtered.iter().all(|(f, _)| f != "low_conf.rs"));
    }

    #[test]
    fn test_filter_and_sort_predictions_include_low_confidence() {
        let mut predictions = create_test_predictions();
        predictions.push((
            "low_conf.rs".to_string(),
            create_mock_defect_score(0.9, 0.3, RiskLevel::High),
        ));

        let filtered = filter_and_sort_predictions(
            predictions,
            false, // high_risk_only
            true,  // include_low_confidence
            0.5,   // confidence_threshold
            10,    // top_files
        );

        // Should include all files
        assert_eq!(filtered.len(), 4);
    }

    #[test]
    fn test_filter_and_sort_predictions_sorted_by_probability() {
        let predictions = create_test_predictions();
        let filtered = filter_and_sort_predictions(
            predictions,
            false, // high_risk_only
            true,  // include_low_confidence
            0.0,   // confidence_threshold
            10,    // top_files
        );

        // Verify sorted by probability descending
        assert_eq!(filtered[0].0, "src/high_risk.rs");
        assert_eq!(filtered[1].0, "src/medium_risk.rs");
        assert_eq!(filtered[2].0, "src/low_risk.rs");
    }

    #[test]
    fn test_filter_and_sort_predictions_top_files_limit() {
        let predictions = create_test_predictions();
        let filtered = filter_and_sort_predictions(
            predictions,
            false, // high_risk_only
            true,  // include_low_confidence
            0.0,   // confidence_threshold
            2,     // top_files - limit to 2
        );

        assert_eq!(filtered.len(), 2);
    }

    #[test]
    fn test_filter_and_sort_predictions_top_files_zero_means_unlimited() {
        let predictions = create_test_predictions();
        let filtered = filter_and_sort_predictions(
            predictions,
            false, // high_risk_only
            true,  // include_low_confidence
            0.0,   // confidence_threshold
            0,     // top_files - 0 means unlimited
        );

        assert_eq!(filtered.len(), 3);
    }

    #[test]
    fn test_filter_and_sort_predictions_empty_input() {
        let predictions: Vec<(String, DefectScore)> = vec![];
        let filtered = filter_and_sort_predictions(predictions, false, true, 0.0, 10);
        assert!(filtered.is_empty());
    }

    #[test]
    fn test_filter_and_sort_predictions_combined_filters() {
        // Test combining high_risk_only AND confidence filtering
        let predictions = vec![
            (
                "high_low_conf.rs".to_string(),
                create_mock_defect_score(0.9, 0.3, RiskLevel::High),
            ),
            (
                "high_high_conf.rs".to_string(),
                create_mock_defect_score(0.85, 0.9, RiskLevel::High),
            ),
            (
                "med_high_conf.rs".to_string(),
                create_mock_defect_score(0.5, 0.9, RiskLevel::Medium),
            ),
        ];

        let filtered = filter_and_sort_predictions(
            predictions,
            true,  // high_risk_only
            false, // filter low confidence
            0.5,   // confidence_threshold
            10,
        );

        // Only high risk with high confidence should remain
        assert_eq!(filtered.len(), 1);
        assert_eq!(filtered[0].0, "high_high_conf.rs");
    }

    // ==================== Test format_defect_output ====================

    #[test]
    fn test_format_defect_output_summary() {
        let predictions = create_test_predictions();
        let elapsed = Duration::from_millis(100);

        let result = format_defect_output(
            DefectPredictionOutputFormat::Summary,
            &predictions,
            elapsed,
            false,
        );

        assert!(result.is_ok());
        let content = result.unwrap();
        assert!(content.contains("Defect Prediction Summary"));
        assert!(content.contains("Risk Distribution"));
    }

    #[test]
    fn test_format_defect_output_json() {
        let predictions = create_test_predictions();
        let elapsed = Duration::from_millis(100);

        let result = format_defect_output(
            DefectPredictionOutputFormat::Json,
            &predictions,
            elapsed,
            false,
        );

        assert!(result.is_ok());
        let content = result.unwrap();
        // Verify it's valid JSON
        let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();
        assert!(parsed.get("analysis_type").is_some());
        assert!(parsed.get("predictions").is_some());
    }

    #[test]
    fn test_format_defect_output_detailed() {
        let predictions = create_test_predictions();
        let elapsed = Duration::from_millis(100);

        let result = format_defect_output(
            DefectPredictionOutputFormat::Detailed,
            &predictions,
            elapsed,
            true,
        );

        assert!(result.is_ok());
        let content = result.unwrap();
        assert!(content.contains("Detailed Report"));
        assert!(content.contains("Recommendations"));
    }

    #[test]
    fn test_format_defect_output_sarif() {
        let predictions = create_test_predictions();
        let elapsed = Duration::from_millis(100);

        let result = format_defect_output(
            DefectPredictionOutputFormat::Sarif,
            &predictions,
            elapsed,
            false,
        );

        assert!(result.is_ok());
        let content = result.unwrap();
        let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();
        assert_eq!(parsed.get("version").unwrap(), "2.1.0");
        assert!(parsed.get("runs").is_some());
    }

    #[test]
    fn test_format_defect_output_csv() {
        let predictions = create_test_predictions();
        let elapsed = Duration::from_millis(100);

        let result = format_defect_output(
            DefectPredictionOutputFormat::Csv,
            &predictions,
            elapsed,
            false,
        );

        assert!(result.is_ok());
        let content = result.unwrap();
        assert!(content.contains("file,probability,confidence,risk_level"));
        assert!(content.contains("high_risk.rs"));
    }

    // ==================== Test format_defect_summary ====================

    #[test]
    fn test_format_defect_summary_with_predictions() {
        let predictions = create_test_predictions();
        let elapsed = Duration::from_millis(250);

        let result = format_defect_summary(&predictions, elapsed).unwrap();

        assert!(result.contains("Defect Prediction Summary"));
        assert!(result.contains("Risk Distribution"));
        assert!(result.contains("Top Risk Files"));
        assert!(result.contains("Analysis time"));
    }

    #[test]
    fn test_format_defect_summary_empty_predictions() {
        let predictions: Vec<(String, DefectScore)> = vec![];
        let elapsed = Duration::from_millis(50);

        let result = format_defect_summary(&predictions, elapsed).unwrap();

        assert!(result.contains("Defect Prediction Summary"));
        assert!(result.contains("0 files")); // Risk distribution shows 0
    }

    // ==================== Test write_summary_header ====================

    #[test]
    fn test_write_summary_header() {
        let mut output = String::new();
        write_summary_header(&mut output).unwrap();

        assert!(output.contains("Defect Prediction Summary"));
        assert!(output.contains("==="));
    }

    // ==================== Test write_risk_distribution ====================

    #[test]
    fn test_write_risk_distribution() {
        let predictions = create_test_predictions();
        let mut output = String::new();

        write_risk_distribution(&mut output, &predictions).unwrap();

        assert!(output.contains("Risk Distribution"));
        assert!(output.contains("High risk"));
        assert!(output.contains("Medium risk"));
        assert!(output.contains("Low risk"));
    }

    // ==================== Test write_top_risk_files ====================

    #[test]
    fn test_write_top_risk_files_with_data() {
        let predictions = create_test_predictions();
        let mut output = String::new();

        write_top_risk_files(&mut output, &predictions).unwrap();

        assert!(output.contains("Top Risk Files"));
        assert!(output.contains("src/high_risk.rs"));
    }

    #[test]
    fn test_write_top_risk_files_empty() {
        let predictions: Vec<(String, DefectScore)> = vec![];
        let mut output = String::new();

        write_top_risk_files(&mut output, &predictions).unwrap();

        // Should not write anything for empty predictions
        assert!(!output.contains("Top Risk Files"));
    }

    #[test]
    fn test_write_top_risk_files_more_than_10() {
        // Create more than 10 predictions
        let predictions: Vec<_> = (0..15)
            .map(|i| (format!("file{i}.rs"), create_high_risk_score()))
            .collect();

        let mut output = String::new();
        write_top_risk_files(&mut output, &predictions).unwrap();

        // Should only show 10 files
        let file_count = output.matches("file").count();
        assert_eq!(file_count, 10);
    }

    // ==================== Test write_summary_footer ====================

    #[test]
    fn test_write_summary_footer() {
        let elapsed = Duration::from_millis(1234);
        let mut output = String::new();

        write_summary_footer(&mut output, elapsed).unwrap();

        assert!(output.contains("Analysis time"));
    }

    // ==================== Test format_defect_json ====================

    #[test]
    fn test_format_defect_json_structure() {
        let predictions = create_test_predictions();
        let elapsed = Duration::from_millis(500);

        let result = format_defect_json(&predictions, elapsed).unwrap();
        let parsed: serde_json::Value = serde_json::from_str(&result).unwrap();

        // Check structure
        assert_eq!(parsed["analysis_type"], "defect_prediction");
        assert!(parsed["summary"]["total_files_analyzed"].as_u64().is_some());
        assert!(parsed["summary"]["high_risk_files"].as_u64().is_some());
        assert!(parsed["summary"]["medium_risk_files"].as_u64().is_some());
        assert!(parsed["summary"]["low_risk_files"].as_u64().is_some());
        assert!(parsed["summary"]["analysis_time_ms"].as_u64().is_some());

        // Check predictions array
        let preds = parsed["predictions"].as_array().unwrap();
        assert_eq!(preds.len(), 3);

        // Check individual prediction structure
        let first = &preds[0];
        assert!(first["file"].is_string());
        assert!(first["probability"].is_f64());
        assert!(first["confidence"].is_f64());
        assert!(first["risk_level"].is_string());
    }

    #[test]
    fn test_format_defect_json_empty() {
        let predictions: Vec<(String, DefectScore)> = vec![];
        let elapsed = Duration::from_millis(10);

        let result = format_defect_json(&predictions, elapsed).unwrap();
        let parsed: serde_json::Value = serde_json::from_str(&result).unwrap();

        assert_eq!(parsed["summary"]["total_files_analyzed"], 0);
        assert!(parsed["predictions"].as_array().unwrap().is_empty());
    }

    // ==================== Test format_defect_detailed ====================

    #[test]
    fn test_format_defect_detailed_with_recommendations() {
        let predictions = create_test_predictions();
        let elapsed = Duration::from_millis(100);

        let result = format_defect_detailed(&predictions, elapsed, true).unwrap();

        assert!(result.contains("Detailed Report"));
        assert!(result.contains("File:"));
        assert!(result.contains("Risk Level:"));
        assert!(result.contains("Confidence:"));
        assert!(result.contains("Contributing Factors:"));
        assert!(result.contains("Recommendations:"));
    }

    #[test]
    fn test_format_defect_detailed_without_recommendations() {
        let predictions = create_test_predictions();
        let elapsed = Duration::from_millis(100);

        let result = format_defect_detailed(&predictions, elapsed, false).unwrap();

        assert!(result.contains("Detailed Report"));
        assert!(!result.contains("Recommendations:"));
    }

    // ==================== Test write_detailed_header ====================

    #[test]
    fn test_write_detailed_header() {
        let mut output = String::new();
        write_detailed_header(&mut output).unwrap();

        assert!(output.contains("Detailed Report"));
        assert!(output.contains("==="));
    }

    // ==================== Test write_file_details ====================

    #[test]
    fn test_write_file_details_with_recommendations() {
        let score = create_high_risk_score();
        let mut output = String::new();

        write_file_details(&mut output, "test.rs", &score, true).unwrap();

        assert!(output.contains("test.rs"));
        assert!(output.contains("Risk Level:"));
        assert!(output.contains("Confidence:"));
        assert!(output.contains("Recommendations:"));
    }

    #[test]
    fn test_write_file_details_without_recommendations() {
        let score = create_high_risk_score();
        let mut output = String::new();

        write_file_details(&mut output, "test.rs", &score, false).unwrap();

        assert!(output.contains("test.rs"));
        assert!(!output.contains("Recommendations:"));
    }

    // ==================== Test write_risk_level ====================

    #[test]
    fn test_write_risk_level() {
        let score = create_high_risk_score();
        let mut output = String::new();

        write_risk_level(&mut output, &score).unwrap();

        assert!(output.contains("Risk Level:"));
        assert!(output.contains("HIGH"));
        assert!(output.contains("85.0%"));
    }

    // ==================== Test write_confidence_level ====================

    #[test]
    fn test_write_confidence_level() {
        let score = create_high_risk_score();
        let mut output = String::new();

        write_confidence_level(&mut output, &score).unwrap();

        assert!(output.contains("Confidence:"));
        assert!(output.contains("90.0%"));
    }

    // ==================== Test write_contributing_factors ====================

    #[test]
    fn test_write_contributing_factors() {
        let score = create_high_risk_score();
        let mut output = String::new();

        write_contributing_factors(&mut output, &score).unwrap();

        assert!(output.contains("Contributing Factors:"));
        assert!(output.contains("complexity"));
        assert!(output.contains("churn"));
    }

    #[test]
    fn test_write_contributing_factors_empty() {
        let mut score = create_high_risk_score();
        score.contributing_factors = vec![];
        let mut output = String::new();

        write_contributing_factors(&mut output, &score).unwrap();

        // Should not write anything for empty factors
        assert!(output.is_empty());
    }

    // ==================== Test write_recommendations ====================

    #[test]
    fn test_write_recommendations() {
        let score = create_high_risk_score();
        let mut output = String::new();

        write_recommendations(&mut output, &score).unwrap();

        assert!(output.contains("Recommendations:"));
        assert!(output.contains("refactoring"));
    }

    #[test]
    fn test_write_recommendations_empty() {
        let mut score = create_high_risk_score();
        score.recommendations = vec![];
        let mut output = String::new();

        write_recommendations(&mut output, &score).unwrap();

        // Should not write anything for empty recommendations
        assert!(output.is_empty());
    }

    // ==================== Test write_analysis_footer ====================

    #[test]
    fn test_write_analysis_footer() {
        let elapsed = Duration::from_secs(2);
        let mut output = String::new();

        write_analysis_footer(&mut output, elapsed).unwrap();

        assert!(output.contains("Analysis time:"));
    }

    // ==================== Test format_defect_sarif ====================

    #[test]
    fn test_format_defect_sarif_structure() {
        let predictions = create_test_predictions();

        let result = format_defect_sarif(&predictions).unwrap();
        let parsed: serde_json::Value = serde_json::from_str(&result).unwrap();

        // Check SARIF schema
        assert_eq!(parsed["version"], "2.1.0");
        assert!(parsed["$schema"].as_str().unwrap().contains("sarif-schema"));

        // Check runs
        let runs = parsed["runs"].as_array().unwrap();
        assert_eq!(runs.len(), 1);

        // Check tool info
        let tool = &runs[0]["tool"]["driver"];
        assert_eq!(tool["name"], "pmat-defect-prediction");
        assert!(tool["version"].is_string());

        // Check results
        let results = runs[0]["results"].as_array().unwrap();
        assert_eq!(results.len(), 3);
    }

    #[test]
    fn test_format_defect_sarif_risk_levels() {
        let predictions = create_test_predictions();

        let result = format_defect_sarif(&predictions).unwrap();
        let parsed: serde_json::Value = serde_json::from_str(&result).unwrap();

        let results = parsed["runs"][0]["results"].as_array().unwrap();

        // High risk should be "error"
        let high_risk = results
            .iter()
            .find(|r| {
                r["locations"][0]["physicalLocation"]["artifactLocation"]["uri"]
                    .as_str()
                    .unwrap()
                    .contains("high_risk")
            })
            .unwrap();
        assert_eq!(high_risk["level"], "error");

        // Medium risk should be "warning"
        let medium_risk = results
            .iter()
            .find(|r| {
                r["locations"][0]["physicalLocation"]["artifactLocation"]["uri"]
                    .as_str()
                    .unwrap()
                    .contains("medium_risk")
            })
            .unwrap();
        assert_eq!(medium_risk["level"], "warning");

        // Low risk should be "note"
        let low_risk = results
            .iter()
            .find(|r| {
                r["locations"][0]["physicalLocation"]["artifactLocation"]["uri"]
                    .as_str()
                    .unwrap()
                    .contains("low_risk")
            })
            .unwrap();
        assert_eq!(low_risk["level"], "note");
    }

    #[test]
    fn test_format_defect_sarif_empty() {
        let predictions: Vec<(String, DefectScore)> = vec![];

        let result = format_defect_sarif(&predictions).unwrap();
        let parsed: serde_json::Value = serde_json::from_str(&result).unwrap();

        let results = parsed["runs"][0]["results"].as_array().unwrap();
        assert!(results.is_empty());
    }

    // ==================== Test format_defect_csv ====================

    #[test]
    fn test_format_defect_csv_header() {
        let predictions = create_test_predictions();

        let result = format_defect_csv(&predictions).unwrap();

        let lines: Vec<&str> = result.lines().collect();
        assert_eq!(
            lines[0],
            "file,probability,confidence,risk_level,top_factor,top_factor_weight"
        );
    }

    #[test]
    fn test_format_defect_csv_data_rows() {
        let predictions = create_test_predictions();

        let result = format_defect_csv(&predictions).unwrap();

        let lines: Vec<&str> = result.lines().collect();
        assert_eq!(lines.len(), 4); // 1 header + 3 data rows

        // Check first data row
        assert!(lines[1].contains("high_risk.rs"));
        assert!(lines[1].contains("0.850"));
    }

    #[test]
    fn test_format_defect_csv_empty_factors() {
        let mut score = create_high_risk_score();
        score.contributing_factors = vec![];
        let predictions = vec![("test.rs".to_string(), score)];

        let result = format_defect_csv(&predictions).unwrap();

        // Should handle missing top factor gracefully
        assert!(result.contains("test.rs"));
        assert!(result.contains("0.000")); // Default weight
    }

    #[test]
    fn test_format_defect_csv_empty_predictions() {
        let predictions: Vec<(String, DefectScore)> = vec![];

        let result = format_defect_csv(&predictions).unwrap();

        let lines: Vec<&str> = result.lines().collect();
        assert_eq!(lines.len(), 1); // Only header
    }

    // ==================== Test RiskStatistics struct ====================

    #[test]
    fn test_risk_statistics_struct_construction() {
        let stats = RiskStatistics {
            high_risk: 5,
            medium_risk: 10,
            low_risk: 15,
        };

        assert_eq!(stats.high_risk, 5);
        assert_eq!(stats.medium_risk, 10);
        assert_eq!(stats.low_risk, 15);
    }

    // ==================== Edge case tests ====================

    #[test]
    fn test_format_defect_summary_single_file() {
        let predictions = vec![("only_file.rs".to_string(), create_high_risk_score())];
        let elapsed = Duration::from_millis(50);

        let result = format_defect_summary(&predictions, elapsed).unwrap();

        assert!(result.contains("1 files"));
        assert!(result.contains("only_file.rs"));
    }

    #[test]
    fn test_format_with_special_characters_in_filename() {
        let predictions = vec![("src/my-file_v2.0.rs".to_string(), create_high_risk_score())];
        let elapsed = Duration::from_millis(50);

        let result = format_defect_summary(&predictions, elapsed).unwrap();
        assert!(result.contains("my-file_v2.0.rs"));

        let csv = format_defect_csv(&predictions).unwrap();
        assert!(csv.contains("my-file_v2.0.rs"));
    }

    #[test]
    fn test_format_with_unicode_filename() {
        let predictions = vec![(
            "src/archivo_espa\u{00f1}ol.rs".to_string(),
            create_medium_risk_score(),
        )];
        let elapsed = Duration::from_millis(50);

        let result = format_defect_json(&predictions, elapsed).unwrap();
        assert!(result.contains("archivo_espa\u{00f1}ol.rs"));
    }

    #[test]
    fn test_format_with_zero_duration() {
        let predictions = create_test_predictions();
        let elapsed = Duration::from_millis(0);

        let result = format_defect_summary(&predictions, elapsed).unwrap();
        assert!(result.contains("Analysis time"));

        let json = format_defect_json(&predictions, elapsed).unwrap();
        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
        assert_eq!(parsed["summary"]["analysis_time_ms"], 0);
    }

    #[test]
    fn test_format_with_very_long_duration() {
        let predictions = create_test_predictions();
        let elapsed = Duration::from_secs(3600); // 1 hour

        let result = format_defect_summary(&predictions, elapsed).unwrap();
        assert!(result.contains("Analysis time"));
    }

    // ==================== Probability boundary tests ====================

    #[test]
    fn test_probability_exactly_zero() {
        let score = create_mock_defect_score(0.0, 0.9, RiskLevel::Low);
        let predictions = vec![("zero.rs".to_string(), score)];

        let stats = calculate_risk_statistics(&predictions);
        assert_eq!(stats.low_risk, 1);
        assert_eq!(stats.medium_risk, 0);
        assert_eq!(stats.high_risk, 0);
    }

    #[test]
    fn test_probability_exactly_one() {
        let score = create_mock_defect_score(1.0, 0.9, RiskLevel::High);
        let predictions = vec![("max.rs".to_string(), score)];

        let stats = calculate_risk_statistics(&predictions);
        assert_eq!(stats.high_risk, 1);
        assert_eq!(stats.medium_risk, 0);
        assert_eq!(stats.low_risk, 0);
    }

    // ==================== Additional format_defect_output tests ====================

    #[test]
    fn test_format_defect_output_all_formats_empty_predictions() {
        let predictions: Vec<(String, DefectScore)> = vec![];
        let elapsed = Duration::from_millis(50);

        // Test all formats with empty predictions
        assert!(format_defect_output(
            DefectPredictionOutputFormat::Summary,
            &predictions,
            elapsed,
            false
        )
        .is_ok());
        assert!(format_defect_output(
            DefectPredictionOutputFormat::Json,
            &predictions,
            elapsed,
            false
        )
        .is_ok());
        assert!(format_defect_output(
            DefectPredictionOutputFormat::Detailed,
            &predictions,
            elapsed,
            true
        )
        .is_ok());
        assert!(format_defect_output(
            DefectPredictionOutputFormat::Sarif,
            &predictions,
            elapsed,
            false
        )
        .is_ok());
        assert!(format_defect_output(
            DefectPredictionOutputFormat::Csv,
            &predictions,
            elapsed,
            false
        )
        .is_ok());
    }

    // ==================== Detailed format tests ====================

    #[test]
    fn test_format_defect_detailed_empty() {
        let predictions: Vec<(String, DefectScore)> = vec![];
        let elapsed = Duration::from_millis(100);

        let result = format_defect_detailed(&predictions, elapsed, true).unwrap();

        assert!(result.contains("Detailed Report"));
        assert!(result.contains("Analysis time"));
    }

    #[test]
    fn test_format_defect_detailed_many_files() {
        let predictions: Vec<_> = (0..50)
            .map(|i| (format!("file{i}.rs"), create_medium_risk_score()))
            .collect();
        let elapsed = Duration::from_millis(500);

        let result = format_defect_detailed(&predictions, elapsed, false).unwrap();

        // Verify all files are included
        assert!(result.contains("file0.rs"));
        assert!(result.contains("file49.rs"));
    }

    // ==================== JSON format verification ====================

    #[test]
    fn test_format_defect_json_contributing_factors() {
        let predictions = create_test_predictions();
        let elapsed = Duration::from_millis(100);

        let result = format_defect_json(&predictions, elapsed).unwrap();
        let parsed: serde_json::Value = serde_json::from_str(&result).unwrap();

        let pred = &parsed["predictions"][0];
        let factors = pred["contributing_factors"].as_array().unwrap();

        // Should have 4 factors
        assert_eq!(factors.len(), 4);
    }

    #[test]
    fn test_format_defect_json_recommendations() {
        let predictions = create_test_predictions();
        let elapsed = Duration::from_millis(100);

        let result = format_defect_json(&predictions, elapsed).unwrap();
        let parsed: serde_json::Value = serde_json::from_str(&result).unwrap();

        let pred = &parsed["predictions"][0];
        let recs = pred["recommendations"].as_array().unwrap();

        // Should have 2 recommendations
        assert_eq!(recs.len(), 2);
    }

    // ==================== SARIF format verification ====================

    #[test]
    fn test_format_defect_sarif_properties() {
        let predictions = create_test_predictions();

        let result = format_defect_sarif(&predictions).unwrap();
        let parsed: serde_json::Value = serde_json::from_str(&result).unwrap();

        let result_item = &parsed["runs"][0]["results"][0];
        let props = &result_item["properties"];

        assert!(props["probability"].is_f64());
        assert!(props["confidence"].is_f64());
        assert!(props["contributing_factors"].is_array());
        assert!(props["recommendations"].is_array());
    }

    #[test]
    fn test_format_defect_sarif_locations() {
        let predictions = create_test_predictions();

        let result = format_defect_sarif(&predictions).unwrap();
        let parsed: serde_json::Value = serde_json::from_str(&result).unwrap();

        let result_item = &parsed["runs"][0]["results"][0];
        let location = &result_item["locations"][0];

        assert!(location["physicalLocation"]["artifactLocation"]["uri"].is_string());
        assert_eq!(
            location["physicalLocation"]["artifactLocation"]["uriBaseId"],
            "%SRCROOT%"
        );
    }

    // ==================== CSV format edge cases ====================

    #[test]
    fn test_format_defect_csv_with_comma_in_factor() {
        let mut score = create_high_risk_score();
        score.contributing_factors = vec![("factor,with,commas".to_string(), 0.5)];
        let predictions = vec![("test.rs".to_string(), score)];

        let result = format_defect_csv(&predictions).unwrap();

        // CSV should still be parseable
        let lines: Vec<&str> = result.lines().collect();
        assert_eq!(lines.len(), 2);
    }

    // ==================== Risk level calculation tests ====================

    #[test]
    fn test_risk_statistics_with_various_probabilities() {
        let predictions = vec![
            (
                "p_0.rs".to_string(),
                create_mock_defect_score(0.0, 0.9, RiskLevel::Low),
            ),
            (
                "p_15.rs".to_string(),
                create_mock_defect_score(0.15, 0.9, RiskLevel::Low),
            ),
            (
                "p_30.rs".to_string(),
                create_mock_defect_score(0.30, 0.9, RiskLevel::Low),
            ), // Exactly 0.3 -> Low
            (
                "p_35.rs".to_string(),
                create_mock_defect_score(0.35, 0.9, RiskLevel::Medium),
            ),
            (
                "p_50.rs".to_string(),
                create_mock_defect_score(0.50, 0.9, RiskLevel::Medium),
            ),
            (
                "p_70.rs".to_string(),
                create_mock_defect_score(0.70, 0.9, RiskLevel::Medium),
            ), // Exactly 0.7 -> Medium
            (
                "p_75.rs".to_string(),
                create_mock_defect_score(0.75, 0.9, RiskLevel::High),
            ),
            (
                "p_100.rs".to_string(),
                create_mock_defect_score(1.0, 0.9, RiskLevel::High),
            ),
        ];

        let stats = calculate_risk_statistics(&predictions);

        // High risk: > 0.7 (0.75, 1.0) = 2
        assert_eq!(stats.high_risk, 2);
        // Medium risk: > 0.3 and <= 0.7 (0.35, 0.50, 0.70) = 3
        assert_eq!(stats.medium_risk, 3);
        // Low risk: <= 0.3 (0.0, 0.15, 0.30) = 3
        assert_eq!(stats.low_risk, 3);
    }
}

/// NOTE: Temporarily disabled due to JsonValue move semantics in prop_assert_eq
#[cfg(all(test, feature = "broken-tests"))]
mod property_tests {
    use super::*;
    use crate::services::defect_probability::RiskLevel;
    use proptest::prelude::*;
    use std::time::Duration;

    // Strategy for generating valid probabilities (0.0 to 1.0)
    fn probability_strategy() -> impl Strategy<Value = f32> {
        (0u32..=1000).prop_map(|x| x as f32 / 1000.0)
    }

    // Strategy for generating valid confidence values (0.0 to 1.0)
    fn confidence_strategy() -> impl Strategy<Value = f32> {
        (0u32..=1000).prop_map(|x| x as f32 / 1000.0)
    }

    // Strategy for generating DefectScore
    fn defect_score_strategy() -> impl Strategy<Value = DefectScore> {
        (probability_strategy(), confidence_strategy()).prop_map(|(prob, conf)| {
            let risk_level = if prob > 0.7 {
                RiskLevel::High
            } else if prob > 0.3 {
                RiskLevel::Medium
            } else {
                RiskLevel::Low
            };

            DefectScore {
                probability: prob,
                confidence: conf,
                risk_level,
                contributing_factors: vec![
                    ("complexity".to_string(), prob * 0.3),
                    ("churn".to_string(), prob * 0.35),
                ],
                recommendations: vec!["Test recommendation".to_string()],
            }
        })
    }

    // Strategy for generating predictions
    fn predictions_strategy() -> impl Strategy<Value = Vec<(String, DefectScore)>> {
        prop::collection::vec(
            ("[a-z][a-z0-9_]{0,20}\\.rs", defect_score_strategy()),
            0..20,
        )
    }

    proptest! {
        #![proptest_config(ProptestConfig::with_cases(100))]

        #[test]
        fn prop_risk_statistics_sum_equals_total(predictions in predictions_strategy()) {
            let stats = calculate_risk_statistics(&predictions);
            let total = stats.high_risk + stats.medium_risk + stats.low_risk;
            prop_assert_eq!(total, predictions.len());
        }

        #[test]
        fn prop_filter_high_risk_only_reduces_count(predictions in predictions_strategy()) {
            let original_len = predictions.len();
            let filtered = filter_and_sort_predictions(
                predictions,
                true,  // high_risk_only
                true,  // include_low_confidence
                0.0,   // confidence_threshold
                0,     // top_files (unlimited)
            );
            prop_assert!(filtered.len() <= original_len);
        }

        #[test]
        fn prop_filtered_results_are_sorted(predictions in predictions_strategy()) {
            let filtered = filter_and_sort_predictions(
                predictions,
                false, // high_risk_only
                true,  // include_low_confidence
                0.0,   // confidence_threshold
                0,     // top_files
            );

            // Verify sorted descending by probability
            for i in 1..filtered.len() {
                prop_assert!(filtered[i-1].1.probability >= filtered[i].1.probability);
            }
        }

        #[test]
        fn prop_top_files_limit_respected(
            predictions in predictions_strategy(),
            limit in 1usize..10
        ) {
            let filtered = filter_and_sort_predictions(
                predictions.clone(),
                false,
                true,
                0.0,
                limit,
            );

            prop_assert!(filtered.len() <= limit);
            prop_assert!(filtered.len() <= predictions.len());
        }

        #[test]
        fn prop_json_output_is_valid(predictions in predictions_strategy()) {
            let elapsed = Duration::from_millis(100);
            let result = format_defect_json(&predictions, elapsed);
            prop_assert!(result.is_ok());

            // Verify it's parseable JSON
            let json_str = result.unwrap();
            let parsed: Result<serde_json::Value, _> = serde_json::from_str(&json_str);
            prop_assert!(parsed.is_ok());
        }

        #[test]
        fn prop_csv_has_correct_line_count(predictions in predictions_strategy()) {
            let result = format_defect_csv(&predictions);
            prop_assert!(result.is_ok());

            let csv = result.unwrap();
            let line_count = csv.lines().count();
            // 1 header + N data rows
            prop_assert_eq!(line_count, predictions.len() + 1);
        }

        #[test]
        fn prop_sarif_output_is_valid(predictions in predictions_strategy()) {
            let result = format_defect_sarif(&predictions);
            prop_assert!(result.is_ok());

            let sarif_str = result.unwrap();
            let parsed: Result<serde_json::Value, _> = serde_json::from_str(&sarif_str);
            prop_assert!(parsed.is_ok());

            let sarif = parsed.unwrap();
            prop_assert_eq!(sarif["version"], "2.1.0");
        }

        #[test]
        fn prop_summary_output_never_fails(predictions in predictions_strategy()) {
            let elapsed = Duration::from_millis(100);
            let result = format_defect_summary(&predictions, elapsed);
            prop_assert!(result.is_ok());
        }

        #[test]
        fn prop_detailed_output_never_fails(predictions in predictions_strategy()) {
            let elapsed = Duration::from_millis(100);
            let result = format_defect_detailed(&predictions, elapsed, true);
            prop_assert!(result.is_ok());

            let result_no_rec = format_defect_detailed(&predictions, elapsed, false);
            prop_assert!(result_no_rec.is_ok());
        }

        #[test]
        fn prop_confidence_threshold_filters_correctly(
            predictions in predictions_strategy(),
            threshold in probability_strategy()
        ) {
            let filtered = filter_and_sort_predictions(
                predictions.clone(),
                false,
                false, // Do NOT include low confidence
                threshold,
                0,
            );

            // All remaining predictions should have confidence >= threshold
            for (_, score) in &filtered {
                prop_assert!(score.confidence > threshold);
            }
        }

        #[test]
        fn prop_high_risk_filter_only_high(predictions in predictions_strategy()) {
            let filtered = filter_and_sort_predictions(
                predictions,
                true, // high_risk_only
                true,
                0.0,
                0,
            );

            // All remaining should have probability > 0.7
            for (_, score) in &filtered {
                prop_assert!(score.probability > 0.7);
            }
        }

        #[test]
        fn prop_risk_icon_always_returns_valid(prob in probability_strategy()) {
            let risk_level = if prob > 0.7 {
                RiskLevel::High
            } else if prob > 0.3 {
                RiskLevel::Medium
            } else {
                RiskLevel::Low
            };

            let icon = get_risk_icon(&risk_level);
            prop_assert!(["🔴", "🟡", "🟢"].contains(&icon));
        }

        #[test]
        fn prop_config_creation_preserves_values(
            threshold in probability_strategy(),
            min_lines in 1usize..1000,
            include_low_conf in proptest::bool::ANY,
            high_risk in proptest::bool::ANY,
            include_rec in proptest::bool::ANY
        ) {
            let config = create_defect_prediction_config(
                threshold,
                min_lines,
                include_low_conf,
                high_risk,
                include_rec,
                None,
                None,
            );

            prop_assert_eq!(config.confidence_threshold, threshold);
            prop_assert_eq!(config.min_lines, min_lines);
            prop_assert_eq!(config.include_low_confidence, include_low_conf);
            prop_assert_eq!(config.high_risk_only, high_risk);
            prop_assert_eq!(config.include_recommendations, include_rec);
        }
    }

    proptest! {
        #[test]
        fn basic_property_stability(_input in ".*") {
            // Basic property test for coverage
            prop_assert!(true);
        }

        #[test]
        fn module_consistency_check(_x in 0u32..1000) {
            // Module consistency verification
            prop_assert!(_x < 1001);
        }
    }
}
