use std::collections::HashMap;
use std::path::Path;

use serde::Serialize;

#[derive(Debug, Serialize, Default)]
pub struct ProcessingReport {
    pub report_version: u32,
    pub tool_version: String,
    pub input: InputSummary,
    pub result: ResultSummary,
    pub events: EventCounters,
}

#[derive(Debug, Serialize, Default)]
pub struct InputSummary {
    pub path: String,
    pub format: String,
    pub size_bytes: u64,
}

#[derive(Debug, Serialize, Default)]
pub struct ResultSummary {
    pub flows_read: u64,
    pub flows_emitted: u64,
    pub paths_in_spec: u64,
}

#[derive(Debug, Serialize, Default)]
pub struct EventCounters {
    #[serde(skip_serializing_if = "HashMap::is_empty")]
    pub cap_fired: HashMap<String, u64>,
    #[serde(skip_serializing_if = "HashMap::is_empty")]
    pub rejected: HashMap<String, u64>,
    #[serde(skip_serializing_if = "HashMap::is_empty")]
    pub parse_error: HashMap<String, u64>,
}

impl EventCounters {
    pub fn total(&self) -> u64 {
        let sum = |m: &HashMap<String, u64>| -> u64 { m.values().sum() };
        sum(&self.cap_fired) + sum(&self.rejected) + sum(&self.parse_error)
    }
}

impl ProcessingReport {
    pub fn new() -> Self {
        Self {
            report_version: 1,
            tool_version: env!("CARGO_PKG_VERSION").to_string(),
            ..Default::default()
        }
    }

    pub fn write_to_path(&self, path: &Path) -> std::io::Result<()> {
        if let Some(parent) = path.parent() {
            std::fs::create_dir_all(parent)?;
        }
        let tmp_path = path.with_extension("tmp");
        let file = std::fs::File::create(&tmp_path)?;
        let writer = std::io::BufWriter::new(file);
        serde_json::to_writer_pretty(writer, self).map_err(std::io::Error::other)?;
        std::fs::rename(&tmp_path, path)?;
        Ok(())
    }
}
