//! Report generation for scan results.
//!
//! Supports multiple output formats:
//! - `table`: Terminal table output (default)
//! - `json`: JSON for programmatic consumption
//! - `sarif`: SARIF for CI integration (GitHub, GitLab, Azure DevOps)
//! - `markdown`: Markdown for documentation

mod json;
mod markdown;
mod sarif;
mod table;

pub use json::JsonReport;
pub use markdown::MarkdownReport;
pub use sarif::SarifReport;
pub use table::TableReport;

use crate::types::{ScanResult, Verdict};

// Re-export helper function for submodules
pub(crate) use crate::types::extract_leaf_concept;

/// Trait for report formatters.
pub trait ReportFormatter {
    /// Format the scan result as a string.
    fn format(&self, result: &ScanResult) -> String;
}

/// Get a report formatter by name.
pub fn get_formatter(name: &str) -> Box<dyn ReportFormatter> {
    match name {
        "json" => Box::new(JsonReport),
        "sarif" => Box::new(SarifReport),
        "markdown" => Box::new(MarkdownReport),
        _ => Box::new(TableReport),
    }
}

/// Convert a Verdict to its display string.
pub(crate) fn verdict_label(verdict: Verdict) -> &'static str {
    match verdict {
        Verdict::Block => "BLOCK",
        Verdict::Flag => "FLAG",
        Verdict::Pass => "PASS",
        Verdict::Ack => "ACK",
        Verdict::Drift => "DRIFT",
    }
}

/// Convert an ObjectValue to a JSON value.
pub(crate) fn object_value_to_json(value: &stemedb_core::types::ObjectValue) -> serde_json::Value {
    use stemedb_core::types::ObjectValue;
    match value {
        ObjectValue::Text(s) => serde_json::Value::String(s.clone()),
        ObjectValue::Number(n) => serde_json::json!(n),
        ObjectValue::Boolean(b) => serde_json::Value::Bool(*b),
        ObjectValue::Reference(id) => serde_json::Value::String(format!("ref:{}", id)),
    }
}

/// Convert an ObjectValue to a human-readable display string.
pub(crate) fn object_value_display(value: &stemedb_core::types::ObjectValue) -> String {
    use stemedb_core::types::ObjectValue;
    match value {
        ObjectValue::Text(s) => s.clone(),
        ObjectValue::Number(n) => format!("{n}"),
        ObjectValue::Boolean(b) => format!("{b}"),
        ObjectValue::Reference(id) => format!("ref:{id}"),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::path::PathBuf;

    #[test]
    fn test_get_formatter_table() {
        let formatter = get_formatter("table");
        let result = ScanResult::stub(&PathBuf::from("."), "table");
        let output = formatter.format(&result);
        assert!(output.contains("Aphoria"));
    }

    #[test]
    fn test_get_formatter_json() {
        let formatter = get_formatter("json");
        let result = ScanResult::stub(&PathBuf::from("myproject"), "json");
        let output = formatter.format(&result);
        // Should be valid JSON
        let parsed: serde_json::Value = serde_json::from_str(&output).expect("valid json");
        assert_eq!(parsed["summary"]["files_scanned"], 0);
    }

    #[test]
    fn test_get_formatter_sarif() {
        let formatter = get_formatter("sarif");
        let result = ScanResult::stub(&PathBuf::from("."), "sarif");
        let output = formatter.format(&result);
        let parsed: serde_json::Value = serde_json::from_str(&output).expect("valid json");
        assert_eq!(parsed["version"], "2.1.0");
    }

    #[test]
    fn test_get_formatter_markdown() {
        let formatter = get_formatter("markdown");
        let result = ScanResult::stub(&PathBuf::from("myproject"), "markdown");
        let output = formatter.format(&result);
        assert!(output.starts_with("# Aphoria Scan:"));
    }

    #[test]
    fn test_get_formatter_unknown_defaults_to_table() {
        let formatter = get_formatter("unknown");
        let result = ScanResult::stub(&PathBuf::from("."), "table");
        let output = formatter.format(&result);
        assert!(output.contains("Aphoria"));
    }
}
