mod ast_analysis;
mod classifiers;
mod pattern_matchers;

use crate::analyzers::rust_data_flow_analyzer::analyze_data_flow;
use crate::core::FunctionMetrics;
use crate::priority::call_graph::{CallGraph, FunctionId};
use ast_analysis::is_simple_accessor_body;
use classifiers::{
    is_constructor_enhanced, is_debug_function, is_enum_converter_enhanced, is_io_wrapper,
    is_orchestrator, is_pattern_matching_function,
};
use pattern_matchers::{is_entry_point_by_name, matches_accessor_name};
use serde::{Deserialize, Serialize};

#[cfg(test)]
use classifiers::{
    calculate_delegation_ratio, delegates_to_tested_functions, has_diagnostic_characteristics,
    is_simple_constructor,
};
#[cfg(test)]
use pattern_matchers::matches_debug_pattern;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum FunctionRole {
    PureLogic,    // Business logic, high test priority
    Orchestrator, // Coordinates other functions
    IOWrapper,    // Thin I/O layer
    EntryPoint,   // Main entry points
    PatternMatch, // Pattern matching function (low complexity)
    Debug,        // Debug/diagnostic functions (low test priority)
    Unknown,      // Cannot classify
}

pub fn classify_function_role(
    func: &FunctionMetrics,
    func_id: &FunctionId,
    call_graph: &CallGraph,
) -> FunctionRole {
    // Use a functional approach with classification rules
    // Note: AST is not available at this level, so we pass None
    // Full AST-based detection will be integrated when threading syn::ItemFn
    classify_by_rules(func, func_id, call_graph, None).unwrap_or_else(|| {
        // BUG-001 fix: Use purity analysis to prevent impure I/O functions from being
        // classified as PureLogic. If function is impure, classify as Unknown instead.
        if is_impure_function(func) {
            FunctionRole::Unknown
        } else {
            FunctionRole::PureLogic
        }
    })
}

// Pure function that applies classification rules in order
fn classify_by_rules(
    func: &FunctionMetrics,
    func_id: &FunctionId,
    call_graph: &CallGraph,
    syn_func: Option<&syn::ItemFn>,
) -> Option<FunctionRole> {
    // Entry point has highest precedence
    if is_entry_point(func_id, call_graph) {
        return Some(FunctionRole::EntryPoint);
    }

    // Check for debug/diagnostic functions early (Spec 119)
    if is_debug_function(func) {
        return Some(FunctionRole::Debug);
    }

    // Check for constructors BEFORE pattern matching (Spec 117 + 122)
    if is_constructor_enhanced(func, syn_func) {
        return Some(FunctionRole::IOWrapper);
    }

    // Check for enum converters (Spec 124)
    if let Some(syn_func) = syn_func
        && is_enum_converter_enhanced(func, syn_func)
    {
        return Some(FunctionRole::IOWrapper);
    }

    // Check for accessor methods (Spec 125)
    // This should come after enum converter detection but before pattern matching
    if is_accessor_method(func, syn_func) {
        return Some(FunctionRole::IOWrapper);
    }

    // Check for data flow classification (Spec 126) - only if enabled and AST available
    if let Some(syn_func) = syn_func {
        let config = crate::config::get_data_flow_classification_config();

        if config.enabled {
            let profile = analyze_data_flow(syn_func);

            // Only classify if high confidence
            if profile.confidence >= config.min_confidence
                && profile.transformation_ratio >= config.min_transformation_ratio
                && profile.business_logic_ratio < config.max_business_logic_ratio
            {
                return Some(FunctionRole::Orchestrator);
            }
        }
    }

    // Check for pattern matching functions (like detect_file_type)
    if is_pattern_matching_function(func, func_id) {
        return Some(FunctionRole::PatternMatch);
    }

    // Check I/O wrapper BEFORE orchestration
    if is_io_wrapper(func) {
        return Some(FunctionRole::IOWrapper);
    }

    // Only then check orchestration patterns
    if is_orchestrator(func, func_id, call_graph) {
        return Some(FunctionRole::Orchestrator);
    }

    None // Will default to PureLogic
}

// Pure function to check if a function is an entry point
fn is_entry_point(func_id: &FunctionId, call_graph: &CallGraph) -> bool {
    call_graph.is_entry_point(func_id) || is_entry_point_by_name(&func_id.name)
}

/// Check if a function is impure based on purity analysis (BUG-001 fix).
///
/// Used to prevent impure functions from being classified as PureLogic.
/// A function is considered impure if:
/// - `purity_level` is `Some(Impure)`
/// - `is_pure` is `Some(false)` (legacy field)
fn is_impure_function(func: &FunctionMetrics) -> bool {
    use crate::core::PurityLevel;

    // Check new purity_level field first
    if let Some(level) = func.purity_level {
        return level == PurityLevel::Impure;
    }

    // Fallback to legacy is_pure field
    func.is_pure == Some(false)
}

