use super::*;
use crate::config::DataFlowScoringConfig;
use crate::core::FunctionMetrics;
use crate::priority::call_graph::CallGraph;
use crate::risk::lcov::{FunctionCoverage, LcovData};
use std::path::PathBuf;

fn create_test_metrics() -> FunctionMetrics {
    FunctionMetrics {
        file: PathBuf::from("test.rs"),
        name: "test_function".to_string(),
        line: 10,
        length: 50,
        cyclomatic: 5,
        cognitive: 8,
        nesting: 0,
        is_test: false,
        visibility: None,
        is_trait_method: false,
        in_test_module: false,
        entropy_score: None,
        is_pure: None,
        purity_confidence: None,
        purity_reason: None,
        call_dependencies: None,
        detected_patterns: None,
        upstream_callers: None,
        downstream_callees: None,
        mapping_pattern_result: None,
        adjusted_complexity: None,
        composition_metrics: None,
        language_specific: None,
        purity_level: None,
        error_swallowing_count: None,
        error_swallowing_patterns: None,
        entropy_analysis: None,
    }
}

#[test]
fn test_unified_scoring() {
    let func = create_test_metrics();
    let graph = CallGraph::new();
    let score = calculate_unified_priority(&func, &graph, None, None);

    assert!(score.complexity_factor > 0.0);
    assert!(score.coverage_factor > 0.0);
    assert!(score.final_score > 0.0);
    assert!(score.final_score <= 100.0); // Score is on 0-100 scale
}

fn create_simple_io_wrapper() -> FunctionMetrics {
    let mut func = create_test_metrics();
    func.name = "extract_module_from_import".to_string();
    func.cyclomatic = 1;
    func.cognitive = 1;
    func.length = 3;
    func.nesting = 1;
    func
}

fn create_full_coverage_data(func: &FunctionMetrics) -> LcovData {
    let mut lcov = LcovData::default();
    lcov.functions.insert(
        func.file.clone(),
        vec![FunctionCoverage {
            name: func.name.clone(),
            start_line: func.line,
            execution_count: 18,
            coverage_percentage: 100.0,
            uncovered_lines: vec![],
            normalized: crate::risk::lcov::NormalizedFunctionName::simple(&func.name),
        }],
    );
    lcov.build_index(); // Rebuild index after modifying functions
    lcov
}

fn assert_zero_debt_score(score: &UnifiedScore) {
    assert_eq!(score.final_score, 0.0);
    assert_eq!(score.complexity_factor, 0.0);
    assert_eq!(score.coverage_factor, 0.0);
}

#[test]
fn test_simple_io_wrapper_with_coverage_zero_score() {
    // Create a simple I/O wrapper function with test coverage
    let func = create_simple_io_wrapper();
    let call_graph = CallGraph::new();
    let lcov = create_full_coverage_data(&func);

    let score = calculate_unified_priority(&func, &call_graph, Some(&lcov), None);

    // Tested simple I/O wrapper should have zero score (not technical debt)
    assert_zero_debt_score(&score);
}

#[test]
fn test_simple_io_wrapper_without_coverage_has_score() {
    // Create a simple I/O wrapper function without test coverage
    let mut func = create_test_metrics();
    func.name = "print_risk_function".to_string();
    func.cyclomatic = 1;
    func.cognitive = 0;
    func.length = 4;
    func.nesting = 1;

    let call_graph = CallGraph::new();

    // Calculate priority score without coverage (assume untested)
    let score = calculate_unified_priority(&func, &call_graph, None, None);

    // Untested simple I/O wrapper should have a non-zero score (testing gap)
    assert!(
        score.final_score > 0.0,
        "Untested I/O wrapper should have non-zero score"
    );
}

fn assert_zero_coverage_boost(score: &UnifiedScore) {
    // Spec 122: With multiplier approach, 0% coverage (multiplier=1.0) keeps full base score
    // No longer applying 10x boost, but function still gets full complexity+dependency score
    assert!(
        score.final_score > 0.0,
        "Zero coverage functions should have non-zero score, got {}",
        score.final_score
    );
}

#[test]
fn test_zero_coverage_prioritization() {
    // Test spec 122: Functions with 0% coverage get full base score (multiplier=1.0)
    let func = create_test_function_for_coverage();
    let call_graph = CallGraph::new();

    let lcov = create_coverage_function(&func, 0, 0.0);
    let score = calculate_unified_priority(&func, &call_graph, Some(&lcov), None);

    assert_zero_coverage_boost(&score);
}

