//! Coverage tests for tools handlers
//! Extracted for file health compliance (CB-040)

use super::*;
use crate::models::churn::{ChurnSummary, CodeChurnAnalysis, FileChurnMetrics};
use crate::models::dead_code::{
    ConfidenceLevel, DeadCodeItem, DeadCodeRankingResult, DeadCodeSummary, DeadCodeType,
    FileDeadCodeMetrics,
};
use crate::models::defect_prediction::{DefectPredictionResult, DefectProbability, DefectRiskLevel};
use crate::models::tdg::{TDGComponents, TDGScore, TDGSeverity};
use serde_json::json;
use std::path::PathBuf;


    #[test]
    fn test_is_template_tool_generate() {
        assert!(is_template_tool("generate_template"));
    }

    #[test]
    fn test_is_template_tool_list() {
        assert!(is_template_tool("list_templates"));
    }

    #[test]
    fn test_is_template_tool_validate() {
        assert!(is_template_tool("validate_template"));
    }

    #[test]
    fn test_is_template_tool_scaffold() {
        assert!(is_template_tool("scaffold_project"));
    }

    #[test]
    fn test_is_template_tool_search() {
        assert!(is_template_tool("search_templates"));
    }

    #[test]
    fn test_is_template_tool_false() {
        assert!(!is_template_tool("analyze_complexity"));
        assert!(!is_template_tool("unknown_tool"));
        assert!(!is_template_tool(""));
    }

    // Tests for is_analysis_tool()

    #[test]
    fn test_is_analysis_tool_churn() {
        assert!(is_analysis_tool("analyze_code_churn"));
    }

    #[test]
    fn test_is_analysis_tool_complexity() {
        assert!(is_analysis_tool("analyze_complexity"));
    }

    #[test]
    fn test_is_analysis_tool_dag() {
        assert!(is_analysis_tool("analyze_dag"));
    }

    #[test]
    fn test_is_analysis_tool_context() {
        assert!(is_analysis_tool("generate_context"));
    }

    #[test]
    fn test_is_analysis_tool_architecture() {
        assert!(is_analysis_tool("analyze_system_architecture"));
    }

    #[test]
    fn test_is_analysis_tool_defect() {
        assert!(is_analysis_tool("analyze_defect_probability"));
    }

    #[test]
    fn test_is_analysis_tool_dead_code() {
        assert!(is_analysis_tool("analyze_dead_code"));
    }

    #[test]
    fn test_is_analysis_tool_deep_context() {
        assert!(is_analysis_tool("analyze_deep_context"));
    }

    #[test]
    fn test_is_analysis_tool_tdg() {
        assert!(is_analysis_tool("analyze_tdg"));
    }

    #[test]
    fn test_is_analysis_tool_makefile() {
        assert!(is_analysis_tool("analyze_makefile_lint"));
    }

    #[test]
    fn test_is_analysis_tool_provability() {
        assert!(is_analysis_tool("analyze_provability"));
    }

    #[test]
    fn test_is_analysis_tool_satd() {
        assert!(is_analysis_tool("analyze_satd"));
    }

    #[test]
    fn test_is_analysis_tool_qdd() {
        assert!(is_analysis_tool("quality_driven_development"));
    }

    #[test]
    fn test_is_analysis_tool_lint_hotspot() {
        assert!(is_analysis_tool("analyze_lint_hotspot"));
    }

    #[test]
    fn test_is_analysis_tool_false() {
        assert!(!is_analysis_tool("generate_template"));
        assert!(!is_analysis_tool("unknown_tool"));
        assert!(!is_analysis_tool(""));
    }

    // Tests for format_churn_summary()

    fn create_test_churn_analysis() -> CodeChurnAnalysis {
        CodeChurnAnalysis {
            generated_at: Utc::now(),
            period_days: 30,
            repository_root: PathBuf::from("/test/repo"),
            files: vec![FileChurnMetrics {
                path: PathBuf::from("src/main.rs"),
                relative_path: "src/main.rs".to_string(),
                commit_count: 15,
                unique_authors: vec!["alice".to_string(), "bob".to_string()],
                additions: 200,
                deletions: 50,
                churn_score: 0.8,
                last_modified: Utc::now(),
                first_seen: Utc::now(),
            }],
            summary: ChurnSummary {
                total_commits: 50,
                total_files_changed: 25,
                hotspot_files: vec![PathBuf::from("src/hot.rs")],
                stable_files: vec![PathBuf::from("src/stable.rs")],
                author_contributions: HashMap::from([
                    ("alice".to_string(), 30),
                    ("bob".to_string(), 20),
                ]),
                mean_churn_score: 0.5,
                variance_churn_score: 0.1,
                stddev_churn_score: 0.316,
            },
        }
    }

    #[test]
    fn test_format_churn_summary_basic() {
        let analysis = create_test_churn_analysis();
        let summary = format_churn_summary(&analysis);

        assert!(summary.contains("# Code Churn Analysis"));
        assert!(summary.contains("Period: 30 days"));
        assert!(summary.contains("Total files changed: 25"));
        assert!(summary.contains("Total commits: 50"));
    }

    #[test]
    fn test_format_churn_summary_hotspots() {
        let analysis = create_test_churn_analysis();
        let summary = format_churn_summary(&analysis);

        assert!(summary.contains("## Hotspot Files"));
        assert!(summary.contains("src/hot.rs"));
    }

    #[test]
    fn test_format_churn_summary_stable() {
        let analysis = create_test_churn_analysis();
        let summary = format_churn_summary(&analysis);

        assert!(summary.contains("## Stable Files"));
        assert!(summary.contains("src/stable.rs"));
    }

    #[test]
    fn test_format_churn_summary_empty() {
        let analysis = CodeChurnAnalysis {
            generated_at: Utc::now(),
            period_days: 7,
            repository_root: PathBuf::from("/test"),
            files: vec![],
            summary: ChurnSummary {
                total_commits: 0,
                total_files_changed: 0,
                hotspot_files: vec![],
                stable_files: vec![],
                author_contributions: HashMap::new(),
                mean_churn_score: 0.0,
                variance_churn_score: 0.0,
                stddev_churn_score: 0.0,
            },
        };

        let summary = format_churn_summary(&analysis);
        assert!(summary.contains("# Code Churn Analysis"));
        assert!(summary.contains("Period: 7 days"));
        // Should not contain hotspot/stable sections when empty
        assert!(!summary.contains("## Hotspot Files"));
        assert!(!summary.contains("## Stable Files"));
    }

    // Tests for format_churn_as_markdown()

    #[test]
    fn test_format_churn_as_markdown_basic() {
        let analysis = create_test_churn_analysis();
        let markdown = format_churn_as_markdown(&analysis);

        assert!(markdown.contains("# Code Churn Analysis Report"));
        assert!(markdown.contains("**Period:** 30 days"));
        assert!(markdown.contains("**Repository:**"));
    }

    #[test]
    fn test_format_churn_as_markdown_summary_section() {
        let analysis = create_test_churn_analysis();
        let markdown = format_churn_as_markdown(&analysis);

        assert!(markdown.contains("## Summary"));
        assert!(markdown.contains("Total files changed: 25"));
        assert!(markdown.contains("Total commits: 50"));
    }

    // Tests for format_churn_as_csv()

    #[test]
    fn test_format_churn_as_csv_headers() {
        let analysis = create_test_churn_analysis();
        let csv = format_churn_as_csv(&analysis);

        // Check that there's a header line
        assert!(csv.lines().next().is_some());
    }

    #[test]
    fn test_format_churn_as_csv_data() {
        let analysis = create_test_churn_analysis();
        let csv = format_churn_as_csv(&analysis);

        // Check that it contains the file path
        assert!(csv.contains("src/main.rs") || csv.contains("main.rs"));
    }

    #[test]
    fn test_format_churn_as_csv_empty() {
        let analysis = CodeChurnAnalysis {
            generated_at: Utc::now(),
            period_days: 7,
            repository_root: PathBuf::from("/test"),
            files: vec![],
            summary: ChurnSummary {
                total_commits: 0,
                total_files_changed: 0,
                hotspot_files: vec![],
                stable_files: vec![],
                author_contributions: HashMap::new(),
                mean_churn_score: 0.0,
                variance_churn_score: 0.0,
                stddev_churn_score: 0.0,
            },
        };

        let csv = format_churn_as_csv(&analysis);
        // Should have header but no data rows
        let lines: Vec<_> = csv.lines().collect();
        assert_eq!(lines.len(), 1); // Only header
    }

    // Tests for tool name categorization (both functions together)

    #[test]
    fn test_tools_are_mutually_exclusive() {
        // Template tools should not be analysis tools and vice versa
        let template_tools = [
            "generate_template",
            "list_templates",
            "validate_template",
            "scaffold_project",
            "search_templates",
        ];
        let analysis_tools = [
            "analyze_code_churn",
            "analyze_complexity",
            "analyze_dag",
            "generate_context",
            "analyze_system_architecture",
            "analyze_defect_probability",
            "analyze_dead_code",
            "analyze_deep_context",
            "analyze_tdg",
            "analyze_makefile_lint",
            "analyze_provability",
            "analyze_satd",
            "quality_driven_development",
            "analyze_lint_hotspot",
        ];

        for tool in template_tools {
            assert!(is_template_tool(tool), "{} should be template tool", tool);
            assert!(
                !is_analysis_tool(tool),
                "{} should NOT be analysis tool",
                tool
            );
        }

        for tool in analysis_tools {
            assert!(is_analysis_tool(tool), "{} should be analysis tool", tool);
            assert!(
                !is_template_tool(tool),
                "{} should NOT be template tool",
                tool
            );
        }
    }

    // Tests for get_template_variant()

    #[test]
    fn test_get_template_variant_makefile_rust() {
        assert_eq!(get_template_variant("makefile", "rust"), Some("cli"));
    }

    #[test]
    fn test_get_template_variant_makefile_deno() {
        assert_eq!(get_template_variant("makefile", "deno"), Some("cli"));
    }

    #[test]
    fn test_get_template_variant_makefile_python() {
        assert_eq!(get_template_variant("makefile", "python-uv"), Some("cli"));
    }

    #[test]
    fn test_get_template_variant_readme_rust() {
        assert_eq!(get_template_variant("readme", "rust"), Some("cli"));
    }

    #[test]
    fn test_get_template_variant_gitignore_rust() {
        assert_eq!(get_template_variant("gitignore", "rust"), Some("cli"));
    }

    #[test]
    fn test_get_template_variant_unknown_template() {
        assert_eq!(get_template_variant("unknown", "rust"), None);
    }

    #[test]
    fn test_get_template_variant_unknown_toolchain() {
        assert_eq!(get_template_variant("makefile", "java"), None);
    }

    // Tests for calculate_relevance()

    fn create_test_template_resource(name: &str, desc: &str) -> TemplateResource {
        TemplateResource {
            uri: format!("template://{}", name),
            name: name.to_string(),
            description: desc.to_string(),
            category: "test".to_string(),
            toolchain: "rust".to_string(),
            mime_type: "text/plain".to_string(),
            parameters: vec![ParameterSpec { param_type: crate::models::template::ParameterType::String,
                name: "project_name".to_string(),
                description: "Project name".to_string(),
                required: true,
                default_value: None,
                validation_pattern: None,
            }],
        }
    }

    #[test]
    fn test_calculate_relevance_exact_name_match() {
        let template = create_test_template_resource("makefile", "A makefile template");
        let score = calculate_relevance(&template, "makefile");
        assert!(score >= 10.0, "Exact match should score at least 10");
    }

    #[test]
    fn test_calculate_relevance_partial_name_match() {
        let template = create_test_template_resource("makefile-rust", "A makefile template");
        let score = calculate_relevance(&template, "make");
        assert!(score >= 5.0, "Partial name match should score at least 5");
    }

    #[test]
    fn test_calculate_relevance_description_match() {
        let template = create_test_template_resource("some_template", "A testing framework setup");
        let score = calculate_relevance(&template, "testing");
        assert!(score >= 3.0, "Description match should score at least 3");
    }

    #[test]
    fn test_calculate_relevance_no_match() {
        let template = create_test_template_resource("makefile", "Build configuration");
        let score = calculate_relevance(&template, "xyz123");
        assert_eq!(score, 0.0, "No match should score 0");
    }

    // Tests for resolve_project_path() and related path functions

    #[test]
    fn test_resolve_project_path_with_explicit_path() {
        let path = resolve_project_path(&Some("/custom/path".to_string()));
        assert_eq!(path, PathBuf::from("/custom/path"));
    }

    #[test]
    fn test_resolve_project_path_none() {
        let path = resolve_project_path(&None);
        // Should return current dir or fallback
        assert!(!path.as_os_str().is_empty());
    }

    #[test]
    fn test_resolve_project_path_complexity_with_path() {
        let path = resolve_project_path_complexity(Some("/my/project".to_string()));
        assert_eq!(path, PathBuf::from("/my/project"));
    }

    #[test]
    fn test_resolve_project_path_complexity_none() {
        let path = resolve_project_path_complexity(None);
        assert!(!path.as_os_str().is_empty());
    }

    #[test]
    fn test_resolve_deep_context_project_path_some() {
        let path = resolve_deep_context_project_path(Some("/deep/context".to_string()));
        assert_eq!(path, PathBuf::from("/deep/context"));
    }

    #[test]
    fn test_resolve_deep_context_project_path_none() {
        let path = resolve_deep_context_project_path(None);
        assert!(!path.as_os_str().is_empty());
    }

    // Tests for detect_toolchain()

    #[test]
    fn test_detect_toolchain_explicit_rust() {
        let toolchain = detect_toolchain(&Some("rust".to_string()), Path::new("/tmp"));
        assert_eq!(toolchain, "rust");
    }

    #[test]
    fn test_detect_toolchain_explicit_deno() {
        let toolchain = detect_toolchain(&Some("deno".to_string()), Path::new("/tmp"));
        assert_eq!(toolchain, "deno");
    }

    #[test]
    fn test_detect_toolchain_explicit_python() {
        let toolchain = detect_toolchain(&Some("python-uv".to_string()), Path::new("/tmp"));
        assert_eq!(toolchain, "python-uv");
    }

    #[test]
    fn test_detect_toolchain_default_rust() {
        // When no files exist and no explicit toolchain, defaults to rust
        let toolchain = detect_toolchain(&None, Path::new("/nonexistent/path"));
        assert_eq!(toolchain, "rust");
    }

    // Tests for should_analyze_file()

    #[test]
    fn test_should_analyze_file_rust() {
        assert!(should_analyze_file(Path::new("src/main.rs"), "rust"));
        assert!(!should_analyze_file(Path::new("src/main.py"), "rust"));
        assert!(!should_analyze_file(Path::new("src/main.ts"), "rust"));
    }

    #[test]
    fn test_should_analyze_file_deno() {
        assert!(should_analyze_file(Path::new("src/main.ts"), "deno"));
        assert!(should_analyze_file(Path::new("src/main.tsx"), "deno"));
        assert!(should_analyze_file(Path::new("src/main.js"), "deno"));
        assert!(should_analyze_file(Path::new("src/main.jsx"), "deno"));
        assert!(!should_analyze_file(Path::new("src/main.rs"), "deno"));
    }

    #[test]
    fn test_should_analyze_file_python() {
        assert!(should_analyze_file(Path::new("src/main.py"), "python-uv"));
        assert!(!should_analyze_file(Path::new("src/main.rs"), "python-uv"));
    }

    #[test]
    fn test_should_analyze_file_unknown_toolchain() {
        assert!(!should_analyze_file(Path::new("src/main.rs"), "unknown"));
    }

    // Tests for matches_include_filters() and matches_pattern()

    #[test]
    fn test_matches_include_filters_none() {
        // No patterns means everything matches
        assert!(matches_include_filters(Path::new("src/main.rs"), &None));
    }

    #[test]
    fn test_matches_include_filters_empty_vec() {
        // Empty patterns means everything matches
        assert!(matches_include_filters(
            Path::new("src/main.rs"),
            &Some(vec![])
        ));
    }

    #[test]
    fn test_matches_include_filters_matching_pattern() {
        let patterns = Some(vec!["*.rs".to_string()]);
        assert!(matches_include_filters(Path::new("src/main.rs"), &patterns));
    }

    #[test]
    fn test_matches_include_filters_non_matching() {
        let patterns = Some(vec!["*.py".to_string()]);
        assert!(!matches_include_filters(
            Path::new("src/main.rs"),
            &patterns
        ));
    }

    #[test]
    fn test_matches_pattern_extension() {
        assert!(matches_pattern("src/main.rs", "*.rs"));
        assert!(!matches_pattern("src/main.py", "*.rs"));
    }

    #[test]
    fn test_matches_pattern_glob_star() {
        assert!(matches_pattern("src/lib/module.rs", "**/module.rs"));
        assert!(matches_pattern("deep/nested/module.rs", "**/module.rs"));
    }

    #[test]
    fn test_matches_pattern_substring() {
        assert!(matches_pattern("src/main.rs", "main"));
        assert!(matches_pattern("src/main_test.rs", "test"));
        assert!(!matches_pattern("src/lib.rs", "main"));
    }

    // Tests for build_complexity_thresholds()

    #[test]
    fn test_build_complexity_thresholds_defaults() {
        let args = AnalyzeComplexityArgs {
            project_path: None,
            toolchain: None,
            format: None,
            max_cyclomatic: None,
            max_cognitive: None,
            include: None,
            top_files: None,
        };
        let thresholds = build_complexity_thresholds(&args);
        // Default thresholds should be reasonable values
        assert!(thresholds.cyclomatic_error > 0);
        assert!(thresholds.cognitive_error > 0);
    }

    #[test]
    fn test_build_complexity_thresholds_custom_cyclomatic() {
        let args = AnalyzeComplexityArgs {
            project_path: None,
            toolchain: None,
            format: None,
            max_cyclomatic: Some(20),
            max_cognitive: None,
            include: None,
            top_files: None,
        };
        let thresholds = build_complexity_thresholds(&args);
        assert_eq!(thresholds.cyclomatic_error, 20);
        // Warning should be 3/4 of error threshold
        assert_eq!(thresholds.cyclomatic_warn, 15);
    }

    #[test]
    fn test_build_complexity_thresholds_custom_cognitive() {
        let args = AnalyzeComplexityArgs {
            project_path: None,
            toolchain: None,
            format: None,
            max_cyclomatic: None,
            max_cognitive: Some(30),
            include: None,
            top_files: None,
        };
        let thresholds = build_complexity_thresholds(&args);
        assert_eq!(thresholds.cognitive_error, 30);
        // Warning should be 3/4 of error threshold
        assert_eq!(thresholds.cognitive_warn, 22);
    }

    // Tests for parse_dag_type()

    #[test]
    fn test_parse_dag_type_call_graph() {
        let dag_type = parse_dag_type(Some("call-graph"));
        assert!(matches!(dag_type, crate::cli::DagType::CallGraph));
    }

    #[test]
    fn test_parse_dag_type_import_graph() {
        let dag_type = parse_dag_type(Some("import-graph"));
        assert!(matches!(dag_type, crate::cli::DagType::ImportGraph));
    }

    #[test]
    fn test_parse_dag_type_inheritance() {
        let dag_type = parse_dag_type(Some("inheritance"));
        assert!(matches!(dag_type, crate::cli::DagType::Inheritance));
    }

    #[test]
    fn test_parse_dag_type_full_dependency() {
        let dag_type = parse_dag_type(Some("full-dependency"));
        assert!(matches!(dag_type, crate::cli::DagType::FullDependency));
    }

    #[test]
    fn test_parse_dag_type_default() {
        let dag_type = parse_dag_type(None);
        assert!(matches!(dag_type, crate::cli::DagType::CallGraph));
    }

    #[test]
    fn test_parse_dag_type_unknown() {
        let dag_type = parse_dag_type(Some("unknown"));
        assert!(matches!(dag_type, crate::cli::DagType::CallGraph));
    }

    // Tests for parse_deep_context_dag_type()

    #[test]
    fn test_parse_deep_context_dag_type_call_graph() {
        let dag_type = parse_deep_context_dag_type(Some("call-graph".to_string()));
        assert!(matches!(
            dag_type,
            crate::services::deep_context::DagType::CallGraph
        ));
    }

    #[test]
    fn test_parse_deep_context_dag_type_import() {
        let dag_type = parse_deep_context_dag_type(Some("import-graph".to_string()));
        assert!(matches!(
            dag_type,
            crate::services::deep_context::DagType::ImportGraph
        ));
    }

    #[test]
    fn test_parse_deep_context_dag_type_inheritance() {
        let dag_type = parse_deep_context_dag_type(Some("inheritance".to_string()));
        assert!(matches!(
            dag_type,
            crate::services::deep_context::DagType::Inheritance
        ));
    }

    #[test]
    fn test_parse_deep_context_dag_type_full() {
        let dag_type = parse_deep_context_dag_type(Some("full-dependency".to_string()));
        assert!(matches!(
            dag_type,
            crate::services::deep_context::DagType::FullDependency
        ));
    }

    #[test]
    fn test_parse_deep_context_dag_type_default() {
        let dag_type = parse_deep_context_dag_type(None);
        assert!(matches!(
            dag_type,
            crate::services::deep_context::DagType::CallGraph
        ));
    }

    // Tests for parse_cache_strategy()

    #[test]
    fn test_parse_cache_strategy_normal() {
        let strategy = parse_cache_strategy(Some("normal".to_string()));
        assert!(matches!(
            strategy,
            crate::services::deep_context::CacheStrategy::Normal
        ));
    }

    #[test]
    fn test_parse_cache_strategy_force_refresh() {
        let strategy = parse_cache_strategy(Some("force-refresh".to_string()));
        assert!(matches!(
            strategy,
            crate::services::deep_context::CacheStrategy::ForceRefresh
        ));
    }

    #[test]
    fn test_parse_cache_strategy_offline() {
        let strategy = parse_cache_strategy(Some("offline".to_string()));
        assert!(matches!(
            strategy,
            crate::services::deep_context::CacheStrategy::Offline
        ));
    }

    #[test]
    fn test_parse_cache_strategy_default() {
        let strategy = parse_cache_strategy(None);
        assert!(matches!(
            strategy,
            crate::services::deep_context::CacheStrategy::Normal
        ));
    }

    // Tests for parse_analysis_type_string() and parse_analysis_types()

    #[test]
    fn test_parse_analysis_type_string_ast() {
        let analysis_type = parse_analysis_type_string("ast");
        assert!(matches!(
            analysis_type,
            Some(crate::services::deep_context::AnalysisType::Ast)
        ));
    }

    #[test]
    fn test_parse_analysis_type_string_complexity() {
        let analysis_type = parse_analysis_type_string("complexity");
        assert!(matches!(
            analysis_type,
            Some(crate::services::deep_context::AnalysisType::Complexity)
        ));
    }

    #[test]
    fn test_parse_analysis_type_string_churn() {
        let analysis_type = parse_analysis_type_string("churn");
        assert!(matches!(
            analysis_type,
            Some(crate::services::deep_context::AnalysisType::Churn)
        ));
    }

    #[test]
    fn test_parse_analysis_type_string_dag() {
        let analysis_type = parse_analysis_type_string("dag");
        assert!(matches!(
            analysis_type,
            Some(crate::services::deep_context::AnalysisType::Dag)
        ));
    }

    #[test]
    fn test_parse_analysis_type_string_dead_code() {
        let analysis_type = parse_analysis_type_string("dead_code");
        assert!(matches!(
            analysis_type,
            Some(crate::services::deep_context::AnalysisType::DeadCode)
        ));
    }

    #[test]
    fn test_parse_analysis_type_string_satd() {
        let analysis_type = parse_analysis_type_string("satd");
        assert!(matches!(
            analysis_type,
            Some(crate::services::deep_context::AnalysisType::Satd)
        ));
    }

    #[test]
    fn test_parse_analysis_type_string_tdg() {
        let analysis_type = parse_analysis_type_string("tdg");
        assert!(matches!(
            analysis_type,
            Some(crate::services::deep_context::AnalysisType::TechnicalDebtGradient)
        ));
    }

    #[test]
    fn test_parse_analysis_type_string_unknown() {
        let analysis_type = parse_analysis_type_string("unknown");
        assert!(analysis_type.is_none());
    }

    #[test]
    fn test_parse_analysis_types_some() {
        let types = parse_analysis_types(Some(vec!["ast".to_string(), "complexity".to_string()]));
        assert_eq!(types.len(), 2);
    }

    #[test]
    fn test_parse_analysis_types_none() {
        let types = parse_analysis_types(None);
        // Default types should include ast, complexity, churn
        assert!(!types.is_empty());
    }

    #[test]
    fn test_get_default_analysis_types() {
        let types = get_default_analysis_types();
        assert_eq!(types.len(), 3);
    }

    // Tests for calculate_* functions

    #[test]
    fn test_calculate_cyclomatic_complexity_simple() {
        let content = "fn main() {}";
        let complexity = calculate_cyclomatic_complexity(content);
        assert_eq!(complexity, 1); // Base complexity is 1
    }

    #[test]
    fn test_calculate_cyclomatic_complexity_with_if() {
        let content = "fn main() { if true {} }";
        let complexity = calculate_cyclomatic_complexity(content);
        assert!(complexity >= 2); // Base + if
    }

    #[test]
    fn test_calculate_cyclomatic_complexity_with_loops() {
        let content = "fn main() { for i in 0..10 {} while true {} }";
        let complexity = calculate_cyclomatic_complexity(content);
        assert!(complexity >= 3); // Base + for + while
    }

    #[test]
    fn test_calculate_cognitive_complexity() {
        // Cognitive is 1.5x cyclomatic
        assert_eq!(calculate_cognitive_complexity(10), 15);
        assert_eq!(calculate_cognitive_complexity(4), 6);
        assert_eq!(calculate_cognitive_complexity(1), 1);
    }

    #[test]
    fn test_calculate_duplicate_ratio_no_duplicates() {
        let lines = vec!["line1", "line2", "line3"];
        let ratio = calculate_duplicate_ratio(&lines);
        assert_eq!(ratio, 0.0);
    }

    #[test]
    fn test_calculate_duplicate_ratio_with_duplicates() {
        let lines = vec!["line1", "line1", "line2"];
        let ratio = calculate_duplicate_ratio(&lines);
        assert!(ratio > 0.0);
    }

    #[test]
    fn test_calculate_duplicate_ratio_empty() {
        let lines: Vec<&str> = vec![];
        let ratio = calculate_duplicate_ratio(&lines);
        assert_eq!(ratio, 0.0);
    }

    #[test]
    fn test_calculate_duplicate_ratio_skips_comments() {
        let lines = vec!["// comment", "// comment", "code"];
        let ratio = calculate_duplicate_ratio(&lines);
        // Comments should be skipped
        assert_eq!(ratio, 0.0);
    }

    #[test]
    fn test_calculate_efferent_coupling() {
        let content = "use std::io;\nuse std::path::Path;\nfn main() {}";
        let coupling = calculate_efferent_coupling(content);
        assert_eq!(coupling, 2.0);
    }

    #[test]
    fn test_calculate_efferent_coupling_none() {
        let content = "fn main() {}";
        let coupling = calculate_efferent_coupling(content);
        assert_eq!(coupling, 0.0);
    }

    #[test]
    fn test_calculate_afferent_coupling() {
        let content = "pub fn foo() {}\npub struct Bar {}\nfn private() {}";
        let coupling = calculate_afferent_coupling(content);
        assert_eq!(coupling, 2.0); // pub fn + pub struct
    }

    #[test]
    fn test_calculate_afferent_coupling_none() {
        let content = "fn foo() {}\nstruct Bar {}";
        let coupling = calculate_afferent_coupling(content);
        assert_eq!(coupling, 0.0);
    }

    #[test]
    fn test_is_public_declaration() {
        assert!(is_public_declaration("pub fn foo() {}"));
        assert!(is_public_declaration("pub struct Bar {}"));
        assert!(is_public_declaration("pub enum Baz {}"));
        assert!(is_public_declaration("pub trait Qux {}"));
        assert!(is_public_declaration("pub mod module;"));
        assert!(!is_public_declaration("fn foo() {}"));
        assert!(!is_public_declaration("struct Bar {}"));
    }

    #[test]
    fn test_get_churn_score_found() {
        let mut map = HashMap::new();
        map.insert("src/main.rs".to_string(), 0.75);
        let score = get_churn_score("src/main.rs", &map);
        assert_eq!(score, 0.75);
    }

    #[test]
    fn test_get_churn_score_not_found() {
        let map = HashMap::new();
        let score = get_churn_score("src/main.rs", &map);
        assert_eq!(score, 0.1); // Default
    }

    #[test]
    fn test_get_relative_path() {
        let path = Path::new("/project/src/main.rs");
        let project_path = Path::new("/project");
        let relative = get_relative_path(path, project_path);
        assert_eq!(relative, "src/main.rs");
    }

    #[test]
    fn test_get_relative_path_no_prefix() {
        let path = Path::new("/other/src/main.rs");
        let project_path = Path::new("/project");
        let relative = get_relative_path(path, project_path);
        // Should return the full path when not a prefix
        assert!(relative.contains("main.rs"));
    }

    // Tests for calculate_percentage()

    #[test]
    fn test_calculate_percentage_normal() {
        assert!((calculate_percentage(50, 100) - 50.0).abs() < f64::EPSILON);
        assert!((calculate_percentage(25, 100) - 25.0).abs() < f64::EPSILON);
    }

    #[test]
    fn test_calculate_percentage_zero_total() {
        assert_eq!(calculate_percentage(10, 0), 0.0);
    }

    #[test]
    fn test_calculate_percentage_all() {
        assert!((calculate_percentage(100, 100) - 100.0).abs() < f64::EPSILON);
    }

    // Tests for default_* functions

    #[test]
    fn test_default_project_path() {
        assert_eq!(default_project_path(), ".");
    }

    #[test]
    fn test_default_top_files() {
        assert_eq!(default_top_files(), 10);
    }

    #[test]
    fn test_default_min_violations() {
        assert_eq!(default_min_violations(), 1);
    }

    #[test]
    fn test_default_table_format() {
        assert_eq!(default_table_format(), "table");
    }

    #[test]
    fn test_default_true() {
        assert!(default_true());
    }

    #[test]
    fn test_default_summary_format() {
        assert_eq!(default_summary_format(), "summary");
    }

    // Tests for TDG formatting functions

    fn create_test_tdg_summary() -> TDGSummary {
        TDGSummary {
            total_files: 100,
            critical_files: 5,
            warning_files: 15,
            average_tdg: 1.5,
            p95_tdg: 2.8,
            p99_tdg: 3.5,
            estimated_debt_hours: 120.0,
            hotspots: vec![crate::models::tdg::TDGHotspot {
                path: "src/complex.rs".to_string(),
                tdg_score: 3.2,
                primary_factor: "High cyclomatic complexity".to_string(),
                estimated_hours: 8.0,
            }],
        }
    }

    #[test]
    fn test_format_tdg_summary_basic() {
        let summary = create_test_tdg_summary();
        let output = format_tdg_summary(&summary);

        assert!(output.contains("# Technical Debt Gradient Analysis"));
        assert!(output.contains("**Total files:** 100"));
    }

    #[test]
    fn test_format_tdg_summary_metrics() {
        let summary = create_test_tdg_summary();
        let output = format_tdg_summary(&summary);

        assert!(output.contains("**Average TDG:**"));
        assert!(output.contains("**95th percentile TDG:**"));
        assert!(output.contains("**99th percentile TDG:**"));
    }

    #[test]
    fn test_format_tdg_summary_hotspots() {
        let summary = create_test_tdg_summary();
        let output = format_tdg_summary(&summary);

        assert!(output.contains("## Top Hotspots"));
        assert!(output.contains("src/complex.rs"));
    }

    #[test]
    fn test_format_tdg_summary_severity() {
        let summary = create_test_tdg_summary();
        let output = format_tdg_summary(&summary);

        assert!(output.contains("## Severity Distribution"));
        assert!(output.contains("Critical"));
        assert!(output.contains("Warning"));
        assert!(output.contains("Normal"));
    }

    // Tests for dead code formatting functions

    fn create_test_dead_code_result() -> DeadCodeRankingResult {
        DeadCodeRankingResult {
            ranked_files: vec![FileDeadCodeMetrics {
                path: "src/unused.rs".to_string(),
                dead_lines: 50,
                total_lines: 200,
                dead_percentage: 25.0,
                dead_functions: 3,
                dead_classes: 1,
                dead_score: 75.0,
                confidence: ConfidenceLevel::High,
                items: vec![DeadCodeItem {
                    item_type: DeadCodeType::Function,
                    name: "unused_fn".to_string(),
                    line: 10,
                    end_line: 20,
                    reason: "Never called".to_string(),
                    confidence: ConfidenceLevel::High,
                }],
            }],
            summary: DeadCodeSummary {
                total_files_analyzed: 50,
                files_with_dead_code: 10,
                total_dead_lines: 200,
                dead_percentage: 4.0,
                dead_functions: 15,
                dead_classes: 3,
                dead_modules: 1,
                unreachable_blocks: 5,
            },
            analysis_timestamp: Utc::now(),
        }
    }

    #[test]
    fn test_format_dead_code_summary_mcp() {
        let result = create_test_dead_code_result();
        let output = format_dead_code_summary_mcp(&result).unwrap();

        assert!(output.contains("# Dead Code Analysis Summary"));
        assert!(output.contains("**Total files analyzed:**"));
    }

    #[test]
    fn test_format_dead_code_as_sarif_mcp() {
        let result = create_test_dead_code_result();
        let output = format_dead_code_as_sarif_mcp(&result).unwrap();

        assert!(output.contains("$schema"));
        assert!(output.contains("sarif"));
        assert!(output.contains("pmat"));
    }

    #[test]
    fn test_format_dead_code_as_markdown_mcp() {
        let result = create_test_dead_code_result();
        let output = format_dead_code_as_markdown_mcp(&result).unwrap();

        assert!(output.contains("# Dead Code Analysis Report"));
        assert!(output.contains("## Summary"));
    }

    #[test]
    fn test_get_confidence_level_text() {
        assert_eq!(get_confidence_level_text(ConfidenceLevel::High), "HIGH ");
        assert_eq!(
            get_confidence_level_text(ConfidenceLevel::Medium),
            "MEDIUM "
        );
        assert_eq!(get_confidence_level_text(ConfidenceLevel::Low), "LOW ");
    }

    #[test]
    fn test_format_confidence_emoji() {
        assert!(format_confidence_emoji(ConfidenceLevel::High).contains("High"));
        assert!(format_confidence_emoji(ConfidenceLevel::Medium).contains("Medium"));
        assert!(format_confidence_emoji(ConfidenceLevel::Low).contains("Low"));
    }

    #[test]
    fn test_calculate_dead_files_percentage_normal() {
        let summary = DeadCodeSummary {
            total_files_analyzed: 100,
            files_with_dead_code: 25,
            total_dead_lines: 0,
            dead_percentage: 0.0,
            dead_functions: 0,
            dead_classes: 0,
            dead_modules: 0,
            unreachable_blocks: 0,
        };
        let pct = calculate_dead_files_percentage(&summary);
        assert!((pct - 25.0).abs() < f64::EPSILON as f32);
    }

    #[test]
    fn test_calculate_dead_files_percentage_zero() {
        let summary = DeadCodeSummary {
            total_files_analyzed: 0,
            files_with_dead_code: 0,
            total_dead_lines: 0,
            dead_percentage: 0.0,
            dead_functions: 0,
            dead_classes: 0,
            dead_modules: 0,
            unreachable_blocks: 0,
        };
        let pct = calculate_dead_files_percentage(&summary);
        assert_eq!(pct, 0.0);
    }

    // Tests for validation functions

    #[test]
    fn test_find_missing_required_parameters_all_present() {
        let mut params = serde_json::Map::new();
        params.insert("name".to_string(), serde_json::json!("test"));
        params.insert("version".to_string(), serde_json::json!("1.0"));

        let specs = vec![
            ParameterSpec { param_type: crate::models::template::ParameterType::String,
                name: "name".to_string(),
                description: "Name".to_string(),
                required: true,
                default_value: None,
                validation_pattern: None,
            },
            ParameterSpec { param_type: crate::models::template::ParameterType::String,
                name: "version".to_string(),
                description: "Version".to_string(),
                required: true,
                default_value: None,
                validation_pattern: None,
            },
        ];

        let missing = find_missing_required_parameters(&params, &specs);
        assert!(missing.is_empty());
    }

    #[test]
    fn test_find_missing_required_parameters_some_missing() {
        let params = serde_json::Map::new();

        let specs = vec![ParameterSpec { param_type: crate::models::template::ParameterType::String,
            name: "name".to_string(),
            description: "Name".to_string(),
            required: true,
            default_value: None,
            validation_pattern: None,
        }];

        let missing = find_missing_required_parameters(&params, &specs);
        assert_eq!(missing.len(), 1);
        assert_eq!(missing[0], "name");
    }

    #[test]
    fn test_find_missing_required_parameters_optional_ok() {
        let params = serde_json::Map::new();

        let specs = vec![ParameterSpec { param_type: crate::models::template::ParameterType::String,
            name: "optional".to_string(),
            description: "Optional".to_string(),
            required: false,
            default_value: None,
            validation_pattern: None,
        }];

        let missing = find_missing_required_parameters(&params, &specs);
        assert!(missing.is_empty());
    }

    #[test]
    fn test_validate_single_parameter_no_pattern() {
        let spec = ParameterSpec { param_type: crate::models::template::ParameterType::String,
            name: "name".to_string(),
            description: "Name".to_string(),
            required: true,
            default_value: None,
            validation_pattern: None,
        };

        let error = validate_single_parameter("name", &serde_json::json!("test"), &spec);
        assert!(error.is_none());
    }

    #[test]
    fn test_validate_single_parameter_matching_pattern() {
        let spec = ParameterSpec { param_type: crate::models::template::ParameterType::String,
            name: "email".to_string(),
            description: "Email".to_string(),
            required: true,
            default_value: None,
            validation_pattern: Some(".*@.*".to_string()),
        };

        let error = validate_single_parameter("email", &serde_json::json!("test@example.com"), &spec);
        assert!(error.is_none());
    }

    #[test]
    fn test_validate_single_parameter_non_matching_pattern() {
        let spec = ParameterSpec { param_type: crate::models::template::ParameterType::String,
            name: "email".to_string(),
            description: "Email".to_string(),
            required: true,
            default_value: None,
            validation_pattern: Some(".*@.*".to_string()),
        };

        let error = validate_single_parameter("email", &serde_json::json!("invalid"), &spec);
        assert!(error.is_some());
        assert!(error.unwrap().contains("does not match pattern"));
    }

    #[test]
    fn test_validate_parameter_values_unknown_param() {
        let mut params = serde_json::Map::new();
        params.insert("unknown".to_string(), serde_json::json!("value"));

        let specs = vec![];

        let errors = validate_parameter_values(&params, &specs);
        assert_eq!(errors.len(), 1);
        assert!(errors[0].contains("Unknown parameter"));
    }

    // Tests for Makefile lint helper functions

    #[test]
    fn test_map_severity() {
        use crate::services::makefile_linter::Severity;

        assert_eq!(map_severity(&Severity::Error), "error");
        assert_eq!(map_severity(&Severity::Warning), "warning");
        assert_eq!(map_severity(&Severity::Performance), "performance");
        assert_eq!(map_severity(&Severity::Info), "info");
    }

    // Tests for SATD helper functions

    #[test]
    fn test_create_satd_detector_normal() {
        let detector = create_satd_detector(false);
        // Just verify it creates without panicking
        drop(detector);
    }

    #[test]
    fn test_create_satd_detector_strict() {
        let detector = create_satd_detector(true);
        // Just verify it creates without panicking
        drop(detector);
    }

    // Tests for lint hotspot data extraction

    #[test]
    fn test_extract_lint_data_empty() {
        let data = serde_json::json!({});
        let extracted = extract_lint_data(&data);

        assert!(extracted.hotspots.is_empty());
        assert_eq!(extracted.total_files, 0);
        assert_eq!(extracted.total_violations, 0);
    }

    #[test]
    fn test_extract_lint_data_with_values() {
        let data = serde_json::json!({
            "hotspots": [{"file": "test.rs"}],
            "total_files_analyzed": 50,
            "total_violations": 100,
            "average_violations_per_file": 2.0
        });
        let extracted = extract_lint_data(&data);

        assert_eq!(extracted.hotspots.len(), 1);
        assert_eq!(extracted.total_files, 50);
        assert_eq!(extracted.total_violations, 100);
        assert!((extracted.average_violations_per_file - 2.0).abs() < f64::EPSILON);
    }

    // Tests for format_lint_hotspot_output

    #[test]
    fn test_format_lint_hotspot_output_json() {
        let args = LintHotspotArgs {
            project_path: "/test".to_string(),
            top_files: 10,
            min_violations: 1,
            include: None,
            exclude: None,
            format: "json".to_string(),
        };
        let data = LintHotspotData {
            hotspots: vec![],
            total_files: 10,
            total_violations: 5,
            average_violations_per_file: 0.5,
        };

        let output = format_lint_hotspot_output(&args, &data);
        assert!(output.get("project_path").is_some());
    }

    #[test]
    fn test_format_lint_hotspot_output_csv() {
        let args = LintHotspotArgs {
            project_path: "/test".to_string(),
            top_files: 10,
            min_violations: 1,
            include: None,
            exclude: None,
            format: "csv".to_string(),
        };
        let data = LintHotspotData {
            hotspots: vec![],
            total_files: 10,
            total_violations: 5,
            average_violations_per_file: 0.5,
        };

        let output = format_lint_hotspot_output(&args, &data);
        assert!(output.get("content_type").is_some());
    }

    #[test]
    fn test_format_lint_hotspot_output_table() {
        let args = LintHotspotArgs {
            project_path: "/test".to_string(),
            top_files: 10,
            min_violations: 1,
            include: None,
            exclude: None,
            format: "table".to_string(),
        };
        let data = LintHotspotData {
            hotspots: vec![],
            total_files: 10,
            total_violations: 5,
            average_violations_per_file: 0.5,
        };

        let output = format_lint_hotspot_output(&args, &data);
        assert!(output.get("formatted_output").is_some());
    }

    // Tests for parse_tool_call_params

    #[test]
    fn test_parse_tool_call_params_valid() {
        let params = serde_json::json!({
            "name": "analyze_complexity",
            "arguments": {}
        });
        let request_id = serde_json::json!(1);

        let result = parse_tool_call_params(Some(params), &request_id);
        assert!(result.is_ok());
    }

    #[test]
    fn test_parse_tool_call_params_none() {
        let request_id = serde_json::json!(1);
        let result = parse_tool_call_params(None, &request_id);
        assert!(result.is_err());
    }

    #[test]
    fn test_parse_tool_call_params_invalid() {
        let params = serde_json::json!("not an object");
        let request_id = serde_json::json!(1);

        let result = parse_tool_call_params(Some(params), &request_id);
        assert!(result.is_err());
    }

    // Tests for argument parsing functions

    #[test]
    fn test_parse_complexity_args_valid() {
        let args = serde_json::json!({
            "project_path": "/test",
            "toolchain": "rust"
        });

        let result = parse_complexity_args(args);
        assert!(result.is_ok());
    }

    #[test]
    fn test_parse_complexity_args_empty() {
        let args = serde_json::json!({});
        let result = parse_complexity_args(args);
        assert!(result.is_ok());
    }

    #[test]
    fn test_parse_code_churn_args_valid() {
        let args = serde_json::json!({
            "project_path": "/test",
            "period_days": 30
        });

        let result = parse_code_churn_args(args);
        assert!(result.is_ok());
    }

    #[test]
    fn test_parse_tdg_args_valid() {
        let args = serde_json::json!({
            "project_path": "/test",
            "format": "json"
        });

        let result = parse_tdg_args(args);
        assert!(result.is_ok());
    }

    #[test]
    fn test_parse_deep_context_args_valid() {
        let args = serde_json::json!({
            "project_path": "/test",
            "format": "markdown"
        });

        let result = parse_deep_context_args(args);
        assert!(result.is_ok());
    }

    #[test]
    fn test_parse_satd_args_valid() {
        let args = serde_json::json!({
            "project_path": "/test",
            "strict": true
        });

        let result = parse_satd_args(args);
        assert!(result.is_ok());
    }

    #[test]
    fn test_parse_lint_hotspot_args_valid() {
        let args = serde_json::json!({
            "project_path": "/test",
            "top_files": 20
        });

        let result = parse_lint_hotspot_args(args);
        assert!(result.is_ok());
    }

    #[test]
    fn test_parse_makefile_lint_args_valid() {
        let args = serde_json::json!({
            "path": "/test/Makefile"
        });

        let result = parse_makefile_lint_args(Some(args));
        assert!(result.is_ok());
    }

    #[test]
    fn test_parse_makefile_lint_args_none() {
        let result = parse_makefile_lint_args(None);
        assert!(result.is_err());
    }

    // Tests for extract_churn_parameters

    #[test]
    fn test_extract_churn_parameters_defaults() {
        let args = AnalyzeCodeChurnArgs {
            project_path: None,
            period_days: None,
            format: None,
        };

        let (path, days, format) = extract_churn_parameters(&args);
        assert!(!path.as_os_str().is_empty());
        assert_eq!(days, 30);
        assert!(matches!(format, ChurnOutputFormat::Summary));
    }

    #[test]
    fn test_extract_churn_parameters_custom() {
        let args = AnalyzeCodeChurnArgs {
            project_path: Some("/custom".to_string()),
            period_days: Some(7),
            format: Some("json".to_string()),
        };

        let (path, days, format) = extract_churn_parameters(&args);
        assert_eq!(path, PathBuf::from("/custom"));
        assert_eq!(days, 7);
        assert!(matches!(format, ChurnOutputFormat::Json));
    }

    // Tests for extract_tdg_project_path

    #[test]
    fn test_extract_tdg_project_path_some() {
        let args = AnalyzeTdgArgs {
            project_path: Some("/custom".to_string()),
            format: None,
            threshold: None,
            include_components: None,
            max_results: None,
        };

        let path = extract_tdg_project_path(&args);
        assert_eq!(path, PathBuf::from("/custom"));
    }

    #[test]
    fn test_extract_tdg_project_path_none() {
        let args = AnalyzeTdgArgs {
            project_path: None,
            format: None,
            threshold: None,
            include_components: None,
            max_results: None,
        };

        let path = extract_tdg_project_path(&args);
        assert!(!path.as_os_str().is_empty());
    }

    // Tests for format_churn_output

    #[test]
    fn test_format_churn_output_json() {
        let analysis = create_test_churn_analysis();
        let output = format_churn_output(&analysis, &ChurnOutputFormat::Json);
        // Should be valid JSON
        assert!(serde_json::from_str::<serde_json::Value>(&output).is_ok());
    }

    #[test]
    fn test_format_churn_output_markdown() {
        let analysis = create_test_churn_analysis();
        let output = format_churn_output(&analysis, &ChurnOutputFormat::Markdown);
        assert!(output.contains("# Code Churn Analysis Report"));
    }

    #[test]
    fn test_format_churn_output_csv() {
        let analysis = create_test_churn_analysis();
        let output = format_churn_output(&analysis, &ChurnOutputFormat::Csv);
        assert!(output.contains(","));
    }

    #[test]
    fn test_format_churn_output_summary() {
        let analysis = create_test_churn_analysis();
        let output = format_churn_output(&analysis, &ChurnOutputFormat::Summary);
        assert!(output.contains("# Code Churn Analysis"));
    }

    // Tests for build_churn_response

    #[test]
    fn test_build_churn_response() {
        let analysis = create_test_churn_analysis();
        let response = build_churn_response(
            "Test content".to_string(),
            analysis,
            &ChurnOutputFormat::Summary,
        );

        assert!(response.get("content").is_some());
        assert!(response.get("analysis").is_some());
        assert!(response.get("format").is_some());
    }
}