/// Detect simple accessor/getter methods (spec 125)
///
/// Identifies simple accessor and getter methods that should be classified as
/// IOWrapper instead of PureLogic to reduce their priority score.
///
/// # Detection Strategy
///
/// 1. Check name matches accessor patterns (id, name, get_*, is_*, etc.)
/// 2. Verify low complexity (cyclomatic ≤ 2, cognitive ≤ 1)
/// 3. Check function is short (< 10 lines, nesting ≤ 1)
/// 4. If AST available, verify body is simple accessor pattern
///
/// # Detected Patterns
///
/// **Simple accessors** (classified as IOWrapper):
/// - Direct field access: `id()`, `name()` returning field values
/// - Boolean checks: `is_active()`, `has_permission()` with simple conditions
/// - Type conversions: `as_str()`, `to_string()` with minimal logic
/// - Uses immutable `&self` reference only
/// - Very low complexity (cyclomatic ≤ 2, cognitive ≤ 1)
/// - Short length (< 10 lines, nesting ≤ 1)
///
/// **Complex methods** (NOT detected, remain PureLogic):
/// - Methods with calculations, iterations, or aggregations
/// - Methods calling other business logic functions
/// - Methods with complex control flow or multiple branches
fn is_accessor_method(func: &FunctionMetrics, syn_func: Option<&syn::ItemFn>) -> bool {
    let config = crate::config::get_accessor_detection_config();

    // Check if accessor detection is enabled
    if !config.enabled {
        return false;
    }

    // Check name matches accessor pattern
    if !matches_accessor_name(&func.name, &config) {
        return false;
    }

    // Check complexity is minimal
    if func.cyclomatic > config.max_cyclomatic
        || func.cognitive > config.max_cognitive
        || func.length >= config.max_length
        || func.nesting > config.max_nesting
    {
        return false;
    }

    // If AST available, verify body is simple
    if let Some(syn_func) = syn_func
        && !is_simple_accessor_body(syn_func)
    {
        return false;
    }

    true
}

pub fn get_role_multiplier(role: FunctionRole) -> f64 {
    // Get multipliers from configuration
    let config = crate::config::get_role_multipliers();

    match role {
        FunctionRole::PureLogic => config.pure_logic,
        FunctionRole::Orchestrator => config.orchestrator,
        FunctionRole::IOWrapper => config.io_wrapper,
        FunctionRole::EntryPoint => config.entry_point,
        FunctionRole::PatternMatch => config.pattern_match,
        FunctionRole::Debug => config.debug,
        FunctionRole::Unknown => config.unknown,
    }
}

// Semantic priority calculation removed per spec 58
// Role multipliers now provide the only role-based adjustment to avoid double penalties

#[cfg(test)]
mod tests {
    use super::*;
    use crate::core::FunctionMetrics;
    use crate::priority::call_graph::{CallGraph, CallType, FunctionCall, FunctionId};
    use std::path::PathBuf;

