use crate::inspect::smp::regions::*;
use std::collections::BTreeMap;

pub fn compare_save_region_fixed_row_run_candidates(
    left: &SmpSaveCompanyChairmanAnalysisReport,
    right: &SmpSaveCompanyChairmanAnalysisReport,
) -> Option<SmpSaveRegionFixedRowRunComparisonReport> {
    let left_probe = left.region_fixed_row_run_candidates.as_ref()?;
    let right_probe = right.region_fixed_row_run_candidates.as_ref()?;

    let left_by_shape = left_probe
        .candidates
        .iter()
        .enumerate()
        .map(|(index, candidate)| (candidate.shape_signature.clone(), (index, candidate)))
        .collect::<BTreeMap<_, _>>();
    let right_by_shape = right_probe
        .candidates
        .iter()
        .enumerate()
        .map(|(index, candidate)| (candidate.shape_signature.clone(), (index, candidate)))
        .collect::<BTreeMap<_, _>>();

    let mut shared_shape_matches = Vec::new();
    let mut shared_shape_family_matches = Vec::new();
    let mut left_only_shape_signatures = Vec::new();
    let mut right_only_shape_signatures = Vec::new();
    let mut left_only_shape_family_signatures = Vec::new();
    let mut right_only_shape_family_signatures = Vec::new();
    let left_family_by_shape = left_probe
        .candidates
        .iter()
        .enumerate()
        .map(|(index, candidate)| (candidate.shape_family_signature.clone(), (index, candidate)))
        .collect::<BTreeMap<_, _>>();
    let right_family_by_shape = right_probe
        .candidates
        .iter()
        .enumerate()
        .map(|(index, candidate)| (candidate.shape_family_signature.clone(), (index, candidate)))
        .collect::<BTreeMap<_, _>>();

    for (shape_signature, (left_index, left_candidate)) in &left_by_shape {
        if let Some((right_index, right_candidate)) = right_by_shape.get(shape_signature) {
            shared_shape_matches.push(SmpSaveRegionFixedRowRunSharedShapeMatch {
                shape_signature: shape_signature.clone(),
                left_rank: left_index + 1,
                left_rows_offset_hex: left_candidate.rows_offset_hex.clone(),
                left_best_probable_density_lane_relative_offset_hex: left_candidate
                    .best_probable_density_lane_relative_offset_hex
                    .clone(),
                right_rank: right_index + 1,
                right_rows_offset_hex: right_candidate.rows_offset_hex.clone(),
                right_best_probable_density_lane_relative_offset_hex: right_candidate
                    .best_probable_density_lane_relative_offset_hex
                    .clone(),
            });
        } else {
            left_only_shape_signatures.push(shape_signature.clone());
        }
    }

    for shape_signature in right_by_shape.keys() {
        if !left_by_shape.contains_key(shape_signature) {
            right_only_shape_signatures.push(shape_signature.clone());
        }
    }

    for (shape_family_signature, (left_index, left_candidate)) in &left_family_by_shape {
        if let Some((right_index, right_candidate)) =
            right_family_by_shape.get(shape_family_signature)
        {
            shared_shape_family_matches.push(SmpSaveRegionFixedRowRunSharedShapeMatch {
                shape_signature: shape_family_signature.clone(),
                left_rank: left_index + 1,
                left_rows_offset_hex: left_candidate.rows_offset_hex.clone(),
                left_best_probable_density_lane_relative_offset_hex: left_candidate
                    .best_probable_density_lane_relative_offset_hex
                    .clone(),
                right_rank: right_index + 1,
                right_rows_offset_hex: right_candidate.rows_offset_hex.clone(),
                right_best_probable_density_lane_relative_offset_hex: right_candidate
                    .best_probable_density_lane_relative_offset_hex
                    .clone(),
            });
        } else {
            left_only_shape_family_signatures.push(shape_family_signature.clone());
        }
    }

    for shape_family_signature in right_family_by_shape.keys() {
        if !left_family_by_shape.contains_key(shape_family_signature) {
            right_only_shape_family_signatures.push(shape_family_signature.clone());
        }
    }

    Some(SmpSaveRegionFixedRowRunComparisonReport {
        left_profile_family: left.profile_family.clone(),
        right_profile_family: right.profile_family.clone(),
        left_best_rows_offset_hex: left_probe
            .candidates
            .first()
            .map(|candidate| candidate.rows_offset_hex.clone()),
        right_best_rows_offset_hex: right_probe
            .candidates
            .first()
            .map(|candidate| candidate.rows_offset_hex.clone()),
        left_best_shape_signature: left_probe
            .candidates
            .first()
            .map(|candidate| candidate.shape_signature.clone()),
        right_best_shape_signature: right_probe
            .candidates
            .first()
            .map(|candidate| candidate.shape_signature.clone()),
        left_best_shape_family_signature: left_probe
            .candidates
            .first()
            .map(|candidate| candidate.shape_family_signature.clone()),
        right_best_shape_family_signature: right_probe
            .candidates
            .first()
            .map(|candidate| candidate.shape_family_signature.clone()),
        shared_shape_matches,
        shared_shape_family_matches,
        left_only_shape_signatures,
        right_only_shape_signatures,
        left_only_shape_family_signatures,
        right_only_shape_family_signatures,
        evidence: vec![
            format!(
                "comparison keys the pre-region-header fixed-row candidates by derived lane-shape fingerprint instead of raw offset, because current grounded saves do not keep the same top rows_offset across files ({:?} vs {:?})",
                left_probe.candidates.first().map(|candidate| candidate.rows_offset_hex.as_str()),
                right_probe.candidates.first().map(|candidate| candidate.rows_offset_hex.as_str())
            ),
            "shared shape matches mean two saves surfaced at least one candidate with the same exact probable-f32/small-unsigned/partial-zero/trailing-byte profile, while shared shape-family matches allow mild count drift inside the same dense lane family".to_string(),
        ],
    })
}
