use std::collections::BTreeMap;

use super::super::RuntimeImportContext;
use super::super::lowering::project_packed_event_collection;
use super::{ChairmanProjection, CompanyProjection};
use crate::event::packed::RuntimePackedEventCollectionSummary;
use crate::event::records::RuntimeEventRecord;
use crate::inspect::smp::save_load::SmpLoadedSaveSlice;
use crate::state::{
    RuntimeCargoCatalogEntry, RuntimeCompanyControllerKind, RuntimeLocomotiveCatalogEntry,
};

pub(super) struct PackedEventProjection {
    pub(super) packed_event_collection: Option<RuntimePackedEventCollectionSummary>,
    pub(super) event_runtime_records: Vec<RuntimeEventRecord>,
}

pub(super) fn project_event_runtime(
    save_slice: &SmpLoadedSaveSlice,
    runtime_context: &RuntimeImportContext,
    company_projection: &CompanyProjection,
    chairman_projection: &ChairmanProjection,
    locomotive_catalog: Option<&[RuntimeLocomotiveCatalogEntry]>,
    cargo_catalog: Option<&[RuntimeCargoCatalogEntry]>,
    metadata: &mut BTreeMap<String, String>,
) -> Result<PackedEventProjection, String> {
    let companies = &company_projection.companies;
    let has_company_projection = company_projection.has_company_projection;
    let has_company_selection_override = company_projection.has_company_selection_override;
    let selected_company_id = company_projection.selected_company_id;
    let chairman_profiles = &chairman_projection.chairman_profiles;
    let has_chairman_projection = chairman_projection.has_chairman_projection;
    let has_chairman_selection_override = chairman_projection.has_chairman_selection_override;
    let selected_chairman_profile_id = chairman_projection.selected_chairman_profile_id;
    let locomotive_catalog = locomotive_catalog.map(|value| value.to_vec());
    let mut event_import_context = runtime_context.clone();
    if has_company_projection {
        event_import_context.known_company_ids =
            companies.iter().map(|company| company.company_id).collect();
        event_import_context.selected_company_id = selected_company_id;
        event_import_context.has_complete_company_controller_context = !companies.is_empty()
            && companies
                .iter()
                .all(|company| company.controller_kind != RuntimeCompanyControllerKind::Unknown);
    } else if has_company_selection_override {
        event_import_context.selected_company_id = selected_company_id;
    }
    if has_chairman_projection {
        event_import_context.known_chairman_profile_ids = chairman_profiles
            .iter()
            .map(|profile| profile.profile_id)
            .collect();
        event_import_context.selected_chairman_profile_id = selected_chairman_profile_id;
    } else if has_chairman_selection_override {
        event_import_context.selected_chairman_profile_id = selected_chairman_profile_id;
    }
    if let Some(catalog) = &locomotive_catalog {
        event_import_context.locomotive_catalog_names_by_id = catalog
            .iter()
            .map(|entry| (entry.locomotive_id, entry.name.clone()))
            .collect();
    }

    let (packed_event_collection, event_runtime_records) = project_packed_event_collection(
        save_slice,
        &event_import_context,
        cargo_catalog.as_deref().unwrap_or(&[]),
    )?;
    if let Some(summary) = &save_slice.event_runtime_collection {
        metadata.insert(
            "save_slice.event_runtime_collection_source_kind".to_string(),
            summary.source_kind.clone(),
        );
        metadata.insert(
            "save_slice.event_runtime_collection_version_hex".to_string(),
            summary.packed_state_version_hex.clone(),
        );
        metadata.insert(
            "save_slice.event_runtime_collection_record_count".to_string(),
            summary.live_record_count.to_string(),
        );
        metadata.insert(
            "save_slice.event_runtime_collection_decoded_record_count".to_string(),
            summary.decoded_record_count.to_string(),
        );
        metadata.insert(
            "save_slice.event_runtime_collection_imported_runtime_record_count".to_string(),
            event_runtime_records.len().to_string(),
        );
    }

    Ok(PackedEventProjection {
        packed_event_collection,
        event_runtime_records,
    })
}