    fn create_test_metrics(
        name: &str,
        cyclomatic: u32,
        cognitive: u32,
        lines: usize,
    ) -> FunctionMetrics {
        FunctionMetrics {
            file: PathBuf::from("test.rs"),
            name: name.to_string(),
            line: 1,
            length: lines,
            cyclomatic,
            cognitive,
            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_entry_point_classification() {
        let graph = CallGraph::new();
        let func = create_test_metrics("main", 5, 8, 50);
        let func_id = FunctionId::new(PathBuf::from("main.rs"), "main".to_string(), 1);

        let role = classify_function_role(&func, &func_id, &graph);
        assert_eq!(role, FunctionRole::EntryPoint);
    }

    #[test]
    fn test_orchestrator_classification() {
        let mut graph = CallGraph::new();
        let func = create_test_metrics("coordinate_tasks", 2, 3, 15);
        let func_id = FunctionId::new(
            PathBuf::from("coord.rs"),
            "coordinate_tasks".to_string(),
            10,
        );

        // Add the orchestrator function
        graph.add_function(func_id.clone(), false, false, 2, 15);

        // Add some worker functions it calls
        for i in 0..3 {
            let worker_id =
                FunctionId::new(PathBuf::from("worker.rs"), format!("worker_{i}"), i * 10);
            graph.add_function(worker_id.clone(), false, false, 8, 40);
            graph.add_call(crate::priority::call_graph::FunctionCall {
                caller: func_id.clone(),
                callee: worker_id,
                call_type: crate::priority::call_graph::CallType::Delegate,
            });
        }

        let role = classify_function_role(&func, &func_id, &graph);
        assert_eq!(role, FunctionRole::Orchestrator);
    }

    #[test]
    fn test_io_wrapper_classification() {
        let graph = CallGraph::new();
        let func = create_test_metrics("read_file", 1, 2, 10);
        let func_id = FunctionId::new(PathBuf::from("io.rs"), "read_file".to_string(), 5);

        let role = classify_function_role(&func, &func_id, &graph);
        assert_eq!(role, FunctionRole::IOWrapper);
    }

    #[test]
    fn test_io_orchestration_classification() {
        let graph = CallGraph::new();

        // Test case similar to output_unified_priorities:
        // - Has "output_" prefix (strong I/O pattern)
        // - 38 lines (within the 50 line limit)
        // - Cyclomatic 12 (from format branching)
        // - Nesting 3 (not deeply nested)
        let mut func = create_test_metrics("output_unified_priorities", 12, 19, 38);
        func.nesting = 3;

        let func_id = FunctionId::new(
            PathBuf::from("main.rs"),
            "output_unified_priorities".to_string(),
            861,
        );

        let role = classify_function_role(&func, &func_id, &graph);
        assert_eq!(role, FunctionRole::IOWrapper);

        // Test that high nesting disqualifies I/O orchestration
        func.nesting = 4;
        let role = classify_function_role(&func, &func_id, &graph);
        assert_eq!(role, FunctionRole::PureLogic);
    }

    #[test]
    fn test_pure_logic_classification() {
        let graph = CallGraph::new();
        let func = create_test_metrics("calculate_risk", 8, 12, 60);
        let func_id = FunctionId::new(PathBuf::from("calc.rs"), "calculate_risk".to_string(), 20);

        let role = classify_function_role(&func, &func_id, &graph);
        assert_eq!(role, FunctionRole::PureLogic);
    }

    #[test]
    fn test_role_multipliers() {
        // Test with updated configuration values (BUG-001 fix: PureLogic now 0.7)
        assert_eq!(get_role_multiplier(FunctionRole::PureLogic), 0.7);
        assert_eq!(get_role_multiplier(FunctionRole::Orchestrator), 0.8);
        assert_eq!(get_role_multiplier(FunctionRole::IOWrapper), 0.7);
        assert_eq!(get_role_multiplier(FunctionRole::EntryPoint), 0.9);
        assert_eq!(get_role_multiplier(FunctionRole::PatternMatch), 0.6);
        assert_eq!(get_role_multiplier(FunctionRole::Unknown), 1.0);
    }

    #[test]
    fn test_formatting_function_not_orchestrator() {
        let mut graph = CallGraph::new();

        // Create a function like format_recommendation_box_header
        let func = create_test_metrics("format_recommendation_box_header", 1, 0, 9);
        let func_id = FunctionId::new(
            PathBuf::from("insights.rs"),
            "format_recommendation_box_header".to_string(),
            142,
        );

        // Add the function to the graph
        graph.add_function(func_id.clone(), false, false, 1, 9);

        // Add callees: calculate_dash_count and format (from macro)
        let callee1 = FunctionId::new(
            PathBuf::from("insights.rs"),
            "calculate_dash_count".to_string(),
            138,
        );
        let callee2 = FunctionId::new(PathBuf::from("std"), "format".to_string(), 1);

        graph.add_function(callee1.clone(), false, false, 1, 3);
        graph.add_function(callee2.clone(), false, false, 1, 1);

        graph.add_call(FunctionCall {
            caller: func_id.clone(),
            callee: callee1,
            call_type: CallType::Direct,
        });
        graph.add_call(FunctionCall {
            caller: func_id.clone(),
            callee: callee2,
            call_type: CallType::Direct,
        });

        // Test that it's not classified as orchestrator
        let role = classify_function_role(&func, &func_id, &graph);
        assert_eq!(
            role,
            FunctionRole::PureLogic,
            "Formatting function should be PureLogic, not Orchestrator"
        );

        // Verify it doesn't match delegation pattern
        assert!(
            !delegates_to_tested_functions(&func_id, &graph, 0.8),
            "Should not be considered delegation when calling std functions"
        );
    }

    #[test]
    fn test_actual_orchestrator_with_meaningful_callees() {
        let mut graph = CallGraph::new();

        // Create an actual orchestrator function
        let func = create_test_metrics("coordinate_workflow", 2, 3, 15);
        let func_id = FunctionId::new(
            PathBuf::from("workflow.rs"),
            "coordinate_workflow".to_string(),
            10,
        );

        graph.add_function(func_id.clone(), false, false, 2, 15);

        // Add meaningful callees (not std library)
        // Need at least 3 for the current config settings
        let callee1 = FunctionId::new(
            PathBuf::from("workflow.rs"),
            "process_step_one".to_string(),
            50,
        );
        let callee2 = FunctionId::new(
            PathBuf::from("workflow.rs"),
            "process_step_two".to_string(),
            100,
        );
        let callee3 = FunctionId::new(
            PathBuf::from("workflow.rs"),
            "process_step_three".to_string(),
            150,
        );

        graph.add_function(callee1.clone(), false, false, 5, 30);
        graph.add_function(callee2.clone(), false, false, 5, 30);
        graph.add_function(callee3.clone(), false, false, 5, 30);

        graph.add_call(FunctionCall {
            caller: func_id.clone(),
            callee: callee1,
            call_type: CallType::Direct,
        });
        graph.add_call(FunctionCall {
            caller: func_id.clone(),
            callee: callee2,
            call_type: CallType::Direct,
        });
        graph.add_call(FunctionCall {
            caller: func_id.clone(),
            callee: callee3,
            call_type: CallType::Direct,
        });

        // This should be classified as orchestrator
        let role = classify_function_role(&func, &func_id, &graph);
        assert_eq!(
            role,
            FunctionRole::Orchestrator,
            "Function coordinating multiple steps should be Orchestrator"
        );
    }

    #[test]
    fn test_orchestrator_with_error_handling_complexity_5() {
        let mut graph = CallGraph::new();

        // Function with complexity 5 from Result handling (spec 117)
        let func = create_test_metrics("coordinate_tasks", 5, 3, 20);
        let func_id = FunctionId::new(
            PathBuf::from("tasks.rs"),
            "coordinate_tasks".to_string(),
            10,
        );

        graph.add_function(func_id.clone(), false, false, 5, 20);

        // Add 4 meaningful callees (20% of 20 lines = 4 calls for delegation ratio)
        for i in 0..4 {
            let callee_id = FunctionId::new(
                PathBuf::from("worker.rs"),
                format!("worker_task_{}", i),
                i * 20,
            );
            graph.add_function(callee_id.clone(), false, false, 8, 40);
            graph.add_call(FunctionCall {
                caller: func_id.clone(),
                callee: callee_id,
                call_type: CallType::Direct,
            });
        }

        // Should be classified as orchestrator despite higher complexity
        let role = classify_function_role(&func, &func_id, &graph);
        assert_eq!(
            role,
            FunctionRole::Orchestrator,
            "Function with complexity 5 and good delegation ratio should be Orchestrator"
        );
    }

    #[test]
    fn test_delegation_ratio_calculation() {
        let func = create_test_metrics("orchestrator", 4, 2, 20);

        // Create test callees - 4 calls in 20 lines = 20% ratio
        let callees_vec: Vec<FunctionId> = (0..4)
            .map(|i| FunctionId::new(PathBuf::from("test.rs"), format!("callee_{}", i), i * 10))
            .collect();

        let callees: Vec<&FunctionId> = callees_vec.iter().collect();

        let ratio = calculate_delegation_ratio(&func, &callees);
        assert!(
            (ratio - 0.2).abs() < 0.01,
            "Expected delegation ratio of 0.2, got {}",
            ratio
        );
    }

    #[test]
    fn test_high_complexity_not_orchestrator() {
        let mut graph = CallGraph::new();

        // Function with complexity > 5 should not be orchestrator
        let func = create_test_metrics("complex_logic", 8, 10, 30);
        let func_id = FunctionId::new(PathBuf::from("logic.rs"), "complex_logic".to_string(), 10);

        graph.add_function(func_id.clone(), false, false, 8, 30);

        // Even with callees, complexity > 5 means not orchestrator
        for i in 0..3 {
            let callee_id =
                FunctionId::new(PathBuf::from("worker.rs"), format!("worker_{}", i), i * 20);
            graph.add_function(callee_id.clone(), false, false, 5, 20);
            graph.add_call(FunctionCall {
                caller: func_id.clone(),
                callee: callee_id,
                call_type: CallType::Direct,
            });
        }

        let role = classify_function_role(&func, &func_id, &graph);
        assert_eq!(
            role,
            FunctionRole::PureLogic,
            "Function with complexity > 5 should be PureLogic, not Orchestrator"
        );
    }

    #[test]
    fn test_simple_constructor_detection() {
        // Test: ContextMatcher::any() case (spec 117)
        let func = create_test_metrics("any", 1, 0, 9);
        assert!(
            is_simple_constructor(&func),
            "any() should be detected as constructor"
        );

        // Test: Standard new() constructor
        let func = create_test_metrics("new", 1, 0, 5);
        assert!(
            is_simple_constructor(&func),
            "new() should be detected as constructor"
        );

        // Test: from_* constructor
        let func = create_test_metrics("from_config", 1, 0, 8);
        assert!(
            is_simple_constructor(&func),
            "from_config() should be detected as constructor"
        );

        // Test: with_* constructor
        let func = create_test_metrics("with_defaults", 2, 1, 12);
        assert!(
            is_simple_constructor(&func),
            "with_defaults() should be detected as constructor"
        );

        // Test: Complex factory should NOT match
        let func = create_test_metrics("create_complex", 8, 12, 50);
        assert!(
            !is_simple_constructor(&func),
            "Complex function should NOT be detected as constructor"
        );

        // Test: Long function should NOT match
        let func = create_test_metrics("new_complex", 2, 2, 25);
        assert!(
            !is_simple_constructor(&func),
            "Long function should NOT be detected as constructor"
        );

        // Test: High cognitive complexity should NOT match
        let func = create_test_metrics("new_with_logic", 2, 8, 10);
        assert!(
            !is_simple_constructor(&func),
            "High cognitive complexity should NOT be detected as constructor"
        );
    }

    #[test]
    fn test_constructor_classification_precedence() {
        let graph = CallGraph::new();
        let func = create_test_metrics("any", 1, 0, 9);
        let func_id = FunctionId::new(PathBuf::from("context/rules.rs"), "any".to_string(), 52);

        let role = classify_function_role(&func, &func_id, &graph);
        assert_eq!(
            role,
            FunctionRole::IOWrapper,
            "Simple constructor should be classified as IOWrapper, not PureLogic"
        );
    }

    #[test]
    fn test_constructor_name_patterns() {
        // Test exact matches
        assert!(is_simple_constructor(&create_test_metrics("new", 1, 0, 5)));
        assert!(is_simple_constructor(&create_test_metrics(
            "default", 1, 0, 5
        )));
        assert!(is_simple_constructor(&create_test_metrics(
            "empty", 1, 0, 5
        )));
        assert!(is_simple_constructor(&create_test_metrics("zero", 1, 0, 5)));

        // Test prefix matches
        assert!(is_simple_constructor(&create_test_metrics(
            "from_str", 1, 0, 8
        )));
        assert!(is_simple_constructor(&create_test_metrics(
            "with_capacity",
            2,
            1,
            10
        )));
        assert!(is_simple_constructor(&create_test_metrics(
            "create_default",
            1,
            0,
            7
        )));
        assert!(is_simple_constructor(&create_test_metrics(
            "make_instance",
            1,
            0,
            6
        )));
        assert!(is_simple_constructor(&create_test_metrics(
            "build_config",
            2,
            2,
            12
        )));

        // Test non-constructor names
        let func = create_test_metrics("calculate_score", 1, 0, 5);
        assert!(
            !is_simple_constructor(&func),
            "Non-constructor name should not match"
        );
    }

    #[test]
    fn test_constructor_complexity_thresholds() {
        // Test at threshold boundaries
        let func = create_test_metrics("new", 2, 3, 14);
        assert!(
            is_simple_constructor(&func),
            "At threshold limits should match"
        );

        // Test just over cyclomatic threshold
        let func = create_test_metrics("new", 3, 2, 10);
        assert!(
            !is_simple_constructor(&func),
            "Over cyclomatic threshold should not match"
        );

        // Test just over cognitive threshold
        let func = create_test_metrics("new", 1, 4, 10);
        assert!(
            !is_simple_constructor(&func),
            "Over cognitive threshold should not match"
        );

        // Test just over length threshold
        let func = create_test_metrics("new", 1, 2, 15);
        assert!(
            !is_simple_constructor(&func),
            "Over length threshold should not match"
        );

        // Test nesting threshold
        let mut func = create_test_metrics("new", 1, 2, 10);
        func.nesting = 2;
        assert!(
            !is_simple_constructor(&func),
            "Over nesting threshold should not match"
        );
    }

    #[test]
    fn test_ast_detects_non_standard_constructor() {
        use syn::parse_quote;

        let source: syn::ItemFn = parse_quote! {
            pub fn create_default_client() -> Self {
                Self {
                    timeout: Duration::from_secs(30),
                    retries: 3,
                }
            }
        };

        let func = create_test_metrics("create_default_client", 1, 0, 5);

        // With AST: should be detected as constructor
        assert!(
            is_constructor_enhanced(&func, Some(&source)),
            "AST should detect non-standard constructor name"
        );

        // Without AST: fallback to name-based (should also match due to create_ prefix)
        assert!(
            is_constructor_enhanced(&func, None),
            "Should fallback to name-based detection"
        );
    }

    #[test]
    fn test_ast_detects_result_self_constructor() {
        use syn::parse_quote;

        let source: syn::ItemFn = parse_quote! {
            pub fn try_new(value: i32) -> Result<Self, Error> {
                if value > 0 {
                    Ok(Self { value })
                } else {
                    Err(Error::InvalidValue)
                }
            }
        };

        let func = create_test_metrics("try_new", 2, 1, 8);

        // With AST: should be detected as constructor (Result<Self>)
        assert!(
            is_constructor_enhanced(&func, Some(&source)),
            "AST should detect Result<Self> constructor"
        );
    }

    #[test]
    fn test_ast_rejects_loop_in_constructor() {
        use syn::parse_quote;

        let source: syn::ItemFn = parse_quote! {
            pub fn process_items() -> Self {
                let mut result = Self::new();
                for item in items {
                    result.add(item);
                }
                result
            }
        };

        let func = create_test_metrics("process_items", 2, 3, 8);

        // Should NOT be detected as constructor due to loop
        assert!(
            !is_constructor_enhanced(&func, Some(&source)),
            "AST should reject constructors with loops"
        );
    }

    #[test]
    fn test_ast_fallback_when_not_returning_self() {
        use syn::parse_quote;

        let source: syn::ItemFn = parse_quote! {
            pub fn calculate_value() -> i32 {
                42
            }
        };

        let func = create_test_metrics("calculate_value", 1, 0, 3);

        // Should fallback to name-based (which will reject non-constructor name)
        assert!(
            !is_constructor_enhanced(&func, Some(&source)),
            "AST should fallback when not returning Self"
        );
    }

    #[test]
    fn test_enum_converter_framework_type_name() {
        use syn::parse_quote;

        let source: syn::ItemFn = parse_quote! {
            pub fn name(&self) -> &'static str {
                match self {
                    FrameworkType::Django => "Django",
                    FrameworkType::Flask => "Flask",
                    FrameworkType::PyQt => "PyQt",
                }
            }
        };

        let func = create_test_metrics("name", 3, 0, 7);

        // Should be detected as enum converter
        assert!(
            is_enum_converter_enhanced(&func, &source),
            "FrameworkType::name should be detected as enum converter"
        );
    }

    #[test]
    fn test_enum_converter_builtin_exception_as_str() {
        use syn::parse_quote;

        let source: syn::ItemFn = parse_quote! {
            fn as_str(&self) -> &str {
                match self {
                    Self::BaseException => "BaseException",
                    Self::ValueError => "ValueError",
                    Self::TypeError => "TypeError",
                }
            }
        };

        let func = create_test_metrics("as_str", 3, 0, 7);

        // Should be detected as enum converter
        assert!(
            is_enum_converter_enhanced(&func, &source),
            "BuiltinException::as_str should be detected as enum converter"
        );
    }

    #[test]
    fn test_enum_converter_with_function_calls_not_detected() {
        use syn::parse_quote;

        let source: syn::ItemFn = parse_quote! {
            pub fn process(&self) -> String {
                match self {
                    Variant::A => format!("A"),
                    Variant::B => format!("B"),
                }
            }
        };

        let func = create_test_metrics("process", 2, 1, 7);

        // Should NOT be detected (has function calls)
        assert!(
            !is_enum_converter_enhanced(&func, &source),
            "Converter with function calls should NOT be detected"
        );
    }

    #[test]
    fn test_enum_converter_high_cognitive_complexity_rejected() {
        use syn::parse_quote;

        let source: syn::ItemFn = parse_quote! {
            pub fn name(&self) -> &'static str {
                match self {
                    Type::A => "A",
                    Type::B => "B",
                }
            }
        };

        let func = create_test_metrics("name", 2, 5, 7); // cognitive = 5

        // Should be rejected due to high cognitive complexity
        assert!(
            !is_enum_converter_enhanced(&func, &source),
            "High cognitive complexity should be rejected"
        );
    }