fn create_coverage_function(
    func: &FunctionMetrics,
    execution_count: u64,
    coverage_percentage: f64,
) -> LcovData {
    let mut lcov = LcovData::default();
    lcov.functions.insert(
        func.file.clone(),
        vec![FunctionCoverage {
            name: func.name.clone(),
            start_line: func.line,
            execution_count,
            coverage_percentage,
            uncovered_lines: vec![],
            normalized: crate::risk::lcov::NormalizedFunctionName::simple(&func.name),
        }],
    );
    lcov.build_index(); // Rebuild index after modifying functions
    lcov
}

fn create_test_function_for_coverage() -> FunctionMetrics {
    let mut func = create_test_metrics();
    func.cyclomatic = 5;
    func.cognitive = 8;
    func.is_test = false;
    func
}

fn assert_low_coverage_boost(score_low: &UnifiedScore, score_mid: &UnifiedScore) {
    // Spec 122: With multiplier approach, lower coverage → higher score (monotonicity)
    // 10% coverage (multiplier=0.9) should score higher than 50% coverage (multiplier=0.5)
    assert!(
        score_low.final_score > score_mid.final_score,
        "10% coverage ({}) should score higher than 50% coverage ({})",
        score_low.final_score,
        score_mid.final_score
    );
}

#[test]
fn test_low_coverage_prioritization() {
    // Test spec 122: Functions with lower coverage score higher (monotonicity)
    let func = create_test_function_for_coverage();
    let call_graph = CallGraph::new();

    let lcov_low = create_coverage_function(&func, 1, 10.0);
    let lcov_mid = create_coverage_function(&func, 5, 50.0);

    let score_low = calculate_unified_priority(&func, &call_graph, Some(&lcov_low), None);
    let score_mid = calculate_unified_priority(&func, &call_graph, Some(&lcov_mid), None);

    assert_low_coverage_boost(&score_low, &score_mid);
}

#[test]
fn test_test_code_not_boosted() {
    // Test spec 98: Test code should not get zero coverage boost
    let mut func = create_test_metrics();
    func.cyclomatic = 5;
    func.cognitive = 8;
    func.is_test = true; // Mark as test code
    func.name = "test_something".to_string();

    let call_graph = CallGraph::new();

    // No coverage data (worst case for non-test code)
    let score = calculate_unified_priority(&func, &call_graph, None, None);

    // Test code with no coverage should still have moderate score (not boosted)
    // With simplified formula: (5*0.4 + 8*0.6)/2 = 3.4 complexity_factor
    // But test code gets 100% coverage assumed, so low final score
    assert!(
        score.final_score < 50.0,
        "Test code should not get zero coverage boost, got {}",
        score.final_score
    );
}

#[test]
fn test_complex_function_has_score() {
    // Create a complex function that should have a non-zero score
    let mut func = create_test_metrics();
    func.name = "complex_logic".to_string();
    func.cyclomatic = 8;
    func.cognitive = 12;
    func.length = 50;

    let call_graph = CallGraph::new();

    // Calculate priority score
    let score = calculate_unified_priority(&func, &call_graph, None, None);

    // Complex function should have non-zero score (is technical debt)
    assert!(score.final_score > 0.0);
    assert!(score.complexity_factor > 0.0);
}

#[test]
fn test_complexity_factor_stores_calculated_factor_not_raw_complexity() {
    // Test: UnifiedScore.complexity_factor should store the result of
    // calculate_complexity_factor with weighted complexity scoring
    let mut func = create_test_metrics();
    func.cyclomatic = 5;
    func.cognitive = 15;
    // With simplified formula (default 0.4 cyclo, 0.6 cognitive):
    // raw_complexity = 5 * 0.4 + 15 * 0.6 = 2 + 9 = 11
    // complexity_factor = calculate_complexity_factor(11) = 11 / 2.0 = 5.5

    let call_graph = CallGraph::new();
    let score = calculate_unified_priority(&func, &call_graph, None, None);

    // The complexity_factor field should store the calculated factor with cognitive weighting
    assert!(
        (score.complexity_factor - 5.5).abs() < 0.1,
        "complexity_factor should be ~5.5 with cognitive weighting, got {}",
        score.complexity_factor
    );
}

#[test]
fn test_well_tested_simple_function_scores_below_20() {
    // Test spec 109: Well-tested simple functions (100% coverage, cyclomatic < 10)
    // should score below 20.0 (spec example shows ~16.25)
    let mut func = create_test_metrics();
    func.cyclomatic = 5;
    func.cognitive = 15;
    func.is_test = false;

    let call_graph = CallGraph::new();
    let lcov = create_full_coverage_data(&func);

    let score = calculate_unified_priority(&func, &call_graph, Some(&lcov), None);

    assert!(
        score.final_score < 20.0,
        "Well-tested simple function (100% coverage, cyclomatic=5) should score < 20.0, got {}",
        score.final_score
    );
}

// Add more tests as needed...

// Tests for spec 110: Role-based coverage weight multiplier

