pmat 3.11.0

PMAT - Zero-config AI context generation and code quality toolkit (CLI, MCP, HTTP)
/// Facade for incremental coverage analysis operations
#[derive(Clone)]
pub struct IncrementalCoverageFacade {
    #[allow(dead_code)]
    registry: Arc<ServiceRegistry>,
}

impl IncrementalCoverageFacade {
    /// Create a new incremental coverage facade
    #[must_use]
    pub fn new(registry: Arc<ServiceRegistry>) -> Self {
        Self { registry }
    }

    /// Perform incremental coverage analysis on a project
    pub async fn analyze_project(
        &self,
        request: IncrementalCoverageRequest,
    ) -> Result<IncrementalCoverageResult> {
        // Get changed files between branches
        let changed_files = self
            .get_changed_files(
                &request.project_path,
                &request.base_branch,
                request.target_branch.as_deref(),
            )
            .await?;

        // Analyze coverage for changed files
        let coverage_data = self
            .analyze_coverage_changes(&request.project_path, &changed_files, &request)
            .await?;

        // Build result
        Ok(self.build_coverage_result(coverage_data, changed_files, &request))
    }

    /// Get changed files between branches
    async fn get_changed_files(
        &self,
        project_path: &Path,
        base_branch: &str,
        target_branch: Option<&str>,
    ) -> Result<Vec<(PathBuf, String)>> {
        use crate::cli::coverage_helpers::get_changed_files_for_coverage;

        get_changed_files_for_coverage(project_path, base_branch, target_branch).await
    }

    /// Analyze coverage changes for files
    async fn analyze_coverage_changes(
        &self,
        _project_path: &Path,
        changed_files: &[(PathBuf, String)],
        request: &IncrementalCoverageRequest,
    ) -> Result<Vec<ChangedFileCoverage>> {
        let mut coverage_data = Vec::new();

        for (path, status) in changed_files {
            if status == "M" || status == "A" {
                // Mock coverage analysis for now - would integrate with real coverage analyzer
                let coverage_before = if status == "A" { 0.0 } else { 0.75 };
                let coverage_after = 0.85;
                let coverage_delta = coverage_after - coverage_before;

                let file_coverage = ChangedFileCoverage {
                    file_path: path.display().to_string(),
                    coverage_before,
                    coverage_after,
                    coverage_delta,
                    status: if coverage_delta > 0.0 {
                        CoverageStatus::Improved
                    } else if coverage_delta < 0.0 {
                        CoverageStatus::Degraded
                    } else {
                        CoverageStatus::Unchanged
                    },
                    lines_covered: 85,
                    lines_total: 100,
                };

                coverage_data.push(file_coverage);

                // Only analyze top N files if requested
                if coverage_data.len() >= request.top_files {
                    break;
                }
            }
        }

        Ok(coverage_data)
    }

    /// Build the final coverage result
    fn build_coverage_result(
        &self,
        coverage_data: Vec<ChangedFileCoverage>,
        changed_files: Vec<(PathBuf, String)>,
        request: &IncrementalCoverageRequest,
    ) -> IncrementalCoverageResult {
        let total_files = changed_files.len();
        let covered_files = coverage_data
            .iter()
            .filter(|f| f.coverage_after > 0.0)
            .count();

        let avg_coverage = if coverage_data.is_empty() {
            0.0
        } else {
            coverage_data.iter().map(|f| f.coverage_after).sum::<f64>()
                / coverage_data.len() as f64
        };

        let files_above_threshold = coverage_data
            .iter()
            .filter(|f| f.coverage_after >= request.coverage_threshold)
            .count();

        let files_below_threshold = coverage_data
            .iter()
            .filter(|f| f.coverage_after < request.coverage_threshold)
            .count();

        let summary = format!(
            "Analyzed {} changed files: {} covered ({:.1}%), {} above threshold ({:.1}%), {} below threshold",
            total_files,
            covered_files,
            avg_coverage * 100.0,
            files_above_threshold,
            request.coverage_threshold * 100.0,
            files_below_threshold
        );

        IncrementalCoverageResult {
            total_files,
            covered_files,
            coverage_percentage: avg_coverage,
            files_above_threshold,
            files_below_threshold,
            changed_files: coverage_data,
            summary,
        }
    }

    /// Quick coverage analysis with defaults
    pub async fn quick_analysis(
        &self,
        project_path: PathBuf,
        base_branch: String,
    ) -> Result<IncrementalCoverageResult> {
        let request = IncrementalCoverageRequest {
            project_path,
            base_branch,
            target_branch: None,
            coverage_threshold: 0.8,
            changed_files_only: true,
            detailed: false,
            cache_dir: None,
            force_refresh: false,
            top_files: 10,
        };

        self.analyze_project(request).await
    }
}