use crate::documents::lowering::{
    chairman_target_import_blocker, company_target_import_blocker, lowered_record_decoded_actions,
    lowered_record_decoded_conditions, player_target_import_blocker,
};
use crate::documents::{RuntimeImportBlocker, RuntimeImportContext};
use crate::event::conditions::RuntimeCondition;
use crate::event::effects::RuntimeEffect;
use crate::event::targets::RuntimeChairmanTarget;
use crate::event::targets::{RuntimeCompanyTarget, RuntimePlayerTarget, RuntimeTerritoryTarget};
use crate::inspect::smp::events::{
    SmpLoadedPackedEventGroupedEffectRowSummary, SmpLoadedPackedEventRecordSummary,
};

pub(super) fn packed_record_import_blocker(
    record: &SmpLoadedPackedEventRecordSummary,
    runtime_context: &RuntimeImportContext,
) -> Option<RuntimeImportBlocker> {
    if record.decoded_actions.iter().any(|effect| {
        runtime_effect_uses_condition_true_company(effect)
            || runtime_effect_uses_condition_true_player(effect)
    }) && record.negative_sentinel_scope.is_none()
    {
        return Some(RuntimeImportBlocker::MissingConditionContext);
    }
    let lowered_conditions = match lowered_record_decoded_conditions(record, runtime_context) {
        Ok(conditions) => conditions,
        Err(blocker) => return Some(blocker),
    };
    if let Some(blocker) = lowered_conditions
        .iter()
        .find_map(|condition| runtime_condition_import_blocker(condition, runtime_context))
    {
        return Some(blocker);
    }
    let lowered_effects = match lowered_record_decoded_actions(record, runtime_context) {
        Ok(effects) => effects,
        Err(blocker) => return Some(blocker),
    };
    lowered_effects
        .iter()
        .find_map(|effect| runtime_effect_import_blocker(effect, runtime_context))
}

pub(super) fn runtime_condition_import_blocker(
    condition: &RuntimeCondition,
    runtime_context: &RuntimeImportContext,
) -> Option<RuntimeImportBlocker> {
    match condition {
        RuntimeCondition::WorldVariableThreshold { .. } => None,
        RuntimeCondition::CompanyNumericThreshold { target, .. } => {
            company_target_import_blocker(target, runtime_context)
        }
        RuntimeCondition::CompanyVariableThreshold { target, .. } => {
            company_target_import_blocker(target, runtime_context)
        }
        RuntimeCondition::PlayerVariableThreshold { target, .. } => {
            player_target_import_blocker(target, runtime_context)
        }
        RuntimeCondition::ChairmanNumericThreshold { target, .. } => {
            chairman_target_import_blocker(target, runtime_context)
        }
        RuntimeCondition::TerritoryNumericThreshold { target, .. } => {
            territory_target_import_blocker(target, runtime_context)
        }
        RuntimeCondition::TerritoryVariableThreshold { target, .. } => {
            territory_target_import_blocker(target, runtime_context)
        }
        RuntimeCondition::CompanyTerritoryNumericThreshold {
            target, territory, ..
        } => company_target_import_blocker(target, runtime_context)
            .or_else(|| territory_target_import_blocker(territory, runtime_context)),
        RuntimeCondition::SpecialConditionThreshold { .. }
        | RuntimeCondition::CandidateAvailabilityThreshold { .. }
        | RuntimeCondition::NamedLocomotiveAvailabilityThreshold { .. }
        | RuntimeCondition::NamedLocomotiveCostThreshold { .. }
        | RuntimeCondition::CargoProductionSlotThreshold { .. }
        | RuntimeCondition::CargoProductionTotalThreshold { .. }
        | RuntimeCondition::FactoryProductionTotalThreshold { .. }
        | RuntimeCondition::FarmMineProductionTotalThreshold { .. }
        | RuntimeCondition::OtherCargoProductionTotalThreshold { .. }
        | RuntimeCondition::LimitedTrackBuildingAmountThreshold { .. }
        | RuntimeCondition::TerritoryAccessCostThreshold { .. }
        | RuntimeCondition::EconomicStatusCodeThreshold { .. }
        | RuntimeCondition::WorldFlagEquals { .. } => None,
    }
}

pub(super) fn territory_target_import_blocker(
    target: &RuntimeTerritoryTarget,
    runtime_context: &RuntimeImportContext,
) -> Option<RuntimeImportBlocker> {
    if !runtime_context.has_territory_context {
        return Some(RuntimeImportBlocker::MissingTerritoryContext);
    }
    match target {
        RuntimeTerritoryTarget::AllTerritories => None,
        RuntimeTerritoryTarget::Ids { ids } => {
            if ids.is_empty() {
                Some(RuntimeImportBlocker::NamedTerritoryBinding)
            } else if !territory_ids_match_known_context(ids, runtime_context) {
                Some(RuntimeImportBlocker::NamedTerritoryBinding)
            } else {
                None
            }
        }
    }
}