fn create_entry_point_function(cyclomatic: u32, cognitive: u32) -> FunctionMetrics {
    let mut func = create_test_metrics();
    func.name = "handle_analyze".to_string(); // Entry point name pattern
    func.cyclomatic = cyclomatic;
    func.cognitive = cognitive;
    func
}

fn create_pure_logic_function(cyclomatic: u32, cognitive: u32) -> FunctionMetrics {
    let mut func = create_test_metrics();
    func.name = "calculate_score".to_string(); // Pure logic name pattern
    func.cyclomatic = cyclomatic;
    func.cognitive = cognitive;
    func
}

fn create_zero_coverage_data(func: &FunctionMetrics) -> LcovData {
    let mut lcov = LcovData::default();
    lcov.functions.insert(
        func.file.clone(),
        vec![FunctionCoverage {
            name: func.name.clone(),
            start_line: func.line,
            execution_count: 0,
            coverage_percentage: 0.0,
            uncovered_lines: vec![func.line],
            normalized: crate::risk::lcov::NormalizedFunctionName::simple(&func.name),
        }],
    );
    lcov.build_index();
    lcov
}

#[test]
fn test_entry_point_coverage_adjustment() {
    // BUG-001 fix: Entry points have higher multiplier (1.3) than pure logic (0.7)
    // So entry points should score HIGHER than pure logic with same complexity
    let entry_point = create_entry_point_function(17, 17);
    let pure_logic = create_pure_logic_function(17, 17);

    let call_graph = CallGraph::new();
    let entry_lcov = create_zero_coverage_data(&entry_point);
    let logic_lcov = create_zero_coverage_data(&pure_logic);

    let entry_score =
        calculate_unified_priority(&entry_point, &call_graph, Some(&entry_lcov), None);
    let logic_score = calculate_unified_priority(&pure_logic, &call_graph, Some(&logic_lcov), None);

    // Entry point should score HIGHER due to 1.3 multiplier vs pure logic 0.7
    // (Entry points need integration tests, so higher priority)
    assert!(
        entry_score.final_score > logic_score.final_score,
        "Entry point (score: {}) should score higher than pure logic (score: {}) - entry points need testing",
        entry_score.final_score,
        logic_score.final_score
    );
}

#[test]
fn test_orchestrator_coverage_adjustment() {
    // Spec 110: Orchestrators should get 0.8x coverage weight multiplier
    let mut orchestrator = create_test_metrics();
    orchestrator.name = "orchestrate_analysis".to_string();
    orchestrator.cyclomatic = 12;
    orchestrator.cognitive = 15;

    let call_graph = CallGraph::new();
    let lcov = create_zero_coverage_data(&orchestrator);

    let score = calculate_unified_priority(&orchestrator, &call_graph, Some(&lcov), None);

    // Orchestrator with 0% coverage should have reduced penalty compared to normal functions
    // The score should be lower than a pure logic function with same complexity
    assert!(
        score.final_score > 0.0,
        "Orchestrator should still have a score, but reduced due to coverage adjustment"
    );
}

#[test]
fn test_entry_point_with_coverage_not_overly_penalized() {
    // Spec 110: Entry point with 50% coverage should not rank in critical tier
    let entry_point = create_entry_point_function(12, 12);
    let mut partial_lcov = LcovData::default();
    partial_lcov.functions.insert(
        entry_point.file.clone(),
        vec![FunctionCoverage {
            name: entry_point.name.clone(),
            start_line: entry_point.line,
            execution_count: 5,
            coverage_percentage: 50.0,
            uncovered_lines: vec![],
            normalized: crate::risk::lcov::NormalizedFunctionName::simple(&entry_point.name),
        }],
    );
    partial_lcov.build_index();

    let call_graph = CallGraph::new();
    let score = calculate_unified_priority(&entry_point, &call_graph, Some(&partial_lcov), None);

    // Should not rank in critical tier (< 20.0)
    assert!(
        score.final_score < 20.0,
        "Entry point with 50% coverage and moderate complexity should not be critical (score: {})",
        score.final_score
    );
}

#[test]
fn test_complex_entry_point_still_flagged() {
    // Spec 110: Complex entry points should still be flagged despite coverage adjustment
    // Spec 121: With cognitive-weighted scoring, high cognitive complexity is emphasized
    let complex_entry = create_entry_point_function(25, 30);

    let call_graph = CallGraph::new();
    let lcov = create_zero_coverage_data(&complex_entry);

    let score = calculate_unified_priority(&complex_entry, &call_graph, Some(&lcov), None);

    // Complex entry points with 0% coverage should be flagged
    // Score should be elevated due to complexity and role multiplier
    assert!(
        score.final_score > 4.0,
        "Complex entry point should still be flagged (score: {})",
        score.final_score
    );
}