    #[test]
    fn test_enum_converter_classification_precedence() {
        use syn::parse_quote;

        let graph = CallGraph::new();

        let source: syn::ItemFn = parse_quote! {
            pub fn name(&self) -> &'static str {
                match self {
                    FrameworkType::Django => "Django",
                    FrameworkType::Flask => "Flask",
                }
            }
        };

        let func = create_test_metrics("name", 2, 0, 7);
        let func_id = FunctionId::new(PathBuf::from("framework.rs"), "name".to_string(), 10);

        let role = classify_by_rules(&func, &func_id, &graph, Some(&source));
        assert_eq!(
            role,
            Some(FunctionRole::IOWrapper),
            "Enum converter should be classified as IOWrapper"
        );
    }

    #[test]
    fn test_ast_detection_can_be_disabled() {
        use syn::parse_quote;

        let source: syn::ItemFn = parse_quote! {
            pub fn create_default_client() -> Self {
                Self { field: 0 }
            }
        };

        let func = create_test_metrics("create_default_client", 1, 0, 5);

        // When AST detection is disabled via config, should use name-based only
        // NOTE: This test assumes config can be set, which currently uses a static global
        // In a real implementation, we'd inject config or use a test-specific config

        // With AST enabled (default), should detect
        assert!(
            is_constructor_enhanced(&func, Some(&source)),
            "Should detect with AST when enabled"
        );

        // Without AST parameter (None), should fallback to name-based
        assert!(
            is_constructor_enhanced(&func, None),
            "Should fallback to name-based when AST unavailable"
        );
    }

    // Accessor detection tests (spec 125)

    #[test]
    fn test_simple_field_accessor_detected() {
        let metrics = create_test_metrics("id", 1, 0, 3);
        assert!(
            is_accessor_method(&metrics, None),
            "id() should be detected as accessor"
        );

        let metrics = create_test_metrics("get_name", 1, 0, 5);
        assert!(
            is_accessor_method(&metrics, None),
            "get_name() should be detected as accessor"
        );
    }

    #[test]
    fn test_bool_accessor_detected() {
        let metrics = create_test_metrics("is_active", 2, 1, 8);
        assert!(
            is_accessor_method(&metrics, None),
            "is_active() should be detected as accessor"
        );

        let metrics = create_test_metrics("has_permission", 2, 0, 5);
        assert!(
            is_accessor_method(&metrics, None),
            "has_permission() should be detected as accessor"
        );
    }

    #[test]
    fn test_converter_method_detected() {
        let metrics = create_test_metrics("as_str", 1, 0, 3);
        assert!(
            is_accessor_method(&metrics, None),
            "as_str() should be detected as accessor"
        );

        let metrics = create_test_metrics("to_string", 1, 0, 4);
        assert!(
            is_accessor_method(&metrics, None),
            "to_string() should be detected as accessor"
        );
    }

    #[test]
    fn test_complex_method_not_detected_as_accessor() {
        // High complexity despite accessor name
        let metrics = create_test_metrics("get_value", 5, 3, 20);
        assert!(
            !is_accessor_method(&metrics, None),
            "Complex method should not be detected as accessor"
        );
    }

    #[test]
    fn test_business_logic_not_misclassified_as_accessor() {
        // Business logic method
        let metrics = create_test_metrics("calculate_total", 4, 2, 15);
        assert!(
            !is_accessor_method(&metrics, None),
            "Business logic should not be detected as accessor"
        );
    }

    #[test]
    fn test_ast_body_validation_for_accessor() {
        use syn::parse_quote;

        let code: syn::ItemFn = parse_quote! {
            pub fn id(&self) -> u32 {
                self.id
            }
        };

        assert!(
            is_simple_accessor_body(&code),
            "Simple field access should be valid accessor body"
        );
    }

    #[test]
    fn test_accessor_side_effect_rejected() {
        use syn::parse_quote;

        let code: syn::ItemFn = parse_quote! {
            pub fn get_value(&self) -> i32 {
                self.counter.fetch_add(1, Ordering::SeqCst);
                self.value
            }
        };

        assert!(
            !is_simple_accessor_body(&code),
            "Accessor with side effects should be rejected"
        );
    }

    #[test]
    fn test_accessor_classification_integration() {
        let graph = CallGraph::new();

        // Simple accessor should be IOWrapper
        let func = create_test_metrics("id", 1, 0, 3);
        let func_id = FunctionId::new(PathBuf::from("user.rs"), "id".to_string(), 10);

        let role = classify_function_role(&func, &func_id, &graph);
        assert_eq!(
            role,
            FunctionRole::IOWrapper,
            "Simple accessor should be classified as IOWrapper"
        );
    }

    #[test]
    fn test_accessor_with_reference_return() {
        use syn::parse_quote;

        let code: syn::ItemFn = parse_quote! {
            pub fn name(&self) -> &str {
                &self.name
            }
        };

        assert!(
            is_simple_accessor_body(&code),
            "Reference to field should be valid accessor body"
        );
    }

    #[test]
    fn test_accessor_with_method_call() {
        use syn::parse_quote;

        let code: syn::ItemFn = parse_quote! {
            pub fn name(&self) -> String {
                self.name.clone()
            }
        };

        assert!(
            is_simple_accessor_body(&code),
            "Field with .clone() should be valid accessor body"
        );
    }

    #[test]
    fn test_accessor_single_word_patterns() {
        // Test all single-word patterns
        for name in &[
            "id", "name", "value", "kind", "type", "status", "code", "key", "index",
        ] {
            let metrics = create_test_metrics(name, 1, 0, 3);
            assert!(
                is_accessor_method(&metrics, None),
                "{} should be detected as accessor",
                name
            );
        }
    }

    #[test]
    fn test_accessor_prefix_patterns() {
        // Test all prefix patterns
        for (name, expected) in &[
            ("get_value", true),
            ("is_active", true),
            ("has_data", true),
            ("can_execute", true),
            ("should_run", true),
            ("as_str", true),
            ("to_string", true),
            ("into_iter", true),
        ] {
            let metrics = create_test_metrics(name, 1, 0, 5);
            assert_eq!(
                is_accessor_method(&metrics, None),
                *expected,
                "{} accessor detection mismatch",
                name
            );
        }
    }

    #[test]
    fn test_accessor_complexity_thresholds() {
        // At threshold (should pass)
        let metrics = create_test_metrics("get_value", 2, 1, 9);
        assert!(
            is_accessor_method(&metrics, None),
            "At threshold should be detected"
        );

        // Over cyclomatic threshold (should fail)
        let metrics = create_test_metrics("get_value", 3, 1, 9);
        assert!(
            !is_accessor_method(&metrics, None),
            "Over cyclomatic threshold should not be detected"
        );

        // Over cognitive threshold (should fail)
        let metrics = create_test_metrics("get_value", 2, 2, 9);
        assert!(
            !is_accessor_method(&metrics, None),
            "Over cognitive threshold should not be detected"
        );

        // Over length threshold (should fail)
        let metrics = create_test_metrics("get_value", 2, 1, 10);
        assert!(
            !is_accessor_method(&metrics, None),
            "At or over length threshold should not be detected"
        );
    }

    #[test]
    fn test_accessor_requires_immutable_self() {
        use syn::parse_quote;

        // Immutable self - should pass
        let code: syn::ItemFn = parse_quote! {
            pub fn value(&self) -> i32 {
                self.value
            }
        };

        assert!(
            is_simple_accessor_body(&code),
            "Immutable self should be valid"
        );

        // Mutable self - should fail
        let code: syn::ItemFn = parse_quote! {
            pub fn value(&mut self) -> i32 {
                self.value
            }
        };

        assert!(
            !is_simple_accessor_body(&code),
            "Mutable self should be rejected"
        );
    }

    #[test]
    fn test_accessor_precedence_in_classification() {
        use syn::parse_quote;

        let graph = CallGraph::new();

        let code: syn::ItemFn = parse_quote! {
            pub fn id(&self) -> u32 {
                self.id
            }
        };

        let func = create_test_metrics("id", 1, 0, 3);
        let func_id = FunctionId::new(PathBuf::from("test.rs"), "id".to_string(), 10);

        let role = classify_by_rules(&func, &func_id, &graph, Some(&code));
        assert_eq!(
            role,
            Some(FunctionRole::IOWrapper),
            "Accessor should be classified as IOWrapper"
        );
    }

    // Debug role detection tests (spec 119)

    #[test]
    fn test_debug_function_with_diagnostics_suffix() {
        let func = create_test_metrics("handle_call_graph_diagnostics", 5, 3, 20);
        assert!(
            is_debug_function(&func),
            "Function with _diagnostics suffix should be detected as debug"
        );
    }

    #[test]
    fn test_debug_function_with_debug_prefix() {
        let func = create_test_metrics("debug_print_info", 3, 2, 15);
        assert!(
            is_debug_function(&func),
            "Function with debug_ prefix should be detected as debug"
        );
    }

    #[test]
    fn test_print_function_detected_as_debug() {
        let func = create_test_metrics("print_statistics", 4, 3, 18);
        assert!(
            is_debug_function(&func),
            "Function with print_ prefix should be detected as debug"
        );
    }

    #[test]
    fn test_dump_function_detected_as_debug() {
        let func = create_test_metrics("dump_state", 2, 1, 10);
        assert!(
            is_debug_function(&func),
            "Function with dump_ prefix should be detected as debug"
        );
    }

    #[test]
    fn test_trace_function_detected_as_debug() {
        let func = create_test_metrics("trace_execution", 3, 2, 12);
        assert!(
            is_debug_function(&func),
            "Function with trace_ prefix should be detected as debug"
        );
    }

    #[test]
    fn test_high_complexity_debug_name_not_debug() {
        // High complexity should prevent misclassification
        let func = create_test_metrics("debug_complex_algorithm", 15, 12, 50);
        assert!(
            !is_debug_function(&func),
            "High complexity function should not be classified as debug even with debug name"
        );
    }

    #[test]
    fn test_debug_stats_function() {
        let func = create_test_metrics("calculate_stats", 4, 3, 20);
        assert!(
            is_debug_function(&func),
            "Function ending with _stats should be detected as debug"
        );
    }

    #[test]
    fn test_debug_classification_integration() {
        let graph = CallGraph::new();
        // Use a function that clearly matches debug pattern without entry point pattern
        let func = create_test_metrics("print_call_graph_diagnostics", 5, 3, 20);
        let func_id = FunctionId::new(
            PathBuf::from("commands/analyze.rs"),
            "print_call_graph_diagnostics".to_string(),
            396,
        );

        let role = classify_function_role(&func, &func_id, &graph);
        assert_eq!(
            role,
            FunctionRole::Debug,
            "Diagnostic function should be classified as Debug role"
        );
    }

    #[test]
    fn test_entry_point_takes_precedence_over_debug() {
        let graph = CallGraph::new();
        // Function with both entry point and debug patterns - entry point wins
        let func = create_test_metrics("handle_diagnostics", 5, 3, 20);
        let func_id = FunctionId::new(
            PathBuf::from("commands/analyze.rs"),
            "handle_diagnostics".to_string(),
            396,
        );

        let role = classify_function_role(&func, &func_id, &graph);
        assert_eq!(
            role,
            FunctionRole::EntryPoint,
            "Entry point pattern should take precedence over debug pattern"
        );
    }

    #[test]
    fn test_business_logic_not_misclassified_as_debug() {
        let func = create_test_metrics("calculate_complexity", 8, 5, 30);
        assert!(
            !is_debug_function(&func),
            "Business logic function should not be detected as debug"
        );
    }

    #[test]
    fn test_debug_role_multiplier() {
        let multiplier = get_role_multiplier(FunctionRole::Debug);
        assert_eq!(
            multiplier, 0.3,
            "Debug role should have lowest multiplier (0.3)"
        );
    }

    #[test]
    fn test_matches_debug_pattern() {
        assert!(matches_debug_pattern("debug_foo"));
        assert!(matches_debug_pattern("print_bar"));
        assert!(matches_debug_pattern("dump_state"));
        assert!(matches_debug_pattern("trace_execution"));
        assert!(matches_debug_pattern("foo_diagnostics"));
        assert!(matches_debug_pattern("bar_debug"));
        assert!(matches_debug_pattern("baz_stats"));
        assert!(matches_debug_pattern("test_diagnostics_helper"));

        // Should not match
        assert!(!matches_debug_pattern("calculate"));
        assert!(!matches_debug_pattern("process"));
        assert!(!matches_debug_pattern("validate"));
    }

    #[test]
    fn test_diagnostic_characteristics() {
        // Should match: simple output function
        let func = create_test_metrics("print_output", 3, 2, 15);
        assert!(
            has_diagnostic_characteristics(&func),
            "Simple print function should have diagnostic characteristics"
        );

        // Should not match: complex function
        let func = create_test_metrics("print_complex", 10, 8, 40);
        assert!(
            !has_diagnostic_characteristics(&func),
            "Complex function should not have diagnostic characteristics"
        );

        // Should not match: no output I/O pattern
        let func = create_test_metrics("calculate_total", 3, 2, 15);
        assert!(
            !has_diagnostic_characteristics(&func),
            "Non-output function should not have diagnostic characteristics"
        );

        // Should not match: read/write operations (not diagnostic output)
        let func = create_test_metrics("read_file", 3, 2, 15);
        assert!(
            !has_diagnostic_characteristics(&func),
            "Read/write operations should not have diagnostic characteristics"
        );
    }
}