pub(super) fn runtime_import_blocker_outcome(blocker: RuntimeImportBlocker) -> &'static str {
    match blocker {
        RuntimeImportBlocker::MissingCompanyContext => "blocked_missing_company_context",
        RuntimeImportBlocker::MissingSelectionContext => "blocked_missing_selection_context",
        RuntimeImportBlocker::MissingCompanyRoleContext => "blocked_missing_company_role_context",
        RuntimeImportBlocker::MissingPlayerContext => "blocked_missing_player_context",
        RuntimeImportBlocker::MissingPlayerSelectionContext => {
            "blocked_missing_player_selection_context"
        }
        RuntimeImportBlocker::MissingPlayerRoleContext => "blocked_missing_player_role_context",
        RuntimeImportBlocker::MissingChairmanContext => "blocked_missing_chairman_context",
        RuntimeImportBlocker::ChairmanTargetScope => "blocked_chairman_target_scope",
        RuntimeImportBlocker::MissingConditionContext => "blocked_missing_condition_context",
        RuntimeImportBlocker::MissingPlayerConditionContext => {
            "blocked_missing_player_condition_context"
        }
        RuntimeImportBlocker::CompanyConditionScopeDisabled => {
            "blocked_company_condition_scope_disabled"
        }
        RuntimeImportBlocker::MissingTerritoryContext => "blocked_missing_territory_context",
        RuntimeImportBlocker::NamedTerritoryBinding => "blocked_named_territory_binding",
        RuntimeImportBlocker::UnmappedOrdinaryCondition => "blocked_unmapped_ordinary_condition",
        RuntimeImportBlocker::UnmappedWorldCondition => "blocked_unmapped_world_condition",
        RuntimeImportBlocker::EvidenceBlockedDescriptor => "blocked_evidence_blocked_descriptor",
        RuntimeImportBlocker::MissingTrainContext => "blocked_missing_train_context",
        RuntimeImportBlocker::MissingTrainTerritoryContext => {
            "blocked_missing_train_territory_context"
        }
        RuntimeImportBlocker::MissingLocomotiveCatalogContext => {
            "blocked_missing_locomotive_catalog_context"
        }
    }
}

pub(super) fn territory_ids_match_known_context(
    ids: &[u32],
    runtime_context: &RuntimeImportContext,
) -> bool {
    ids.iter()
        .all(|territory_id| runtime_context.known_territory_ids.contains(territory_id))
}

pub(super) fn real_grouped_row_is_unsupported_territory_access_variant(
    row: &SmpLoadedPackedEventGroupedEffectRowSummary,
) -> bool {
    row.descriptor_id == 3 && !(row.row_shape == "bool_toggle" && row.raw_scalar_value != 0)
}

pub(super) fn real_grouped_row_is_unsupported_chairman_target_scope(
    row: &SmpLoadedPackedEventGroupedEffectRowSummary,
) -> bool {
    matches!(row.grouped_target_subject.as_deref(), Some("chairman"))
        && matches!(row.descriptor_id, 1 | 14)
        && row.notes.iter().any(|note| {
            note.starts_with("chairman row uses unsupported grouped target scope ordinal ")
        })
}

pub(super) fn real_grouped_row_is_unsupported_territory_access_scope(
    row: &SmpLoadedPackedEventGroupedEffectRowSummary,
) -> bool {
    row.descriptor_id == 3
        && row.row_shape == "bool_toggle"
        && row.raw_scalar_value != 0
        && row
            .notes
            .iter()
            .any(|note| note == "territory access row is missing company or territory scope")
}

pub(super) fn real_grouped_row_is_unsupported_confiscation_variant(
    row: &SmpLoadedPackedEventGroupedEffectRowSummary,
) -> bool {
    row.descriptor_id == 9 && !(row.row_shape == "bool_toggle" && row.raw_scalar_value != 0)
}

pub(super) fn real_grouped_row_is_unsupported_retire_train_variant(
    row: &SmpLoadedPackedEventGroupedEffectRowSummary,
) -> bool {
    row.descriptor_id == 15 && !(row.row_shape == "bool_toggle" && row.raw_scalar_value != 0)
}

pub(super) fn real_grouped_row_is_unsupported_retire_train_scope(
    row: &SmpLoadedPackedEventGroupedEffectRowSummary,
) -> bool {
    row.descriptor_id == 15
        && row.row_shape == "bool_toggle"
        && row.raw_scalar_value != 0
        && row
            .notes
            .iter()
            .any(|note| note == "retire train row is missing company and territory scope")
}