#[test]
fn test_io_wrapper_role_multiplier_not_clamped() {
    // BUG-001 fix: IOWrapper role multiplier is now 1.2 (I/O is hard to test)
    // This is higher than PureLogic (0.7) because I/O functions need more test attention
    let mut io_wrapper = create_test_metrics();
    io_wrapper.name = "write_output".to_string();
    io_wrapper.cyclomatic = 16;
    io_wrapper.cognitive = 18;
    io_wrapper.length = 50;
    io_wrapper.nesting = 3;

    let call_graph = CallGraph::new();
    let lcov = create_zero_coverage_data(&io_wrapper);

    let score = calculate_unified_priority(&io_wrapper, &call_graph, Some(&lcov), None);

    // IOWrapper now has 1.2 multiplier (I/O is hard to test, needs attention)
    // This means IOWrapper scores HIGHER than pure logic, not lower
    assert!(
        score.final_score > 10.0,
        "IOWrapper should have elevated score due to I/O testing difficulty. Score: {}",
        score.final_score
    );

    // Verify role multiplier was applied (should be 1.2 for IOWrapper now)
    assert!(
        score.role_multiplier >= 1.0,
        "Role multiplier for IOWrapper should be >=1.0 (I/O is hard to test), got: {}",
        score.role_multiplier
    );
}

#[test]
fn test_entry_point_role_multiplier_not_clamped() {
    // BUG-001 fix: EntryPoint role multiplier is now 1.3 (entry points need integration tests)
    let entry_point = create_entry_point_function(15, 18);

    let call_graph = CallGraph::new();
    let lcov = create_zero_coverage_data(&entry_point);

    let score = calculate_unified_priority(&entry_point, &call_graph, Some(&lcov), None);

    // Verify role multiplier was applied (now 1.3, not 1.5)
    assert!(
        (score.role_multiplier - 1.3).abs() < 0.01,
        "Role multiplier should be 1.3 for EntryPoint, got: {}",
        score.role_multiplier
    );

    // EntryPoint with 1.3x multiplier and 0% coverage should score reasonably
    assert!(
        score.final_score > 2.0,
        "EntryPoint with 1.3x multiplier should have elevated score. Score: {}",
        score.final_score
    );
}

#[test]
fn test_io_wrapper_coverage_weight_reduced() {
    // BUG-001 fix: IOWrapper now has 1.2 multiplier (I/O is hard to test)
    // Coverage weight is still reduced (0.5x) but role multiplier is increased
    let mut io_wrapper = create_test_metrics();
    io_wrapper.name = "format_output".to_string();
    io_wrapper.file = PathBuf::from("src/io/formatter.rs");
    io_wrapper.cyclomatic = 12;
    io_wrapper.cognitive = 14;
    io_wrapper.length = 40;
    io_wrapper.nesting = 2;

    let call_graph = CallGraph::new();
    let lcov = create_zero_coverage_data(&io_wrapper);

    let score = calculate_unified_priority(&io_wrapper, &call_graph, Some(&lcov), None);

    // With 0% coverage, 0.5x coverage weight, and 1.2x role multiplier,
    // the score should be moderate - coverage weight reduction is offset by role multiplier
    assert!(
        score.final_score > 5.0,
        "IOWrapper should have a meaningful score. Score: {}",
        score.final_score
    );
}

#[test]
fn test_pure_logic_coverage_weight_unchanged() {
    // BUG-001 fix: PureLogic now has 0.7 multiplier (pure functions are easy to test)
    let pure_logic = create_pure_logic_function(12, 14);

    let call_graph = CallGraph::new();
    let lcov = create_zero_coverage_data(&pure_logic);

    let score = calculate_unified_priority(&pure_logic, &call_graph, Some(&lcov), None);

    // PureLogic with 0% coverage should score reasonably (but lower than I/O)
    assert!(
        score.final_score > 2.0,
        "PureLogic with 0% coverage should score reasonably. Score: {}",
        score.final_score
    );

    // Role multiplier for PureLogic is now 0.7 (pure functions are easy to test)
    assert!(
        score.role_multiplier < 1.0,
        "Role multiplier for PureLogic should be < 1.0 (pure is easy to test), got: {}",
        score.role_multiplier
    );
}

// Tests for spec 157d: Purity level-based scoring

#[test]
fn test_purity_adjustment_strictly_pure_high_confidence() {
    let mut func = create_test_metrics();
    func.purity_level = Some(crate::core::PurityLevel::StrictlyPure);
    func.purity_confidence = Some(0.9);

    let adjustment = calculate_purity_adjustment(&func);
    assert_eq!(
        adjustment, 0.70,
        "StrictlyPure with high confidence should get 0.70"
    );
}

