pmat 3.14.0

PMAT - Zero-config AI context generation and code quality toolkit (CLI, MCP, HTTP)
#![allow(unused)]
//! Utility command handlers (list, search, context, etc.)
//!
//! This module contains utility command implementations extracted from
//! the main CLI module to reduce complexity.

#![cfg_attr(coverage_nightly, coverage(off))]
use crate::cli::{ContextFormat, OutputFormat};
use crate::models::template::TemplateResource;
use crate::services::template_service::{list_templates, search_templates};
use crate::stateless_server::StatelessTemplateServer;
use anyhow::Result;
use std::path::{Path, PathBuf};
use std::sync::Arc;

/// Handle template listing command
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub async fn handle_list(
    server: Arc<StatelessTemplateServer>,
    toolchain: Option<String>,
    category: Option<String>,
    format: OutputFormat,
) -> Result<()> {
    let templates =
        list_templates(server.as_ref(), toolchain.as_deref(), category.as_deref()).await?;

    match format {
        OutputFormat::Table => super::super::analysis_utilities::print_table(&templates),
        OutputFormat::Json => {
            let templates_deref: Vec<&TemplateResource> =
                templates.iter().map(std::convert::AsRef::as_ref).collect();
            println!("{}", serde_json::to_string_pretty(&templates_deref)?);
        }
        OutputFormat::Yaml => {
            let templates_deref: Vec<&TemplateResource> =
                templates.iter().map(std::convert::AsRef::as_ref).collect();
            println!("{}", serde_yaml_ng::to_string(&templates_deref)?);
        }
        _ => super::super::analysis_utilities::print_table(&templates),
    }
    Ok(())
}

// Helper structures for markdown formatting
struct MarkdownBuilder {
    content: String,
}

impl MarkdownBuilder {
    fn new() -> Self {
        Self {
            content: String::new(),
        }
    }

    fn add_header(&mut self, level: usize, text: &str) {
        for _ in 0..level {
            self.content.push('#');
        }
        self.content.push(' ');
        self.content.push_str(text);
        self.content.push_str("\n\n");
    }

    fn add_metric(&mut self, label: &str, value: impl std::fmt::Display) {
        self.content.push_str(&format!("- **{label}**: {value}\n"));
    }

    fn add_percentage_metric(&mut self, label: &str, value: f64) {
        self.content
            .push_str(&format!("- **{label}**: {value:.1}%\n"));
    }

    fn add_newline(&mut self) {
        self.content.push('\n');
    }

    fn build(self) -> String {
        self.content
    }
}

/// Handle template search command
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub async fn handle_search(
    server: Arc<StatelessTemplateServer>,
    query: String,
    toolchain: Option<String>,
    limit: usize,
) -> Result<()> {
    let results = search_templates(server.clone(), &query, toolchain.as_deref()).await?;

    if results.is_empty() {
        eprintln!("No templates found matching '{}'", query);
        eprintln!("Hint: use 'pmat list' to see all available templates");
        return Ok(());
    }
    for (i, result) in results.iter().take(limit).enumerate() {
        println!(
            "{:2}. {} (score: {:.2})",
            i + 1,
            result.template.uri,
            result.relevance
        );
        if !result.matches.is_empty() {
            println!("    Matches: {}", result.matches.join(", "));
        }
    }
    Ok(())
}

/// Handle context generation command
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
#[allow(clippy::too_many_arguments)]
pub async fn handle_context(
    toolchain: Option<String>,
    project_path: PathBuf,
    output: Option<PathBuf>,
    format: ContextFormat,
    include_large_files: bool,
    skip_expensive_metrics: bool,
    language: Option<String>,
    languages: Option<Vec<String>>,
) -> Result<()> {
    use crate::services::deep_context::{
        AnalysisType, CacheStrategy, DagType, DeepContextAnalyzer, DeepContextConfig,
    };
    use crate::services::language_override::{get_effective_languages, LanguageOverride};

    // BUG-012: Apply language override if specified
    let override_opts = LanguageOverride {
        language,
        languages,
    };
    let effective_languages = get_effective_languages(&override_opts, &project_path)?;

    // Use the first effective language as the toolchain (single language support)
    let toolchain = if !effective_languages.is_empty() {
        effective_languages[0].clone()
    } else {
        detect_or_use_toolchain(toolchain, &project_path)?
    };

    // Configure deep context analysis - RESTORE FULL ANALYSIS CAPABILITY
    let config = DeepContextConfig {
        include_analyses: if skip_expensive_metrics {
            vec![
                AnalysisType::Ast,
                AnalysisType::Complexity,
                AnalysisType::DeadCode,
                AnalysisType::Satd,
            ]
        } else {
            // FULL analysis with ALL annotations - this is what users expect!
            vec![
                AnalysisType::Ast,
                AnalysisType::Complexity,
                AnalysisType::Churn,
                AnalysisType::TechnicalDebtGradient,
                AnalysisType::DeadCode,
                AnalysisType::Satd,
                AnalysisType::Provability,
                AnalysisType::BigO,
            ]
        },
        period_days: 30, // Restore full period for proper churn analysis
        dag_type: DagType::CallGraph,
        complexity_thresholds: Some(crate::services::deep_context::ComplexityThresholds {
            max_cyclomatic: 20,
            max_cognitive: 25,
        }),
        max_depth: Some(5),       // Smart bounds - limit depth for performance
        include_patterns: vec![], // Remove overly restrictive patterns - let file classifier handle it
        exclude_patterns: vec![
            "**/target/**".to_string(),
            "**/node_modules/**".to_string(),
            "**/build/**".to_string(),
            "**/dist/**".to_string(),
            "**/.git/**".to_string(),
            "**/fuzz/**".to_string(),
        ],
        cache_strategy: CacheStrategy::Normal,
        parallel: num_cpus::get(), // Use all available CPUs like cargo test does
        file_classifier_config: None,
    };

    // Run the deep context analysis
    let analyzer = DeepContextAnalyzer::new(config);
    let context = analyzer.analyze_project(&project_path).await?;

    // Generate enhanced annotated AST output
    let output_content = generate_enhanced_ast_context(
        &toolchain,
        &project_path,
        &context,
        format,
        include_large_files,
    )
    .await?;

    // Write output
    write_context_output(output, &output_content).await?;

    Ok(())
}

include!("context_generation.rs");
include!("project_analysis.rs");
include!("context_output.rs");

pub use super::utility_serve_handlers::handle_serve;

/// Handle diagnose command
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub async fn handle_diagnose(args: crate::cli::diagnose::DiagnoseArgs) -> Result<()> {
    crate::cli::diagnose::handle_diagnose(args).await
}

// Tests extracted to utility_handlers_tests.rs for file health compliance (CB-040)
#[cfg(test)]
#[path = "utility_handlers_tests.rs"]
mod tests;