pub(super) fn runtime_effect_uses_condition_true_company(effect: &RuntimeEffect) -> bool {
    match effect {
        RuntimeEffect::SetCompanyCash { target, .. }
        | RuntimeEffect::SetCompanyVariable { target, .. }
        | RuntimeEffect::SetCompanyGovernanceScalar { target, .. }
        | RuntimeEffect::SetCompanyTerritoryAccess { target, .. }
        | RuntimeEffect::ConfiscateCompanyAssets { target }
        | RuntimeEffect::DeactivateCompany { target }
        | RuntimeEffect::SetCompanyTrackLayingCapacity { target, .. }
        | RuntimeEffect::AdjustCompanyCash { target, .. }
        | RuntimeEffect::AdjustCompanyDebt { target, .. } => {
            matches!(target, RuntimeCompanyTarget::ConditionTrueCompany)
        }
        RuntimeEffect::RetireTrains { company_target, .. } => matches!(
            company_target,
            Some(RuntimeCompanyTarget::ConditionTrueCompany)
        ),
        RuntimeEffect::AppendEventRecord { record } => record
            .effects
            .iter()
            .any(runtime_effect_uses_condition_true_company),
        RuntimeEffect::SetWorldFlag { .. }
        | RuntimeEffect::SetWorldVariable { .. }
        | RuntimeEffect::SetWorldScalarOverride { .. }
        | RuntimeEffect::SetLimitedTrackBuildingAmount { .. }
        | RuntimeEffect::SetEconomicStatusCode { .. }
        | RuntimeEffect::SetPlayerCash { .. }
        | RuntimeEffect::SetPlayerVariable { .. }
        | RuntimeEffect::SetChairmanCash { .. }
        | RuntimeEffect::DeactivatePlayer { .. }
        | RuntimeEffect::DeactivateChairman { .. }
        | RuntimeEffect::SetCandidateAvailability { .. }
        | RuntimeEffect::SetNamedLocomotiveAvailability { .. }
        | RuntimeEffect::SetNamedLocomotiveAvailabilityValue { .. }
        | RuntimeEffect::SetNamedLocomotiveCost { .. }
        | RuntimeEffect::SetCargoPriceOverride { .. }
        | RuntimeEffect::SetCargoProductionOverride { .. }
        | RuntimeEffect::SetCargoProductionSlot { .. }
        | RuntimeEffect::SetTerritoryVariable { .. }
        | RuntimeEffect::SetTerritoryAccessCost { .. }
        | RuntimeEffect::SetSpecialCondition { .. }
        | RuntimeEffect::ActivateEventRecord { .. }
        | RuntimeEffect::DeactivateEventRecord { .. }
        | RuntimeEffect::RemoveEventRecord { .. } => false,
    }
}

pub(super) fn runtime_effect_uses_condition_true_player(effect: &RuntimeEffect) -> bool {
    match effect {
        RuntimeEffect::SetPlayerCash { target, .. }
        | RuntimeEffect::SetPlayerVariable { target, .. } => {
            matches!(target, RuntimePlayerTarget::ConditionTruePlayer)
        }
        RuntimeEffect::DeactivatePlayer { target } => {
            matches!(target, RuntimePlayerTarget::ConditionTruePlayer)
        }
        RuntimeEffect::SetChairmanCash { .. } | RuntimeEffect::DeactivateChairman { .. } => false,
        RuntimeEffect::AppendEventRecord { record } => record
            .effects
            .iter()
            .any(runtime_effect_uses_condition_true_player),
        _ => false,
    }
}

pub(super) fn record_uses_condition_true_chairman(
    record: &SmpLoadedPackedEventRecordSummary,
) -> bool {
    record
        .decoded_actions
        .iter()
        .any(runtime_effect_uses_condition_true_chairman)
}

pub(super) fn runtime_effect_uses_condition_true_chairman(effect: &RuntimeEffect) -> bool {
    match effect {
        RuntimeEffect::SetChairmanCash { target, .. }
        | RuntimeEffect::DeactivateChairman { target } => {
            matches!(target, RuntimeChairmanTarget::ConditionTrueChairman)
        }
        RuntimeEffect::AppendEventRecord { record } => record
            .effects
            .iter()
            .any(runtime_effect_uses_condition_true_chairman),
        _ => false,
    }
}

