use super::super::blockers::real_grouped_row_is_unsupported_chairman_target_scope;
use super::import_outcome::real_grouped_row_is_world_state_family;
use crate::documents::{RuntimeImportBlocker, RuntimeImportContext};
use crate::event::effects::RuntimeEffect;
use crate::event::targets::{RuntimeCargoPriceTarget, RuntimeCargoProductionTarget};
use crate::inspect::smp::events::{
    SmpLoadedPackedEventGroupedEffectRowSummary, SmpLoadedPackedEventRecordSummary,
};

pub(crate) fn lower_contextual_real_grouped_effects(
    record: &SmpLoadedPackedEventRecordSummary,
    runtime_context: &RuntimeImportContext,
) -> Result<Vec<RuntimeEffect>, RuntimeImportBlocker> {
    if record.payload_family != "real_packed_v1" || record.compact_control.is_none() {
        return Err(RuntimeImportBlocker::UnmappedWorldCondition);
    }

    let mut effects = Vec::with_capacity(record.grouped_effect_rows.len());
    for row in &record.grouped_effect_rows {
        if real_grouped_row_is_unsupported_chairman_target_scope(row) {
            return Err(RuntimeImportBlocker::ChairmanTargetScope);
        }
        if let Some(effect) = lower_contextual_cargo_price_effect(row)? {
            effects.push(effect);
            continue;
        }
        if let Some(effect) = lower_contextual_world_scalar_override_effect(row)? {
            effects.push(effect);
            continue;
        }
        if let Some(effect) = lower_contextual_runtime_variable_effect(row)? {
            effects.push(effect);
            continue;
        }
        if let Some(effect) = lower_contextual_cargo_production_effect(row)? {
            effects.push(effect);
            continue;
        }
        if let Some(effect) = lower_contextual_territory_access_cost_effect(row)? {
            effects.push(effect);
            continue;
        }
        if let Some(effect) = lower_contextual_locomotive_cost_effect(row, runtime_context)? {
            effects.push(effect);
            continue;
        }
        if let Some(effect) = lower_contextual_locomotive_availability_effect(row, runtime_context)?
        {
            effects.push(effect);
            continue;
        }
        return Err(if real_grouped_row_is_world_state_family(row) {
            RuntimeImportBlocker::UnmappedWorldCondition
        } else {
            RuntimeImportBlocker::UnmappedOrdinaryCondition
        });
    }

    if effects.is_empty() {
        return Err(RuntimeImportBlocker::UnmappedWorldCondition);
    }

    Ok(effects)
}

pub(crate) fn lower_contextual_cargo_price_effect(
    row: &SmpLoadedPackedEventGroupedEffectRowSummary,
) -> Result<Option<RuntimeEffect>, RuntimeImportBlocker> {
    if row.parameter_family.as_deref() != Some("cargo_price_scalar") {
        return Ok(None);
    }
    if row.row_shape != "scalar_assignment" {
        return Ok(None);
    }
    let Some(value) = u32::try_from(row.raw_scalar_value).ok() else {
        return Ok(None);
    };
    let target = if row.descriptor_id == 105 {
        RuntimeCargoPriceTarget::All
    } else if let Some(name) = row.recovered_cargo_label.as_deref() {
        RuntimeCargoPriceTarget::Named {
            name: name.to_string(),
        }
    } else {
        return Ok(None);
    };
    Ok(Some(RuntimeEffect::SetCargoPriceOverride { target, value }))
}

pub(crate) fn lower_contextual_world_scalar_override_effect(
    row: &SmpLoadedPackedEventGroupedEffectRowSummary,
) -> Result<Option<RuntimeEffect>, RuntimeImportBlocker> {
    if row.parameter_family.as_deref() != Some("world_scalar_override") {
        return Ok(None);
    }
    if row.row_shape != "scalar_assignment" {
        return Ok(None);
    }
    let Some(key) = row
        .descriptor_label
        .as_deref()
        .map(crate::inspect::smp::events::runtime_world_scalar_key_from_label)
    else {
        return Ok(None);
    };
    Ok(Some(RuntimeEffect::SetWorldScalarOverride {
        key,
        value: i64::from(row.raw_scalar_value),
    }))
}

pub(crate) fn lower_contextual_runtime_variable_effect(
    row: &SmpLoadedPackedEventGroupedEffectRowSummary,
) -> Result<Option<RuntimeEffect>, RuntimeImportBlocker> {
    if row.parameter_family.as_deref() != Some("runtime_variable_scalar") {
        return Ok(None);
    }
    if row.row_shape != "scalar_assignment" {
        return Ok(None);
    }
    let value = i64::from(row.raw_scalar_value);
    Ok(match row.descriptor_id {
        39..=42 => Some(RuntimeEffect::SetWorldVariable {
            index: row.descriptor_id - 38,
            value,
        }),
        _ => None,
    })
}

