use std::collections::BTreeMap;

use super::embedded_names::EmbeddedNameRow;
use crate::inspect::smp::structures::*;

#[derive(Default)]
struct PrefixPatternAccumulator {
    count: usize,
    first_name_tag_relative_offset: usize,
    first_primary_name: Option<String>,
    first_secondary_name: Option<String>,
    section_like_primary_name_count: usize,
    cap_like_primary_name_count: usize,
    other_primary_name_count: usize,
}

#[derive(Default)]
struct NamePairAccumulator {
    count: usize,
    first_name_tag_relative_offset: usize,
    prefix_counts: BTreeMap<(u32, u16, u8), usize>,
}

pub(super) struct PrefixAndNamePairSummary {
    pub(super) compact_prefix_pattern_summaries:
        Vec<SmpSavePlacedStructureDynamicSideBufferPrefixPatternSummary>,
    pub(super) name_pair_summaries: Vec<SmpSavePlacedStructureDynamicSideBufferNamePairSummary>,
    pub(super) prefix_leading_dword_matching_embedded_profile_tag_count: usize,
}

pub(super) fn summarize_prefix_and_name_pairs(
    embedded_name_rows: &[EmbeddedNameRow],
) -> PrefixAndNamePairSummary {
    let mut compact_prefix_pattern_map =
        BTreeMap::<(u32, u16, u8), PrefixPatternAccumulator>::new();
    let mut name_pair_map = BTreeMap::<(String, String), NamePairAccumulator>::new();
    for row in embedded_name_rows {
        let entry = compact_prefix_pattern_map
            .entry((
                row.prefix_leading_dword,
                row.prefix_trailing_word,
                row.prefix_separator_byte,
            ))
            .or_insert_with(|| PrefixPatternAccumulator {
                first_name_tag_relative_offset: row.name_tag_relative_offset,
                first_primary_name: row.primary_name.clone(),
                first_secondary_name: row.secondary_name.clone(),
                ..Default::default()
            });
        entry.count += 1;
        match row.primary_name.as_deref() {
            Some(name) if name.ends_with("_Section.3dp") => {
                entry.section_like_primary_name_count += 1;
            }
            Some(name) if name.ends_with("_Cap.3dp") => {
                entry.cap_like_primary_name_count += 1;
            }
            _ => {
                entry.other_primary_name_count += 1;
            }
        }
        if let (Some(primary_name), Some(secondary_name)) =
            (row.primary_name.as_ref(), row.secondary_name.as_ref())
        {
            let entry = name_pair_map
                .entry((primary_name.clone(), secondary_name.clone()))
                .or_insert_with(|| NamePairAccumulator {
                    first_name_tag_relative_offset: row.name_tag_relative_offset,
                    ..Default::default()
                });
            entry.count += 1;
            *entry
                .prefix_counts
                .entry((
                    row.prefix_leading_dword,
                    row.prefix_trailing_word,
                    row.prefix_separator_byte,
                ))
                .or_default() += 1;
        }
    }

    let prefix_leading_dword_matching_embedded_profile_tag_count = embedded_name_rows
        .iter()
        .filter(|row| row.prefix_leading_dword == u32::from(SAVE_REGION_RECORD_PROFILE_TAG))
        .count();

    let mut compact_prefix_pattern_summaries = compact_prefix_pattern_map
        .into_iter()
        .map(
            |((prefix_leading_dword, prefix_trailing_word, prefix_separator_byte), accumulator)| {
                SmpSavePlacedStructureDynamicSideBufferPrefixPatternSummary {
                    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}"),
                    count: accumulator.count,
                    first_name_tag_relative_offset: accumulator.first_name_tag_relative_offset,
                    prefix_leading_dword_matches_embedded_profile_tag: prefix_leading_dword
                        == u32::from(SAVE_REGION_RECORD_PROFILE_TAG),
                    section_like_primary_name_count: accumulator.section_like_primary_name_count,
                    cap_like_primary_name_count: accumulator.cap_like_primary_name_count,
                    other_primary_name_count: accumulator.other_primary_name_count,
                    first_primary_name: accumulator.first_primary_name,
                    first_secondary_name: accumulator.first_secondary_name,
                }
            },
        )
        .collect::<Vec<_>>();
    compact_prefix_pattern_summaries.sort_by(|left, right| {
        right
            .count
            .cmp(&left.count)
            .then_with(|| {
                left.first_name_tag_relative_offset
                    .cmp(&right.first_name_tag_relative_offset)
            })
            .then_with(|| left.prefix_leading_dword.cmp(&right.prefix_leading_dword))
            .then_with(|| left.prefix_trailing_word.cmp(&right.prefix_trailing_word))
            .then_with(|| left.prefix_separator_byte.cmp(&right.prefix_separator_byte))
    });

    let mut name_pair_summaries = name_pair_map
        .into_iter()
        .filter_map(|((primary_name, secondary_name), accumulator)| {
            let dominant_prefix = accumulator.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))
                },
            )?;
            let (
                dominant_prefix_leading_dword,
                dominant_prefix_trailing_word,
                dominant_prefix_separator_byte,
            ) = *dominant_prefix.0;
            let dominant_prefix_count = *dominant_prefix.1;
            Some(SmpSavePlacedStructureDynamicSideBufferNamePairSummary {
                primary_name,
                secondary_name,
                count: accumulator.count,
                first_name_tag_relative_offset: accumulator.first_name_tag_relative_offset,
                unique_compact_prefix_pattern_count: accumulator.prefix_counts.len(),
                dominant_prefix_leading_dword,
                dominant_prefix_leading_dword_hex: format!("0x{dominant_prefix_leading_dword:08x}"),
                dominant_prefix_trailing_word,
                dominant_prefix_trailing_word_hex: format!("0x{dominant_prefix_trailing_word:04x}"),
                dominant_prefix_separator_byte,
                dominant_prefix_separator_byte_hex: format!(
                    "0x{dominant_prefix_separator_byte:02x}"
                ),
                dominant_prefix_count,
            })
        })
        .collect::<Vec<_>>();
    name_pair_summaries.sort_by(|left, right| {
        right
            .count
            .cmp(&left.count)
            .then_with(|| {
                left.first_name_tag_relative_offset
                    .cmp(&right.first_name_tag_relative_offset)
            })
            .then_with(|| left.primary_name.cmp(&right.primary_name))
            .then_with(|| left.secondary_name.cmp(&right.secondary_name))
    });

    PrefixAndNamePairSummary {
        compact_prefix_pattern_summaries,
        name_pair_summaries,
        prefix_leading_dword_matching_embedded_profile_tag_count,
    }
}