pub(super) fn runtime_effect_import_blocker(
    effect: &RuntimeEffect,
    runtime_context: &RuntimeImportContext,
) -> Option<RuntimeImportBlocker> {
    match effect {
        RuntimeEffect::SetCompanyCash { target, .. }
        | RuntimeEffect::SetCompanyVariable { target, .. }
        | RuntimeEffect::SetCompanyGovernanceScalar { target, .. }
        | RuntimeEffect::SetCompanyTerritoryAccess { target, .. }
        | RuntimeEffect::ConfiscateCompanyAssets { target }
        | RuntimeEffect::DeactivateCompany { target }
        | RuntimeEffect::SetCompanyTrackLayingCapacity { target, .. }
        | RuntimeEffect::AdjustCompanyCash { target, .. }
        | RuntimeEffect::AdjustCompanyDebt { target, .. } => {
            if matches!(effect, RuntimeEffect::ConfiscateCompanyAssets { .. })
                && !runtime_context.has_train_context
            {
                Some(RuntimeImportBlocker::MissingTrainContext)
            } else if let RuntimeEffect::SetCompanyTerritoryAccess { territory, .. } = effect {
                company_target_import_blocker(target, runtime_context)
                    .or_else(|| territory_target_import_blocker(territory, runtime_context))
            } else {
                company_target_import_blocker(target, runtime_context)
            }
        }
        RuntimeEffect::SetPlayerCash { target, .. }
        | RuntimeEffect::SetPlayerVariable { target, .. }
        | RuntimeEffect::DeactivatePlayer { target } => {
            player_target_import_blocker(target, runtime_context)
        }
        RuntimeEffect::SetTerritoryVariable { target, .. } => {
            territory_target_import_blocker(target, runtime_context)
        }
        RuntimeEffect::SetChairmanCash { target, .. }
        | RuntimeEffect::DeactivateChairman { target } => {
            chairman_target_import_blocker(target, runtime_context)
        }
        RuntimeEffect::RetireTrains {
            company_target,
            territory_target,
            ..
        } => {
            if !runtime_context.has_train_context {
                return Some(RuntimeImportBlocker::MissingTrainContext);
            }
            if territory_target.is_some() && !runtime_context.has_train_territory_context {
                return Some(RuntimeImportBlocker::MissingTrainTerritoryContext);
            }
            company_target
                .as_ref()
                .and_then(|target| company_target_import_blocker(target, runtime_context))
                .or_else(|| {
                    territory_target
                        .as_ref()
                        .and_then(|target| territory_target_import_blocker(target, runtime_context))
                })
        }
        RuntimeEffect::AppendEventRecord { record } => record
            .effects
            .iter()
            .find_map(|nested| runtime_effect_import_blocker(nested, runtime_context)),
        RuntimeEffect::SetWorldFlag { .. }
        | RuntimeEffect::SetWorldVariable { .. }
        | RuntimeEffect::SetWorldScalarOverride { .. }
        | RuntimeEffect::SetLimitedTrackBuildingAmount { .. }
        | RuntimeEffect::SetEconomicStatusCode { .. }
        | RuntimeEffect::SetCandidateAvailability { .. }
        | RuntimeEffect::SetNamedLocomotiveAvailability { .. }
        | RuntimeEffect::SetNamedLocomotiveAvailabilityValue { .. }
        | RuntimeEffect::SetNamedLocomotiveCost { .. }
        | RuntimeEffect::SetCargoPriceOverride { .. }
        | RuntimeEffect::SetCargoProductionOverride { .. }
        | RuntimeEffect::SetCargoProductionSlot { .. }
        | RuntimeEffect::SetTerritoryAccessCost { .. }
        | RuntimeEffect::SetSpecialCondition { .. }
        | RuntimeEffect::ActivateEventRecord { .. }
        | RuntimeEffect::DeactivateEventRecord { .. }
        | RuntimeEffect::RemoveEventRecord { .. } => None,
    }
}

pub(super) fn classify_real_grouped_company_targets(
    record: &SmpLoadedPackedEventRecordSummary,
) -> Vec<Option<RuntimeCompanyTarget>> {
    let Some(control) = &record.compact_control else {
        return Vec::new();
    };

    control
        .grouped_target_scope_ordinals_0x7fb
        .iter()
        .enumerate()
        .map(|(group_index, ordinal)| {
            if !record
                .grouped_effect_rows
                .iter()
                .any(|row| row.group_index == group_index)
            {
                return None;
            }
            classify_real_grouped_company_target(*ordinal)
        })
        .collect()
}

pub(super) fn classify_real_grouped_company_target(ordinal: u8) -> Option<RuntimeCompanyTarget> {
    match ordinal {
        0 => Some(RuntimeCompanyTarget::ConditionTrueCompany),
        1 => Some(RuntimeCompanyTarget::SelectedCompany),
        2 => Some(RuntimeCompanyTarget::HumanCompanies),
        3 => Some(RuntimeCompanyTarget::AiCompanies),
        _ => None,
    }
}
