use super::super::super::super::*;
use super::super::super::mode_family::classify_side_buffer_mode_family;
use super::super::candidate_rows::NamePreludeCandidateRow;
use std::collections::BTreeMap;

pub(in crate::inspect::smp) fn build_compact_prefix_correlations(
    name_prelude_candidate_rows: &[NamePreludeCandidateRow],
) -> Vec<SmpSavePlacedStructureDynamicSideBufferNamePreludeCompactPrefixCorrelation> {
    let mut name_prelude_compact_prefix_groups = BTreeMap::<
        (u32, u16, u8),
        Vec<(
            usize,
            Option<String>,
            Option<String>,
            u16,
            u8,
            Option<usize>,
            Option<(u8, u8)>,
        )>,
    >::new();
    for (
        name_tag_relative_offset,
        primary_name,
        secondary_name,
        prefix_leading_dword,
        prefix_trailing_word,
        prefix_separator_byte,
        child_count_candidate,
        saved_primary_child_byte_candidate,
        previous_span,
        previous_short_profile_flag_pair,
    ) in name_prelude_candidate_rows
    {
        name_prelude_compact_prefix_groups
            .entry((
                *prefix_leading_dword,
                *prefix_trailing_word,
                *prefix_separator_byte,
            ))
            .or_default()
            .push((
                *name_tag_relative_offset,
                primary_name.clone(),
                secondary_name.clone(),
                *child_count_candidate,
                *saved_primary_child_byte_candidate,
                *previous_span,
                *previous_short_profile_flag_pair,
            ));
    }
    name_prelude_compact_prefix_groups
        .into_iter()
        .map(|((prefix_leading_dword, prefix_trailing_word, prefix_separator_byte), rows)| {
            let mut name_pair_counts = BTreeMap::<(Option<String>, Option<String>), usize>::new();
            let mut profile_span_counts = BTreeMap::<usize, usize>::new();
            let mut candidate_pattern_counts = BTreeMap::<(u16, u8), usize>::new();
            let mut mode_family_counts = BTreeMap::<String, usize>::new();
            let mut flag_pair_counts = BTreeMap::<(u8, u8), usize>::new();
            for (
                _name_tag_relative_offset,
                primary_name,
                secondary_name,
                child_count_candidate,
                saved_primary_child_byte_candidate,
                previous_span,
                previous_short_profile_flag_pair,
            ) in &rows
            {
                *name_pair_counts
                    .entry((primary_name.clone(), secondary_name.clone()))
                    .or_default() += 1;
                if let Some(previous_span) = previous_span {
                    *profile_span_counts.entry(*previous_span).or_default() += 1;
                }
                *candidate_pattern_counts
                    .entry((*child_count_candidate, *saved_primary_child_byte_candidate))
                    .or_default() += 1;
                *mode_family_counts
                    .entry(
                        classify_side_buffer_mode_family(
                            primary_name.as_deref(),
                            secondary_name.as_deref(),
                        )
                        .to_string(),
                    )
                    .or_default() += 1;
                if let Some((first_flag_byte, second_flag_byte)) = previous_short_profile_flag_pair
                {
                    *flag_pair_counts
                        .entry((*first_flag_byte, *second_flag_byte))
                        .or_default() += 1;
                }
            }
            let dominant_name_pair = name_pair_counts
                .iter()
                .max_by(|(left_key, left_count), (right_key, right_count)| {
                    left_count
                        .cmp(right_count)
                        .then_with(|| right_key.cmp(left_key))
                })
                .map(|((primary_name, secondary_name), count)| {
                    (primary_name.clone(), secondary_name.clone(), *count)
                });
            let dominant_profile_span = profile_span_counts
                .iter()
                .max_by(|(left_key, left_count), (right_key, right_count)| {
                    left_count
                        .cmp(right_count)
                        .then_with(|| right_key.cmp(left_key))
                })
                .map(|(span, count)| (*span, *count));
            let dominant_candidate_pattern = candidate_pattern_counts
                .iter()
                .max_by(|(left_key, left_count), (right_key, right_count)| {
                    left_count
                        .cmp(right_count)
                        .then_with(|| right_key.cmp(left_key))
                })
                .map(|((child_count_candidate, saved_primary_child_byte_candidate), count)| {
                    SmpSavePlacedStructureDynamicSideBufferNamePreludeCandidatePattern {
                        child_count_candidate: *child_count_candidate,
                        child_count_candidate_hex: format!("0x{child_count_candidate:04x}"),
                        saved_primary_child_byte_candidate: *saved_primary_child_byte_candidate,
                        saved_primary_child_byte_candidate_hex: format!(
                            "0x{saved_primary_child_byte_candidate:02x}"
                        ),
                        count: *count,
                    }
                });
            let dominant_mode_family = mode_family_counts
                .iter()
                .max_by(|(left_key, left_count), (right_key, right_count)| {
                    left_count
                        .cmp(right_count)
                        .then_with(|| right_key.cmp(left_key))
                })
                .map(|(mode_family, count)| (mode_family.clone(), *count));
            SmpSavePlacedStructureDynamicSideBufferNamePreludeCompactPrefixCorrelation {
                prefix_leading_dword,
                prefix_leading_dword_hex: format!("0x{prefix_leading_dword:08x}"),
                prefix_trailing_word,
                prefix_trailing_word_hex: format!("0x{prefix_trailing_word:04x}"),
                prefix_separator_byte,
                prefix_separator_byte_hex: format!("0x{prefix_separator_byte:02x}"),
                row_count: rows.len(),
                unique_name_pair_count: name_pair_counts.len(),
                unique_profile_span_count: profile_span_counts.len(),
                dominant_primary_name: dominant_name_pair
                    .as_ref()
                    .and_then(|(primary_name, _, _)| primary_name.clone()),
                dominant_secondary_name: dominant_name_pair
                    .as_ref()
                    .and_then(|(_, secondary_name, _)| secondary_name.clone()),
                dominant_name_pair_count: dominant_name_pair
                    .map(|(_, _, count)| count)
                    .unwrap_or_default(),
                dominant_profile_span: dominant_profile_span.map(|(profile_span, _)| profile_span),
                dominant_profile_span_count: dominant_profile_span
                    .map(|(_, count)| count)
                    .unwrap_or_default(),
                dominant_candidate_pattern,
                dominant_mode_family: dominant_mode_family
                    .as_ref()
                    .map(|(mode_family, _)| mode_family.clone()),
                dominant_mode_family_count: dominant_mode_family
                    .map(|(_, count)| count)
                    .unwrap_or_default(),
                mode_family_counts: mode_family_counts
                    .into_iter()
                    .map(|(mode_family, count)| {
                        SmpSavePlacedStructureDynamicSideBufferNamePreludeModeFamilyCount {
                            mode_family,
                            count,
                        }
                    })
                    .collect(),
                name_pair_summaries: name_pair_counts
                    .into_iter()
                    .map(|((primary_name, secondary_name), count)| {
                        SmpSavePlacedStructureDynamicSideBufferDominantProfileSpanNamePairSummary {
                            primary_name,
                            secondary_name,
                            count,
                        }
                    })
                    .collect(),
                profile_span_counts: profile_span_counts
                    .into_iter()
                    .map(|(previous_profile_chunk_len_to_next_name_or_end, count)| {
                        SmpSavePlacedStructureDynamicSideBufferNamePreludeCompactPrefixProfileSpanCount {
                            previous_profile_chunk_len_to_next_name_or_end,
                            count,
                        }
                    })
                    .collect(),
                rows_with_previous_short_profile_flag_pair: flag_pair_counts.values().copied().sum(),
                previous_short_profile_flag_pair_counts: flag_pair_counts
                    .into_iter()
                    .map(|((first_flag_byte, second_flag_byte), count)| {
                        SmpSavePlacedStructureDynamicSideBufferNamePreludeCompactPrefixFlagPairCount {
                            first_flag_byte,
                            first_flag_byte_hex: format!("0x{first_flag_byte:02x}"),
                            second_flag_byte,
                            second_flag_byte_hex: format!("0x{second_flag_byte:02x}"),
                            count,
                        }
                    })
                    .collect(),
                sample_rows: rows
                    .iter()
                    .take(8)
                    .enumerate()
                    .map(|(sample_index, (name_tag_relative_offset, primary_name, secondary_name, child_count_candidate, saved_primary_child_byte_candidate, previous_profile_chunk_len_to_next_name_or_end, _))| {
                        SmpSavePlacedStructureDynamicSideBufferNamePreludeCompactPrefixSample {
                            sample_index,
                            name_tag_relative_offset: *name_tag_relative_offset,
                            primary_name: primary_name.clone(),
                            secondary_name: secondary_name.clone(),
                            child_count_candidate: *child_count_candidate,
                            child_count_candidate_hex: format!("0x{child_count_candidate:04x}"),
                            saved_primary_child_byte_candidate: *saved_primary_child_byte_candidate,
                            saved_primary_child_byte_candidate_hex: format!(
                                "0x{saved_primary_child_byte_candidate:02x}"
                            ),
                            previous_profile_chunk_len_to_next_name_or_end:
                                *previous_profile_chunk_len_to_next_name_or_end,
                        }
                    })
                    .collect(),
            }
        })
        .take(8)
        .collect::<Vec<_>>()
}