pub(crate) fn lower_contextual_locomotive_availability_effect(
    row: &SmpLoadedPackedEventGroupedEffectRowSummary,
    runtime_context: &RuntimeImportContext,
) -> Result<Option<RuntimeEffect>, RuntimeImportBlocker> {
    if row.parameter_family.as_deref() != Some("locomotive_availability_scalar") {
        return Ok(None);
    }
    if row.row_shape != "scalar_assignment" {
        return Ok(None);
    }
    let Some(value) = u32::try_from(row.raw_scalar_value).ok() else {
        return Ok(None);
    };
    let Some(locomotive_id) = row.recovered_locomotive_id else {
        return Ok(None);
    };
    let Some(name) = runtime_context
        .locomotive_catalog_names_by_id
        .get(&locomotive_id)
        .cloned()
    else {
        return Err(RuntimeImportBlocker::MissingLocomotiveCatalogContext);
    };
    Ok(Some(RuntimeEffect::SetNamedLocomotiveAvailabilityValue {
        name,
        value,
    }))
}

pub(crate) fn lower_contextual_cargo_production_effect(
    row: &SmpLoadedPackedEventGroupedEffectRowSummary,
) -> Result<Option<RuntimeEffect>, RuntimeImportBlocker> {
    if row.parameter_family.as_deref() != Some("cargo_production_scalar") {
        return Ok(None);
    }
    if row.row_shape != "scalar_assignment" {
        return Ok(None);
    }
    let Some(value) = u32::try_from(row.raw_scalar_value).ok() else {
        return Ok(None);
    };
    match row.descriptor_id {
        177 => Ok(Some(RuntimeEffect::SetCargoProductionOverride {
            target: RuntimeCargoProductionTarget::All,
            value,
        })),
        178 => Ok(Some(RuntimeEffect::SetCargoProductionOverride {
            target: RuntimeCargoProductionTarget::Factory,
            value,
        })),
        179 => Ok(Some(RuntimeEffect::SetCargoProductionOverride {
            target: RuntimeCargoProductionTarget::FarmMine,
            value,
        })),
        180..=229 => {
            let Some(name) = row.recovered_cargo_label.clone() else {
                return Err(RuntimeImportBlocker::EvidenceBlockedDescriptor);
            };
            Ok(Some(RuntimeEffect::SetCargoProductionOverride {
                target: RuntimeCargoProductionTarget::Named { name },
                value,
            }))
        }
        230..=240 => {
            let Some(slot) = row.descriptor_id.checked_sub(229) else {
                return Ok(None);
            };
            Ok(Some(RuntimeEffect::SetCargoProductionSlot { slot, value }))
        }
        _ => Ok(None),
    }
}

pub(crate) fn lower_contextual_territory_access_cost_effect(
    row: &SmpLoadedPackedEventGroupedEffectRowSummary,
) -> Result<Option<RuntimeEffect>, RuntimeImportBlocker> {
    if row.parameter_family.as_deref() != Some("territory_access_cost_scalar") {
        return Ok(None);
    }
    if row.row_shape != "scalar_assignment" {
        return Ok(None);
    }
    let Some(value) = u32::try_from(row.raw_scalar_value).ok() else {
        return Ok(None);
    };
    Ok(Some(RuntimeEffect::SetTerritoryAccessCost { value }))
}

pub(crate) fn lower_contextual_locomotive_cost_effect(
    row: &SmpLoadedPackedEventGroupedEffectRowSummary,
    runtime_context: &RuntimeImportContext,
) -> Result<Option<RuntimeEffect>, RuntimeImportBlocker> {
    if row.parameter_family.as_deref() != Some("locomotive_cost_scalar") {
        return Ok(None);
    }
    if row.row_shape != "scalar_assignment" {
        return Ok(None);
    }
    let value = u32::try_from(row.raw_scalar_value).ok();
    let Some(value) = value else {
        return Ok(None);
    };
    let Some(locomotive_id) = row.recovered_locomotive_id else {
        return Ok(None);
    };
    let Some(name) = runtime_context
        .locomotive_catalog_names_by_id
        .get(&locomotive_id)
        .cloned()
    else {
        return Err(RuntimeImportBlocker::MissingLocomotiveCatalogContext);
    };
    Ok(Some(RuntimeEffect::SetNamedLocomotiveCost { name, value }))
}
