pmat 3.15.0

PMAT - Zero-config AI context generation and code quality toolkit (CLI, MCP, HTTP)
impl EnhancedReportingService {
    /// Create new enhanced reporting service
    #[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
    pub fn new() -> Result<Self> {
        Ok(Self {
            renderer: crate::services::renderer::TemplateRenderer::new()?,
        })
    }

    /// Generate unified analysis report
    #[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
    pub async fn generate_report(
        &self,
        config: ReportConfig,
        analysis_results: AnalysisResults,
    ) -> Result<UnifiedAnalysisReport> {
        info!("📊 Generating enhanced analysis report");
        info!("📂 Project: {}", config.project_path.display());
        info!("📄 Format: {:?}", config.output_format);

        let start_time = std::time::Instant::now();

        // Build metadata
        let metadata = self.build_metadata(&config, &analysis_results)?;

        // Generate executive summary
        let executive_summary = self.generate_executive_summary(&analysis_results)?;

        // Build report sections
        let sections = self.build_sections(&analysis_results, &config)?;

        // Generate recommendations
        let recommendations = self.generate_recommendations(&analysis_results, &sections)?;

        // Create visualizations if requested
        let visualizations = if config.include_visualizations {
            self.create_visualizations(&analysis_results, &sections)?
        } else {
            Vec::new()
        };

        let report = UnifiedAnalysisReport {
            metadata,
            executive_summary,
            sections,
            recommendations,
            visualizations,
        };

        let duration = start_time.elapsed();
        info!("✅ Report generated in {:?}", duration);

        Ok(report)
    }

    /// Build report metadata
    fn build_metadata(
        &self,
        config: &ReportConfig,
        results: &AnalysisResults,
    ) -> Result<ReportMetadata> {
        Ok(ReportMetadata {
            project_name: config
                .project_path
                .file_name()
                .unwrap_or_default()
                .to_string_lossy()
                .to_string(),
            project_path: config.project_path.display().to_string(),
            report_date: chrono::Utc::now().to_rfc3339(),
            tool_version: env!("CARGO_PKG_VERSION").to_string(),
            analysis_duration: results.total_duration.as_secs_f64(),
            analyzed_files: results.analyzed_files,
            total_lines: results.total_lines,
        })
    }

    /// Generate executive summary
    fn generate_executive_summary(&self, results: &AnalysisResults) -> Result<ExecutiveSummary> {
        let overall_health_score = self.calculate_health_score(results);
        let critical_issues = self.count_issues_by_severity(results, Severity::Critical);
        let high_priority_issues = self.count_issues_by_severity(results, Severity::High);

        let key_findings = self.extract_key_findings(results);
        let risk_assessment = self.assess_overall_risk(results);

        Ok(ExecutiveSummary {
            overall_health_score,
            critical_issues,
            high_priority_issues,
            key_findings,
            risk_assessment,
        })
    }

    /// Calculate overall health score (0-100)
    fn calculate_health_score(&self, results: &AnalysisResults) -> f64 {
        let mut score = 100.0;

        // Deduct points for various issues
        if let Some(complexity) = &results.complexity_analysis {
            let avg_complexity =
                f64::from(complexity.total_cyclomatic) / complexity.functions as f64;
            if avg_complexity > 10.0 {
                score -= (avg_complexity - 10.0).min(20.0);
            }
        }

        if let Some(dead_code) = &results.dead_code_analysis {
            let dead_code_ratio = dead_code.dead_lines as f64 / results.total_lines as f64;
            score -= (dead_code_ratio * 100.0).min(15.0);
        }

        if let Some(duplication) = &results.duplication_analysis {
            let duplication_ratio =
                duplication.duplicated_lines as f64 / results.total_lines as f64;
            score -= (duplication_ratio * 100.0).min(15.0);
        }

        if let Some(tdg) = &results.tdg_analysis {
            if tdg.average_tdg > 3.0 {
                score -= ((tdg.average_tdg - 3.0) * 5.0).min(20.0);
            }
        }

        score.max(0.0)
    }

    /// Count issues by severity
    fn count_issues_by_severity(&self, _results: &AnalysisResults, _severity: Severity) -> usize {
        // Count from various analyses
        // This is a simplified version - in real implementation, each analysis
        // would contribute its issues

        0
    }

    /// Extract key findings
    fn extract_key_findings(&self, results: &AnalysisResults) -> Vec<String> {
        let mut findings = Vec::new();

        if let Some(complexity) = &results.complexity_analysis {
            if complexity.max_cyclomatic > 20 {
                findings.push(format!(
                    "Found {} functions with high complexity (CC > 20)",
                    complexity.high_complexity_functions
                ));
            }
        }

        if let Some(dead_code) = &results.dead_code_analysis {
            if dead_code.dead_functions > 10 {
                findings.push(format!(
                    "Detected {} unused functions that could be removed",
                    dead_code.dead_functions
                ));
            }
        }

        if let Some(duplication) = &results.duplication_analysis {
            if duplication.duplicate_blocks > 20 {
                findings.push(format!(
                    "Found {} duplicate code blocks affecting maintainability",
                    duplication.duplicate_blocks
                ));
            }
        }

        findings
    }

    /// Assess overall risk level
    fn assess_overall_risk(&self, results: &AnalysisResults) -> RiskLevel {
        let health_score = self.calculate_health_score(results);

        match health_score {
            s if s >= 80.0 => RiskLevel::Low,
            s if s >= 60.0 => RiskLevel::Medium,
            s if s >= 40.0 => RiskLevel::High,
            _ => RiskLevel::Critical,
        }
    }
}