use super::super::super::*;
use super::super::embedded_names::EmbeddedNameRow;
use super::super::payload_envelope::PayloadEnvelopeRow;
use std::collections::BTreeMap;

pub(in crate::inspect::smp) fn build_dominant_profile_span_class_summary(
    records_payload: &[u8],
    payload_envelope_rows: &[PayloadEnvelopeRow],
    embedded_name_rows: &[EmbeddedNameRow],
    dominant_profile_chunk_len: Option<(usize, usize)>,
) -> Option<SmpSavePlacedStructureDynamicSideBufferDominantProfileSpanClassSummary> {
    let (dominant_profile_span_len, _) = dominant_profile_chunk_len?;
    let dominant_rows = embedded_name_rows
        .iter()
        .zip(payload_envelope_rows.iter())
        .filter_map(|(name_row, envelope_row)| {
            (envelope_row.profile_chunk_len_to_next_name_or_end == Some(dominant_profile_span_len))
                .then(|| {
                    let candidate_offset = name_row.name_tag_relative_offset.checked_sub(3);
                    let child_count_candidate =
                        candidate_offset.and_then(|offset| read_u16_at(records_payload, offset));
                    let saved_primary_child_byte_candidate =
                        candidate_offset.and_then(|offset| read_u8_at(records_payload, offset + 2));
                    (
                        name_row.name_tag_relative_offset,
                        name_row.primary_name.clone(),
                        name_row.secondary_name.clone(),
                        name_row.prefix_leading_dword,
                        name_row.prefix_trailing_word,
                        name_row.prefix_separator_byte,
                        child_count_candidate,
                        saved_primary_child_byte_candidate,
                    )
                })
        })
        .collect::<Vec<_>>();
    let mut dominant_name_pair_counts = BTreeMap::<(Option<String>, Option<String>), usize>::new();
    let mut dominant_prefix_counts = BTreeMap::<(u32, u16, u8), usize>::new();
    let mut dominant_candidate_pattern_counts = BTreeMap::<(u16, u8), usize>::new();
    for (
        _,
        primary_name,
        secondary_name,
        prefix_leading_dword,
        prefix_trailing_word,
        prefix_separator_byte,
        child_count_candidate,
        saved_primary_child_byte_candidate,
    ) in &dominant_rows
    {
        *dominant_name_pair_counts
            .entry((primary_name.clone(), secondary_name.clone()))
            .or_default() += 1;
        *dominant_prefix_counts
            .entry((
                *prefix_leading_dword,
                *prefix_trailing_word,
                *prefix_separator_byte,
            ))
            .or_default() += 1;
        if let (Some(child_count_candidate), Some(saved_primary_child_byte_candidate)) =
            (child_count_candidate, saved_primary_child_byte_candidate)
        {
            *dominant_candidate_pattern_counts
                .entry((*child_count_candidate, *saved_primary_child_byte_candidate))
                .or_default() += 1;
        }
    }
    let dominant_name_pair = dominant_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_prefix = dominant_prefix_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(
            |((prefix_leading_dword, prefix_trailing_word, prefix_separator_byte), count)| {
                (
                    *prefix_leading_dword,
                    *prefix_trailing_word,
                    *prefix_separator_byte,
                    *count,
                )
            },
        );
    let dominant_candidate_pattern = dominant_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 name_pair_summaries = dominant_name_pair_counts
        .iter()
        .map(|((primary_name, secondary_name), count)| {
            SmpSavePlacedStructureDynamicSideBufferDominantProfileSpanNamePairSummary {
                primary_name: primary_name.clone(),
                secondary_name: secondary_name.clone(),
                count: *count,
            }
        })
        .take(8)
        .collect::<Vec<_>>();
    let compact_prefix_pattern_summaries = dominant_prefix_counts
        .iter()
        .map(
            |((prefix_leading_dword, prefix_trailing_word, prefix_separator_byte), count)| {
                SmpSavePlacedStructureDynamicSideBufferDominantProfileSpanPrefixSummary {
                    prefix_leading_dword: *prefix_leading_dword,
                    prefix_leading_dword_hex: format!("0x{prefix_leading_dword:08x}"),
                    prefix_trailing_word: *prefix_trailing_word,
                    prefix_trailing_word_hex: format!("0x{prefix_trailing_word:04x}"),
                    prefix_separator_byte: *prefix_separator_byte,
                    prefix_separator_byte_hex: format!("0x{prefix_separator_byte:02x}"),
                    count: *count,
                }
            },
        )
        .take(8)
        .collect::<Vec<_>>();
    let candidate_pattern_summaries = dominant_candidate_pattern_counts
        .iter()
        .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,
                }
            },
        )
        .take(8)
        .collect::<Vec<_>>();
    Some(
        SmpSavePlacedStructureDynamicSideBufferDominantProfileSpanClassSummary {
            profile_chunk_len_to_next_name_or_end: dominant_profile_span_len,
            row_count: dominant_rows.len(),
            unique_name_pair_count: dominant_name_pair_counts.len(),
            unique_compact_prefix_pattern_count: dominant_prefix_counts.len(),
            dominant_candidate_pattern,
            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_prefix_leading_dword: dominant_prefix
                .map(|(prefix_leading_dword, _, _, _)| prefix_leading_dword),
            dominant_prefix_leading_dword_hex: dominant_prefix
                .map(|(prefix_leading_dword, _, _, _)| format!("0x{prefix_leading_dword:08x}")),
            dominant_prefix_trailing_word: dominant_prefix
                .map(|(_, prefix_trailing_word, _, _)| prefix_trailing_word),
            dominant_prefix_trailing_word_hex: dominant_prefix
                .map(|(_, prefix_trailing_word, _, _)| format!("0x{prefix_trailing_word:04x}")),
            dominant_prefix_separator_byte: dominant_prefix
                .map(|(_, _, prefix_separator_byte, _)| prefix_separator_byte),
            dominant_prefix_separator_byte_hex: dominant_prefix
                .map(|(_, _, prefix_separator_byte, _)| format!("0x{prefix_separator_byte:02x}")),
            dominant_prefix_count: dominant_prefix
                .map(|(_, _, _, count)| count)
                .unwrap_or_default(),
            sample_rows: dominant_rows
                .iter()
                .take(8)
                .enumerate()
                .map(
                    |(
                        sample_index,
                        (
                            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,
                        ),
                    )| {
                        SmpSavePlacedStructureDynamicSideBufferDominantProfileSpanSample {
                            sample_index,
                            name_tag_relative_offset: *name_tag_relative_offset,
                            primary_name: primary_name.clone(),
                            secondary_name: secondary_name.clone(),
                            prefix_leading_dword: *prefix_leading_dword,
                            prefix_leading_dword_hex: format!("0x{prefix_leading_dword:08x}"),
                            prefix_trailing_word: *prefix_trailing_word,
                            prefix_trailing_word_hex: format!("0x{prefix_trailing_word:04x}"),
                            prefix_separator_byte: *prefix_separator_byte,
                            prefix_separator_byte_hex: format!("0x{prefix_separator_byte:02x}"),
                            child_count_candidate: *child_count_candidate,
                            child_count_candidate_hex: child_count_candidate
                                .map(|value| format!("0x{value:04x}")),
                            saved_primary_child_byte_candidate: *saved_primary_child_byte_candidate,
                            saved_primary_child_byte_candidate_hex:
                                saved_primary_child_byte_candidate
                                    .map(|value| format!("0x{value:02x}")),
                        }
                    },
                )
                .collect(),
            name_pair_summaries,
            compact_prefix_pattern_summaries,
            candidate_pattern_summaries,
        },
    )
}