#[test]
fn test_purity_adjustment_strictly_pure_medium_confidence() {
    let mut func = create_test_metrics();
    func.purity_level = Some(crate::core::PurityLevel::StrictlyPure);
    func.purity_confidence = Some(0.7);

    let adjustment = calculate_purity_adjustment(&func);
    assert_eq!(
        adjustment, 0.80,
        "StrictlyPure with medium confidence should get 0.80"
    );
}

#[test]
fn test_purity_adjustment_locally_pure_high_confidence() {
    let mut func = create_test_metrics();
    func.purity_level = Some(crate::core::PurityLevel::LocallyPure);
    func.purity_confidence = Some(0.9);

    let adjustment = calculate_purity_adjustment(&func);
    assert_eq!(
        adjustment, 0.75,
        "LocallyPure with high confidence should get 0.75"
    );
}

#[test]
fn test_purity_adjustment_locally_pure_medium_confidence() {
    let mut func = create_test_metrics();
    func.purity_level = Some(crate::core::PurityLevel::LocallyPure);
    func.purity_confidence = Some(0.7);

    let adjustment = calculate_purity_adjustment(&func);
    assert_eq!(
        adjustment, 0.85,
        "LocallyPure with medium confidence should get 0.85"
    );
}

#[test]
fn test_purity_adjustment_read_only() {
    let mut func = create_test_metrics();
    func.purity_level = Some(crate::core::PurityLevel::ReadOnly);
    func.purity_confidence = Some(0.9);

    let adjustment = calculate_purity_adjustment(&func);
    assert_eq!(adjustment, 0.90, "ReadOnly should get 0.90");
}

#[test]
fn test_purity_adjustment_impure() {
    let mut func = create_test_metrics();
    func.purity_level = Some(crate::core::PurityLevel::Impure);
    func.purity_confidence = Some(0.9);

    let adjustment = calculate_purity_adjustment(&func);
    assert_eq!(adjustment, 1.0, "Impure should get 1.0");
}

#[test]
fn test_backward_compatibility_with_is_pure() {
    let mut func = create_test_metrics();
    func.purity_level = None; // Not set - old code path
    func.is_pure = Some(true);
    func.purity_confidence = Some(0.9);

    let adjustment = calculate_purity_adjustment(&func);
    assert_eq!(
        adjustment, 0.70,
        "Old is_pure field with high confidence should still work"
    );
}

#[test]
fn test_backward_compatibility_with_is_pure_medium_confidence() {
    let mut func = create_test_metrics();
    func.purity_level = None; // Not set - old code path
    func.is_pure = Some(true);
    func.purity_confidence = Some(0.7);

    let adjustment = calculate_purity_adjustment(&func);
    assert_eq!(
        adjustment, 0.85,
        "Old is_pure field with medium confidence should still work"
    );
}

#[test]
fn test_locally_pure_scores_lower_than_impure() {
    // Test that LocallyPure functions get lower scores than Impure functions
    let mut func_locally_pure = create_test_metrics();
    func_locally_pure.purity_level = Some(crate::core::PurityLevel::LocallyPure);
    func_locally_pure.purity_confidence = Some(0.9);
    func_locally_pure.cyclomatic = 10;
    func_locally_pure.cognitive = 15;

    let mut func_impure = create_test_metrics();
    func_impure.purity_level = Some(crate::core::PurityLevel::Impure);
    func_impure.purity_confidence = Some(0.9);
    func_impure.cyclomatic = 10;
    func_impure.cognitive = 15;

    let call_graph = CallGraph::new();
    let score_locally_pure =
        calculate_unified_priority(&func_locally_pure, &call_graph, None, None);
    let score_impure = calculate_unified_priority(&func_impure, &call_graph, None, None);

    assert!(
        score_locally_pure.final_score < score_impure.final_score,
        "LocallyPure (score: {}) should score lower than Impure (score: {})",
        score_locally_pure.final_score,
        score_impure.final_score
    );
}

#[test]
fn test_locally_pure_scores_higher_than_strictly_pure() {
    // Test that LocallyPure functions get slightly higher scores than StrictlyPure functions
    let mut func_locally_pure = create_test_metrics();
    func_locally_pure.purity_level = Some(crate::core::PurityLevel::LocallyPure);
    func_locally_pure.purity_confidence = Some(0.9);
    func_locally_pure.cyclomatic = 10;
    func_locally_pure.cognitive = 15;

    let mut func_strictly_pure = create_test_metrics();
    func_strictly_pure.purity_level = Some(crate::core::PurityLevel::StrictlyPure);
    func_strictly_pure.purity_confidence = Some(0.9);
    func_strictly_pure.cyclomatic = 10;
    func_strictly_pure.cognitive = 15;

    let call_graph = CallGraph::new();
    let score_locally_pure =
        calculate_unified_priority(&func_locally_pure, &call_graph, None, None);
    let score_strictly_pure =
        calculate_unified_priority(&func_strictly_pure, &call_graph, None, None);

    assert!(
        score_locally_pure.final_score > score_strictly_pure.final_score,
        "LocallyPure (score: {}) should score slightly higher than StrictlyPure (score: {})",
        score_locally_pure.final_score,
        score_strictly_pure.final_score
    );
}

