use std::collections::BTreeMap;

use crate::inspect::smp::save_load::SmpLoadedSaveSlice;
use crate::state::{RuntimeCargoCatalogEntry, RuntimeLocomotiveCatalogEntry};

pub(super) struct CatalogProjection {
    pub(super) locomotive_catalog: Option<Vec<RuntimeLocomotiveCatalogEntry>>,
    pub(super) cargo_catalog: Option<Vec<RuntimeCargoCatalogEntry>>,
}

pub(super) fn project_catalogs(
    save_slice: &SmpLoadedSaveSlice,
    metadata: &mut BTreeMap<String, String>,
) -> CatalogProjection {
    let locomotive_catalog = if let Some(catalog) = &save_slice.locomotive_catalog {
        metadata.insert(
            "save_slice.locomotive_catalog_source_kind".to_string(),
            catalog.source_kind.clone(),
        );
        metadata.insert(
            "save_slice.locomotive_catalog_semantic_family".to_string(),
            catalog.semantic_family.clone(),
        );
        metadata.insert(
            "save_slice.locomotive_catalog_entry_count".to_string(),
            catalog.observed_entry_count.to_string(),
        );
        if let Some(entries_offset) = catalog.entries_offset {
            metadata.insert(
                "save_slice.locomotive_catalog_entries_offset".to_string(),
                entries_offset.to_string(),
            );
        }
        Some(
            catalog
                .entries
                .iter()
                .map(|entry| RuntimeLocomotiveCatalogEntry {
                    locomotive_id: entry.locomotive_id,
                    name: entry.name.clone(),
                })
                .collect::<Vec<_>>(),
        )
    } else if let Some(table) = &save_slice.named_locomotive_availability_table {
        metadata.insert(
            "save_slice.locomotive_catalog_source_kind".to_string(),
            "derived-from-named-locomotive-availability-table".to_string(),
        );
        metadata.insert(
            "save_slice.locomotive_catalog_semantic_family".to_string(),
            "scenario-save-derived-locomotive-catalog".to_string(),
        );
        metadata.insert(
            "save_slice.locomotive_catalog_entry_count".to_string(),
            table.observed_entry_count.to_string(),
        );
        if let Some(entries_offset) = table.entries_offset {
            metadata.insert(
                "save_slice.locomotive_catalog_entries_offset".to_string(),
                entries_offset.to_string(),
            );
        }
        Some(
            table
                .entries
                .iter()
                .enumerate()
                .map(|(index, entry)| RuntimeLocomotiveCatalogEntry {
                    locomotive_id: (index + 1) as u32,
                    name: entry.text.clone(),
                })
                .collect::<Vec<_>>(),
        )
    } else {
        None
    };

    let cargo_catalog = if let Some(catalog) = &save_slice.cargo_catalog {
        metadata.insert(
            "save_slice.cargo_catalog_source_kind".to_string(),
            catalog.source_kind.clone(),
        );
        metadata.insert(
            "save_slice.cargo_catalog_semantic_family".to_string(),
            catalog.semantic_family.clone(),
        );
        metadata.insert(
            "save_slice.cargo_catalog_entry_count".to_string(),
            catalog.observed_entry_count.to_string(),
        );
        if let Some(root_offset) = catalog.root_offset {
            metadata.insert(
                "save_slice.cargo_catalog_root_offset".to_string(),
                root_offset.to_string(),
            );
        }
        Some(
            catalog
                .entries
                .iter()
                .map(|entry| RuntimeCargoCatalogEntry {
                    slot_id: entry.slot_id,
                    label: entry.label.clone(),
                    cargo_class: entry.cargo_class,
                    supplied_token_stem: entry
                        .supplied_cargo_token_probable_high16_ascii_stem
                        .clone(),
                    demanded_token_stem: entry
                        .demanded_cargo_token_probable_high16_ascii_stem
                        .clone(),
                })
                .collect::<Vec<_>>(),
        )
    } else {
        None
    };

    CatalogProjection {
        locomotive_catalog,
        cargo_catalog,
    }
}
