use super::super::embedded_names::EmbeddedNameRow;
use super::rows::PayloadEnvelopeRow;
use crate::inspect::smp::common::{read_u16_at, read_u32_at};
use crate::inspect::smp::structures::dynamic_side_buffer::mode_family::classify_side_buffer_mode_family;
use crate::inspect::smp::structures::{
    SmpSavePlacedStructureDynamicSideBufferFixedPolicyCompactPrefixCorrelation,
    SmpSavePlacedStructureDynamicSideBufferFixedPolicySample,
    SmpSavePlacedStructureDynamicSideBufferFixedPolicySummary,
    SmpSavePlacedStructureDynamicSideBufferNamePreludeModeFamilyCount,
};
use std::collections::BTreeMap;

pub(crate) fn build_fixed_policy_summary(
    records_payload: &[u8],
    payload_envelope_rows: &[PayloadEnvelopeRow],
    embedded_name_rows: &[EmbeddedNameRow],
) -> Option<SmpSavePlacedStructureDynamicSideBufferFixedPolicySummary> {
    let fixed_policy_rows = payload_envelope_rows
        .iter()
        .filter(|row| row.policy_chunk_len == Some(0x1a))
        .filter_map(|row| {
            let embedded_name_row = embedded_name_rows
                .iter()
                .find(|entry| entry.name_tag_relative_offset == row.name_tag_relative_offset)?;
            let policy_tag_relative_offset = row.policy_tag_relative_offset?;
            let policy_payload = records_payload
                .get(policy_tag_relative_offset + 4..policy_tag_relative_offset + 4 + 0x1a)?;
            let first_triplet_dwords = [
                read_u32_at(policy_payload, 0)?,
                read_u32_at(policy_payload, 4)?,
                read_u32_at(policy_payload, 8)?,
            ];
            let second_triplet_dwords = [
                read_u32_at(policy_payload, 12)?,
                read_u32_at(policy_payload, 16)?,
                read_u32_at(policy_payload, 20)?,
            ];
            let trailing_word = read_u16_at(policy_payload, 24)?;
            Some((
                row.name_tag_relative_offset,
                row.primary_name.clone(),
                row.secondary_name.clone(),
                embedded_name_row.prefix_leading_dword,
                embedded_name_row.prefix_trailing_word,
                embedded_name_row.prefix_separator_byte,
                first_triplet_dwords,
                second_triplet_dwords,
                trailing_word,
            ))
        })
        .collect::<Vec<_>>();
    let mut fixed_policy_trailing_word_counts = BTreeMap::<u16, usize>::new();
    for (_, _, _, _, _, _, _, _, trailing_word) in &fixed_policy_rows {
        *fixed_policy_trailing_word_counts
            .entry(*trailing_word)
            .or_default() += 1;
    }
    let dominant_fixed_policy_trailing_word = fixed_policy_trailing_word_counts
        .iter()
        .max_by(|(left_word, left_count), (right_word, right_count)| {
            left_count
                .cmp(right_count)
                .then_with(|| right_word.cmp(left_word))
        })
        .map(|(word, count)| (*word, *count));
    let mut fixed_policy_compact_prefix_groups = BTreeMap::<
        (u32, u16, u8),
        Vec<(
            usize,
            Option<String>,
            Option<String>,
            [u32; 3],
            [u32; 3],
            u16,
        )>,
    >::new();
    for (
        name_tag_relative_offset,
        primary_name,
        secondary_name,
        prefix_leading_dword,
        prefix_trailing_word,
        prefix_separator_byte,
        first_triplet_dwords,
        second_triplet_dwords,
        trailing_word,
    ) in &fixed_policy_rows
    {
        fixed_policy_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(),
                *first_triplet_dwords,
                *second_triplet_dwords,
                *trailing_word,
            ));
    }
    let fixed_policy_compact_prefix_correlations = fixed_policy_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 mode_family_counts = BTreeMap::<String, usize>::new();
                let mut policy_tuple_counts = BTreeMap::<([u32; 3], [u32; 3], u16), usize>::new();
                for (
                    _,
                    primary_name,
                    secondary_name,
                    first_triplet_dwords,
                    second_triplet_dwords,
                    trailing_word,
                ) in &rows
                {
                    *name_pair_counts
                        .entry((primary_name.clone(), secondary_name.clone()))
                        .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;
                    *policy_tuple_counts
                        .entry((
                            *first_triplet_dwords,
                            *second_triplet_dwords,
                            *trailing_word,
                        ))
                        .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_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));
                let dominant_policy_tuple = policy_tuple_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(
                        |((first_triplet_dwords, second_triplet_dwords, trailing_word), count)| {
                            (
                                *first_triplet_dwords,
                                *second_triplet_dwords,
                                *trailing_word,
                                *count,
                            )
                        },
                    );
                SmpSavePlacedStructureDynamicSideBufferFixedPolicyCompactPrefixCorrelation {
                    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_policy_tuple_count: policy_tuple_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_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(),
                    dominant_first_triplet_dwords_hex: dominant_policy_tuple
                        .as_ref()
                        .map(|(first_triplet_dwords, _, _, _)| {
                            first_triplet_dwords
                                .iter()
                                .map(|value| format!("0x{value:08x}"))
                                .collect()
                        })
                        .unwrap_or_default(),
                    dominant_second_triplet_dwords_hex: dominant_policy_tuple
                        .as_ref()
                        .map(|(_, second_triplet_dwords, _, _)| {
                            second_triplet_dwords
                                .iter()
                                .map(|value| format!("0x{value:08x}"))
                                .collect()
                        })
                        .unwrap_or_default(),
                    dominant_trailing_word: dominant_policy_tuple
                        .map(|(_, _, trailing_word, _)| trailing_word),
                    dominant_trailing_word_hex: dominant_policy_tuple
                        .map(|(_, _, trailing_word, _)| format!("0x{trailing_word:04x}")),
                    dominant_policy_tuple_count: dominant_policy_tuple
                        .map(|(_, _, _, count)| count)
                        .unwrap_or_default(),
                    mode_family_counts: mode_family_counts
                        .into_iter()
                        .map(|(mode_family, count)| {
                            SmpSavePlacedStructureDynamicSideBufferNamePreludeModeFamilyCount {
                                mode_family,
                                count,
                            }
                        })
                        .collect(),
                    sample_rows: rows
                        .iter()
                        .take(8)
                        .enumerate()
                        .map(
                            |(
                                sample_index,
                                (
                                    name_tag_relative_offset,
                                    primary_name,
                                    secondary_name,
                                    first_triplet_dwords,
                                    second_triplet_dwords,
                                    trailing_word,
                                ),
                            )| {
                                SmpSavePlacedStructureDynamicSideBufferFixedPolicySample {
                                    sample_index,
                                    name_tag_relative_offset: *name_tag_relative_offset,
                                    primary_name: primary_name.clone(),
                                    secondary_name: secondary_name.clone(),
                                    first_triplet_dwords_hex: first_triplet_dwords
                                        .iter()
                                        .map(|value| format!("0x{value:08x}"))
                                        .collect(),
                                    second_triplet_dwords_hex: second_triplet_dwords
                                        .iter()
                                        .map(|value| format!("0x{value:08x}"))
                                        .collect(),
                                    trailing_word: *trailing_word,
                                    trailing_word_hex: format!("0x{trailing_word:04x}"),
                                }
                            },
                        )
                        .collect(),
                }
            },
        )
        .take(8)
        .collect::<Vec<_>>();
    Some(SmpSavePlacedStructureDynamicSideBufferFixedPolicySummary {
        row_count_with_0x1a_policy_chunk: fixed_policy_rows.len(),
        unique_trailing_word_count: fixed_policy_trailing_word_counts.len(),
        dominant_trailing_word: dominant_fixed_policy_trailing_word.map(|(word, _)| word),
        dominant_trailing_word_hex: dominant_fixed_policy_trailing_word
            .map(|(word, _)| format!("0x{word:04x}")),
        dominant_trailing_word_count: dominant_fixed_policy_trailing_word
            .map(|(_, count)| count)
            .unwrap_or_default(),
        compact_prefix_correlations: fixed_policy_compact_prefix_correlations,
        sample_rows: fixed_policy_rows
            .iter()
            .take(8)
            .enumerate()
            .map(
                |(
                    sample_index,
                    (
                        name_tag_relative_offset,
                        primary_name,
                        secondary_name,
                        _,
                        _,
                        _,
                        first_triplet_dwords,
                        second_triplet_dwords,
                        trailing_word,
                    ),
                )| {
                    SmpSavePlacedStructureDynamicSideBufferFixedPolicySample {
                        sample_index,
                        name_tag_relative_offset: *name_tag_relative_offset,
                        primary_name: primary_name.clone(),
                        secondary_name: secondary_name.clone(),
                        first_triplet_dwords_hex: first_triplet_dwords
                            .iter()
                            .map(|value| format!("0x{value:08x}"))
                            .collect(),
                        second_triplet_dwords_hex: second_triplet_dwords
                            .iter()
                            .map(|value| format!("0x{value:08x}"))
                            .collect(),
                        trailing_word: *trailing_word,
                        trailing_word_hex: format!("0x{trailing_word:04x}"),
                    }
                },
            )
            .collect(),
    })
}