// Tests for entropy dampening integration (spec 214)

#[test]
fn test_entropy_dampening_reduces_complexity_score() {
    use crate::complexity::entropy_core::EntropyScore;

    // Create two functions - one with LOW entropy (repetitive patterns), one without entropy data
    // Dampening only applies when token_entropy < 0.4 (repetitive code)
    let mut func_with_patterns = create_test_metrics();
    func_with_patterns.cyclomatic = 10;
    func_with_patterns.cognitive = 15;
    func_with_patterns.entropy_score = Some(EntropyScore {
        token_entropy: 0.3,      // LOW entropy = repetitive = dampening applies
        pattern_repetition: 0.8, // High pattern repetition
        branch_similarity: 0.6,
        effective_complexity: 0.3,
        unique_variables: 5,
        max_nesting: 2,
        dampening_applied: 1.0,
    });

    let mut func_without_patterns = create_test_metrics();
    func_without_patterns.cyclomatic = 10;
    func_without_patterns.cognitive = 15;
    func_without_patterns.entropy_score = None; // No entropy data = no dampening

    let call_graph = CallGraph::new();
    let score_with_patterns =
        calculate_unified_priority(&func_with_patterns, &call_graph, None, None);
    let score_without_patterns =
        calculate_unified_priority(&func_without_patterns, &call_graph, None, None);

    // Repetitive (low entropy) code should score lower due to entropy dampening
    assert!(
        score_with_patterns.final_score < score_without_patterns.final_score,
        "Repetitive code (score: {}) should score lower than code without patterns (score: {})",
        score_with_patterns.final_score,
        score_without_patterns.final_score
    );
}

#[test]
fn test_entropy_dampening_never_increases_complexity() {
    use crate::complexity::entropy_core::EntropyScore;

    // Create function with LOW entropy score (repetitive = dampening applies)
    let mut func = create_test_metrics();
    func.cyclomatic = 20;
    func.cognitive = 30;
    func.entropy_score = Some(EntropyScore {
        token_entropy: 0.3, // LOW entropy = repetitive = dampening applies
        pattern_repetition: 0.5,
        branch_similarity: 0.4,
        effective_complexity: 0.6,
        unique_variables: 10,
        max_nesting: 3,
        dampening_applied: 1.0,
    });

    // Calculate entropy details
    let entropy_analysis = crate::priority::scoring::computation::calculate_entropy_analysis(&func);

    assert!(entropy_analysis.is_some());
    let analysis = entropy_analysis.unwrap();

    // Entropy-adjusted complexity should never exceed raw complexity
    assert!(
        analysis.adjusted_complexity <= func.cognitive,
        "Adjusted complexity ({}) should not exceed raw cognitive ({})",
        analysis.adjusted_complexity,
        func.cognitive
    );
    assert!(
        analysis.dampening_factor <= 1.0,
        "Dampening factor ({}) should not exceed 1.0",
        analysis.dampening_factor
    );
}

#[test]
fn test_high_entropy_no_dampening() {
    use crate::complexity::entropy_core::EntropyScore;

    // HIGH entropy code (> 0.4) should NOT get dampening
    // This tests the new behavior where real complexity isn't artificially reduced
    let mut func = create_test_metrics();
    func.cyclomatic = 20;
    func.cognitive = 30;
    func.entropy_score = Some(EntropyScore {
        token_entropy: 0.6, // HIGH entropy = chaotic = no dampening
        pattern_repetition: 0.5,
        branch_similarity: 0.4,
        effective_complexity: 0.6,
        unique_variables: 10,
        max_nesting: 3,
        dampening_applied: 1.0,
    });

    let entropy_analysis = crate::priority::scoring::computation::calculate_entropy_analysis(&func);

    assert!(entropy_analysis.is_some());
    let analysis = entropy_analysis.unwrap();

    // High entropy = dampening factor should be 1.0 (no reduction)
    assert_eq!(
        analysis.dampening_factor, 1.0,
        "High entropy code should have dampening_factor=1.0, got {}",
        analysis.dampening_factor
    );
    // Cognitive should not be reduced
    assert_eq!(
        analysis.adjusted_complexity, func.cognitive,
        "High entropy code cognitive should not be reduced"
    );
}

