use std::collections::{BTreeMap, BTreeSet};

use serde::{Deserialize, Serialize};

use crate::event::packed::RuntimePackedEventCollectionSummary;
use crate::event::records::RuntimeEventRecord;
use crate::inspect::smp::save_load::SmpLoadedSaveSlice;
use crate::state::{
    RuntimeCargoCatalogEntry, RuntimeChairmanProfile, RuntimeCompany, RuntimeCompanyControllerKind,
    RuntimeCompanyMarketState, RuntimeLocomotiveCatalogEntry, RuntimeSaveProfileState,
    RuntimeState, RuntimeWorldRestoreState,
};
use crate::state::{RuntimeNearCityAcquisitionRegion, RuntimeNearCityAcquisitionSite};

pub const STATE_INPUT_DOCUMENT_FORMAT_VERSION: u32 = 1;
pub const SAVE_SLICE_DOCUMENT_FORMAT_VERSION: u32 = 1;
pub const OVERLAY_IMPORT_DOCUMENT_FORMAT_VERSION: u32 = 1;

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct RuntimeStateInputSource {
    #[serde(default)]
    pub description: Option<String>,
    #[serde(default)]
    pub source_binary: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RuntimeStateInputDocument {
    pub format_version: u32,
    pub input_id: String,
    #[serde(default)]
    pub source: RuntimeStateInputSource,
    pub state: RuntimeState,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct RuntimeSaveSliceDocumentSource {
    #[serde(default)]
    pub description: Option<String>,
    #[serde(default)]
    pub original_save_filename: Option<String>,
    #[serde(default)]
    pub original_save_sha256: Option<String>,
    #[serde(default)]
    pub notes: Vec<String>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RuntimeSaveSliceDocument {
    pub format_version: u32,
    pub save_slice_id: String,
    #[serde(default)]
    pub source: RuntimeSaveSliceDocumentSource,
    pub save_slice: SmpLoadedSaveSlice,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct RuntimeOverlayImportDocumentSource {
    #[serde(default)]
    pub description: Option<String>,
    #[serde(default)]
    pub notes: Vec<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RuntimeOverlayImportDocument {
    pub format_version: u32,
    pub import_id: String,
    #[serde(default)]
    pub source: RuntimeOverlayImportDocumentSource,
    pub base_snapshot_path: String,
    pub save_slice_path: String,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RuntimeStateInput {
    pub input_id: String,
    pub description: Option<String>,
    pub state: RuntimeState,
}

#[derive(Debug)]
pub(super) struct SaveSliceProjection {
    pub(super) world_flags: BTreeMap<String, bool>,
    pub(super) save_profile: RuntimeSaveProfileState,
    pub(super) world_restore: RuntimeWorldRestoreState,
    pub(super) metadata: BTreeMap<String, String>,
    pub(super) packed_event_collection: Option<RuntimePackedEventCollectionSummary>,
    pub(super) event_runtime_records: Vec<RuntimeEventRecord>,
    pub(super) companies: Vec<RuntimeCompany>,
    pub(super) has_company_projection: bool,
    pub(super) has_company_selection_override: bool,
    pub(super) selected_company_id: Option<u32>,
    pub(super) company_market_state: BTreeMap<u32, RuntimeCompanyMarketState>,
    pub(super) company_periodic_side_latch_state:
        BTreeMap<u32, crate::state::RuntimeCompanyPeriodicSideLatchState>,
    pub(super) has_company_market_projection: bool,
    pub(super) world_issue_opinion_base_terms_raw_i32: Vec<i32>,
    pub(super) chairman_profiles: Vec<RuntimeChairmanProfile>,
    pub(super) has_chairman_projection: bool,
    pub(super) has_chairman_selection_override: bool,
    pub(super) selected_chairman_profile_id: Option<u32>,
    pub(super) chairman_issue_opinion_terms_raw_i32: BTreeMap<u32, Vec<i32>>,
    pub(super) chairman_personality_raw_u8: BTreeMap<u32, u8>,
    pub(super) near_city_acquisition_regions: Vec<RuntimeNearCityAcquisitionRegion>,
    pub(super) near_city_acquisition_sites: Vec<RuntimeNearCityAcquisitionSite>,
    pub(super) has_near_city_projection: bool,
    pub(super) candidate_availability: BTreeMap<String, u32>,
    pub(super) named_locomotive_availability: BTreeMap<String, u32>,
    pub(super) locomotive_catalog: Option<Vec<RuntimeLocomotiveCatalogEntry>>,
    pub(super) cargo_catalog: Option<Vec<RuntimeCargoCatalogEntry>>,
    pub(super) named_locomotive_cost: BTreeMap<String, u32>,
    pub(super) all_cargo_price_override: Option<u32>,
    pub(super) named_cargo_price_overrides: BTreeMap<String, u32>,
    pub(super) all_cargo_production_override: Option<u32>,
    pub(super) factory_cargo_production_override: Option<u32>,
    pub(super) farm_mine_cargo_production_override: Option<u32>,
    pub(super) named_cargo_production_overrides: BTreeMap<String, u32>,
    pub(super) cargo_production_overrides: BTreeMap<u32, u32>,
    pub(super) world_scalar_overrides: BTreeMap<String, i64>,
    pub(super) special_conditions: BTreeMap<String, u32>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum SaveSliceProjectionMode {
    Standalone,
    Overlay,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) struct RuntimeImportContext {
    pub(super) known_company_ids: BTreeSet<u32>,
    pub(super) selected_company_id: Option<u32>,
    pub(super) has_complete_company_controller_context: bool,
    pub(super) known_player_ids: BTreeSet<u32>,
    pub(super) selected_player_id: Option<u32>,
    pub(super) has_complete_player_controller_context: bool,
    pub(super) known_chairman_profile_ids: BTreeSet<u32>,
    pub(super) selected_chairman_profile_id: Option<u32>,
    pub(super) known_territory_ids: BTreeSet<u32>,
    pub(super) has_territory_context: bool,
    pub(super) territory_name_to_id: BTreeMap<String, u32>,
    pub(super) has_train_context: bool,
    pub(super) has_train_territory_context: bool,
    pub(super) locomotive_catalog_names_by_id: BTreeMap<u32, String>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum RuntimeImportBlocker {
    MissingCompanyContext,
    MissingSelectionContext,
    MissingCompanyRoleContext,
    MissingPlayerContext,
    MissingPlayerSelectionContext,
    MissingPlayerRoleContext,
    MissingChairmanContext,
    ChairmanTargetScope,
    MissingConditionContext,
    MissingPlayerConditionContext,
    CompanyConditionScopeDisabled,
    MissingTerritoryContext,
    NamedTerritoryBinding,
    UnmappedOrdinaryCondition,
    UnmappedWorldCondition,
    EvidenceBlockedDescriptor,
    MissingTrainContext,
    MissingTrainTerritoryContext,
    MissingLocomotiveCatalogContext,
}

impl RuntimeImportContext {
    pub(super) fn standalone() -> Self {
        Self {
            known_company_ids: BTreeSet::new(),
            selected_company_id: None,
            has_complete_company_controller_context: false,
            known_player_ids: BTreeSet::new(),
            selected_player_id: None,
            has_complete_player_controller_context: false,
            known_chairman_profile_ids: BTreeSet::new(),
            selected_chairman_profile_id: None,
            known_territory_ids: BTreeSet::new(),
            has_territory_context: false,
            territory_name_to_id: BTreeMap::new(),
            has_train_context: false,
            has_train_territory_context: false,
            locomotive_catalog_names_by_id: BTreeMap::new(),
        }
    }

    pub(super) fn from_runtime_state(state: &RuntimeState) -> Self {
        Self {
            known_company_ids: state
                .companies
                .iter()
                .map(|company| company.company_id)
                .collect(),
            selected_company_id: state.selected_company_id,
            has_complete_company_controller_context: !state.companies.is_empty()
                && state.companies.iter().all(|company| {
                    company.controller_kind != RuntimeCompanyControllerKind::Unknown
                }),
            known_player_ids: state
                .players
                .iter()
                .map(|player| player.player_id)
                .collect(),
            selected_player_id: state.selected_player_id,
            has_complete_player_controller_context: !state.players.is_empty()
                && state
                    .players
                    .iter()
                    .all(|player| player.controller_kind != RuntimeCompanyControllerKind::Unknown),
            known_chairman_profile_ids: state
                .chairman_profiles
                .iter()
                .map(|profile| profile.profile_id)
                .collect(),
            selected_chairman_profile_id: state.selected_chairman_profile_id,
            known_territory_ids: state
                .territories
                .iter()
                .map(|territory| territory.territory_id)
                .collect(),
            has_territory_context: !state.territories.is_empty(),
            territory_name_to_id: state
                .territories
                .iter()
                .filter_map(|territory| {
                    territory
                        .name
                        .as_ref()
                        .map(|name| (name.clone(), territory.territory_id))
                })
                .collect(),
            has_train_context: !state.trains.is_empty(),
            has_train_territory_context: state
                .trains
                .iter()
                .any(|train| train.territory_id.is_some()),
            locomotive_catalog_names_by_id: state
                .locomotive_catalog
                .iter()
                .map(|entry| (entry.locomotive_id, entry.name.clone()))
                .collect(),
        }
    }
}
