use super::common::{RuntimeClassicProfileDifference, collect_json_multi_differences};
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};

use serde::Serialize;

#[derive(Debug, Serialize)]
pub(crate) struct RuntimePostSpecialConditionsScalarSample {
    pub(crate) path: String,
    pub(crate) profile_family: String,
    pub(crate) source_kind: String,
    pub(crate) nonzero_relative_offset_hexes: Vec<String>,
    pub(crate) values_by_relative_offset_hex: BTreeMap<String, String>,
}

#[derive(Debug, Serialize)]
pub(crate) struct RuntimePostSpecialConditionsScalarComparisonReport {
    pub(crate) file_count: usize,
    pub(crate) matches: bool,
    pub(crate) common_profile_family: Option<String>,
    pub(crate) samples: Vec<RuntimePostSpecialConditionsScalarSample>,
    pub(crate) difference_count: usize,
    pub(crate) differences: Vec<RuntimeClassicProfileDifference>,
}

pub(crate) fn compare_post_special_conditions_scalars(
    smp_paths: &[PathBuf],
) -> Result<(), Box<dyn std::error::Error>> {
    let samples = smp_paths
        .iter()
        .map(|path| load_post_special_conditions_scalar_sample(path))
        .collect::<Result<Vec<_>, _>>()?;
    let common_profile_family = samples
        .first()
        .map(|sample| sample.profile_family.clone())
        .filter(|family| {
            samples
                .iter()
                .all(|sample| sample.profile_family == *family)
        });
    let differences = diff_post_special_conditions_scalar_samples(&samples)?;
    let report = RuntimePostSpecialConditionsScalarComparisonReport {
        file_count: samples.len(),
        matches: differences.is_empty(),
        common_profile_family,
        difference_count: differences.len(),
        differences,
        samples,
    };
    println!("{}", serde_json::to_string_pretty(&report)?);
    Ok(())
}

pub(crate) fn load_post_special_conditions_scalar_sample(
    smp_path: &Path,
) -> Result<RuntimePostSpecialConditionsScalarSample, Box<dyn std::error::Error>> {
    let sample =
        crate::app::runtime_scan::post_special::load_post_special_conditions_scalar_scan_sample(
            smp_path,
        )?;
    Ok(RuntimePostSpecialConditionsScalarSample {
        path: sample.path,
        profile_family: sample.profile_family,
        source_kind: sample.source_kind,
        nonzero_relative_offset_hexes: sample
            .nonzero_relative_offsets
            .into_iter()
            .map(|offset| format!("0x{offset:x}"))
            .collect(),
        values_by_relative_offset_hex: sample.values_by_relative_offset_hex,
    })
}

pub(crate) fn diff_post_special_conditions_scalar_samples(
    samples: &[RuntimePostSpecialConditionsScalarSample],
) -> Result<Vec<RuntimeClassicProfileDifference>, Box<dyn std::error::Error>> {
    let labeled_values = samples
        .iter()
        .map(|sample| {
            (
                sample.path.clone(),
                serde_json::json!({
                    "profile_family": sample.profile_family,
                    "source_kind": sample.source_kind,
                    "nonzero_relative_offset_hexes": sample.nonzero_relative_offset_hexes,
                    "values_by_relative_offset_hex": sample.values_by_relative_offset_hex,
                }),
            )
        })
        .collect::<Vec<_>>();
    let mut differences = Vec::new();
    collect_json_multi_differences("$", &labeled_values, &mut differences);
    Ok(differences)
}