#[test]
fn test_entropy_details_populated_in_debt_item() {
    use crate::complexity::entropy_core::EntropyScore;
    use crate::priority::scoring::construction::create_unified_debt_item_enhanced;

    let mut func = create_test_metrics();
    func.cyclomatic = 15;
    func.cognitive = 20;
    // Spec 206: Set nesting > 2 to avoid clean dispatcher classification (returns None)
    func.nesting = 3;
    func.entropy_score = Some(EntropyScore {
        token_entropy: 0.6,
        pattern_repetition: 0.7,
        branch_similarity: 0.5,
        effective_complexity: 0.4,
        unique_variables: 8,
        max_nesting: 3,
        dampening_applied: 1.0,
    });

    let call_graph = CallGraph::new();
    let debt_item = create_unified_debt_item_enhanced(&func, &call_graph, None, None);

    assert!(debt_item.is_some());
    let item = debt_item.unwrap();

    // Verify entropy analysis is populated
    assert!(item.entropy_analysis.is_some());
    let entropy = item.entropy_analysis.as_ref().unwrap();

    // Verify adjusted cognitive value is reasonable (dampened from original)
    assert!(entropy.adjusted_complexity <= func.cognitive);
}

#[test]
fn test_normalize_complexity_with_entropy_dampening() {
    use crate::complexity::entropy_core::EntropyScore;

    // Test that normalize_complexity uses entropy-adjusted values when available
    // LOW entropy (< 0.4) triggers dampening for repetitive code patterns
    let mut func = create_test_metrics();
    func.cyclomatic = 20;
    func.cognitive = 30;
    func.entropy_score = Some(EntropyScore {
        token_entropy: 0.25,     // LOW entropy = repetitive patterns = dampening applies
        pattern_repetition: 0.8, // High repetition should reduce complexity
        branch_similarity: 0.6,
        effective_complexity: 0.3,
        unique_variables: 5,
        max_nesting: 2,
        dampening_applied: 1.0,
    });

    let call_graph = CallGraph::new();

    // Calculate score with entropy dampening
    let score_with_entropy = calculate_unified_priority(&func, &call_graph, None, None);

    // Remove entropy data and recalculate
    func.entropy_score = None;
    let score_without_entropy = calculate_unified_priority(&func, &call_graph, None, None);

    // Complexity factor should be lower with entropy dampening for low-entropy code
    assert!(
        score_with_entropy.complexity_factor < score_without_entropy.complexity_factor,
        "Complexity factor with entropy ({}) should be lower than without entropy ({})",
        score_with_entropy.complexity_factor,
        score_without_entropy.complexity_factor
    );
}

// Data flow scoring tests (spec 218)

#[test]
fn test_purity_spectrum_score_multipliers() {
    assert_eq!(PuritySpectrum::StrictlyPure.score_multiplier(), 0.0);
    assert_eq!(PuritySpectrum::LocallyPure.score_multiplier(), 0.3);
    assert_eq!(PuritySpectrum::IOIsolated.score_multiplier(), 0.6);
    assert_eq!(PuritySpectrum::IOMixed.score_multiplier(), 0.9);
    assert_eq!(PuritySpectrum::Impure.score_multiplier(), 1.0);
}

#[test]
fn test_calculate_purity_factor_strictly_pure() {
    use crate::data_flow::{DataFlowGraph, MutationInfo, PurityInfo};

    let mut data_flow = DataFlowGraph::new();
    let func_id = FunctionId::new(PathBuf::from("test.rs"), "pure_func".to_string(), 10);

    // Add strictly pure function info
    data_flow.set_purity_info(
        func_id.clone(),
        PurityInfo {
            is_pure: true,
            confidence: 0.9,
            impurity_reasons: vec![],
        },
    );

    // No mutations (spec 257: binary signals)
    data_flow.set_mutation_info(
        func_id.clone(),
        MutationInfo {
            has_mutations: false,
            detected_mutations: vec![],
        },
    );

    let purity_factor = calculate_purity_factor(&func_id, &data_flow);

    // Strictly pure functions should get 0.0 (minimal priority)
    assert_eq!(purity_factor, 0.0);
}

#[test]
fn test_calculate_purity_factor_locally_pure() {
    use crate::data_flow::{DataFlowGraph, MutationInfo, PurityInfo};

    let mut data_flow = DataFlowGraph::new();
    let func_id = FunctionId::new(PathBuf::from("test.rs"), "locally_pure".to_string(), 20);

    // Pure with high confidence
    data_flow.set_purity_info(
        func_id.clone(),
        PurityInfo {
            is_pure: true,
            confidence: 0.9,
            impurity_reasons: vec![],
        },
    );

    // Has local mutations (spec 257: binary signals)
    data_flow.set_mutation_info(
        func_id.clone(),
        MutationInfo {
            has_mutations: true,
            detected_mutations: vec!["local_var".to_string()],
        },
    );

    let purity_factor = calculate_purity_factor(&func_id, &data_flow);

    // Locally pure functions should get 0.3
    assert_eq!(purity_factor, 0.3);
}

#[test]
fn test_calculate_refactorability_factor_returns_neutral() {
    use crate::config::DataFlowScoringConfig;
    use crate::data_flow::{DataFlowGraph, MutationInfo};

    let mut data_flow = DataFlowGraph::new();
    let func_id = FunctionId::new(PathBuf::from("test.rs"), "refactorable".to_string(), 30);

    // Spec 257: binary signals - refactorability always returns 1.0
    data_flow.set_mutation_info(
        func_id.clone(),
        MutationInfo {
            has_mutations: true,
            detected_mutations: vec!["live1".to_string()],
        },
    );

    let config = DataFlowScoringConfig::default();
    let refactor_factor = calculate_refactorability_factor(&func_id, &data_flow, &config);

    // Dead store analysis removed - always returns 1.0
    assert_eq!(refactor_factor, 1.0);
}

#[test]
fn test_calculate_pattern_factor_data_flow_pipeline() {
    use crate::data_flow::{DataFlowGraph, DataTransformation};

    use crate::priority::call_graph::{CallType, FunctionCall};

    // Create call graph with callees
    let mut call_graph = CallGraph::new();
    let func_id = FunctionId::new(PathBuf::from("test.rs"), "pipeline".to_string(), 50);
    let callee1 = FunctionId::new(PathBuf::from("test.rs"), "map_fn".to_string(), 60);
    let callee2 = FunctionId::new(PathBuf::from("test.rs"), "filter_fn".to_string(), 70);

    // Add call relationships
    call_graph.add_call(FunctionCall {
        caller: func_id.clone(),
        callee: callee1.clone(),
        call_type: CallType::Direct,
    });
    call_graph.add_call(FunctionCall {
        caller: func_id.clone(),
        callee: callee2.clone(),
        call_type: CallType::Direct,
    });

    let mut data_flow = DataFlowGraph::from_call_graph(call_graph);

    // Add variable dependencies
    let mut vars = std::collections::HashSet::new();
    vars.insert("input".to_string());
    vars.insert("output".to_string());
    data_flow.add_variable_dependencies(func_id.clone(), vars);

    // Add data transformations to callees
    data_flow.add_data_transformation(
        func_id.clone(),
        callee1,
        DataTransformation {
            input_vars: vec!["input".to_string()],
            output_vars: vec!["mapped".to_string()],
            transformation_type: "map".to_string(),
        },
    );

    data_flow.add_data_transformation(
        func_id.clone(),
        callee2,
        DataTransformation {
            input_vars: vec!["mapped".to_string()],
            output_vars: vec!["output".to_string()],
            transformation_type: "filter".to_string(),
        },
    );

    let pattern_factor = calculate_pattern_factor(&func_id, &data_flow);

    // High transformation ratio (2 transforms / 2 vars = 1.0) should give 0.7
    assert_eq!(pattern_factor, 0.7);
}

#[test]
fn test_calculate_unified_priority_with_data_flow_disabled() {
    let func = create_test_metrics();
    let call_graph = CallGraph::new();
    let data_flow = DataFlowGraph::from_call_graph(call_graph.clone());
    let config = DataFlowScoringConfig {
        enabled: false,
        ..Default::default()
    };

    let score = calculate_unified_priority_with_data_flow(
        &func,
        &call_graph,
        &data_flow,
        None,
        None,
        None,
        &config,
    );

    // With data flow scoring disabled, factors should be None
    assert!(score.purity_factor.is_none());
    assert!(score.refactorability_factor.is_none());
    assert!(score.pattern_factor.is_none());
}

#[test]
fn test_calculate_unified_priority_with_data_flow_enabled() {
    use crate::data_flow::{DataFlowGraph, PurityInfo};

    let func = create_test_metrics();
    let call_graph = CallGraph::new();
    let mut data_flow = DataFlowGraph::from_call_graph(call_graph.clone());

    let func_id = FunctionId::new(func.file.clone(), func.name.clone(), func.line);

    // Add some purity info
    data_flow.set_purity_info(
        func_id,
        PurityInfo {
            is_pure: false,
            confidence: 0.5,
            impurity_reasons: vec!["mutation detected".to_string()],
        },
    );

    let config = DataFlowScoringConfig::default();

    let score = calculate_unified_priority_with_data_flow(
        &func,
        &call_graph,
        &data_flow,
        None,
        None,
        None,
        &config,
    );

    // With data flow scoring enabled, factors should be populated
    assert!(score.purity_factor.is_some());
    assert!(score.refactorability_factor.is_some());
    assert!(score.pattern_factor.is_some());

    // Impure function should have purity_factor = 1.0
    assert_eq!(score.purity_factor.unwrap(), 1.0);
}
