use crate::inspect::smp::events::*;
use crate::inspect::smp::*;

pub(in crate::inspect::smp) fn parse_real_condition_row_summary(
    row_bytes: &[u8],
    row_index: usize,
    candidate_name: Option<String>,
) -> Option<SmpLoadedPackedEventConditionRowSummary> {
    let raw_condition_id = read_u32_at(row_bytes, 0)? as i32;
    let subtype = read_u8_at(row_bytes, 4)?;
    let flag_bytes = row_bytes
        .get(5..PACKED_EVENT_REAL_CONDITION_ROW_LEN)?
        .to_vec();
    let candidate_name_display = candidate_name.clone();
    let candidate_name_ref = candidate_name_display.as_deref();
    let ordinary_metadata = real_ordinary_condition_metadata(raw_condition_id);
    let comparator = ordinary_metadata
        .and_then(|_| decode_real_condition_comparator(subtype))
        .map(condition_comparator_label);
    let metric = ordinary_metadata
        .map(|metadata| real_ordinary_condition_metric_label(metadata, candidate_name_ref));
    let threshold = ordinary_metadata.and_then(|_| decode_real_condition_threshold(&flag_bytes));
    let requires_candidate_name_binding = ordinary_metadata.is_some_and(|metadata| {
        matches!(
            metadata.kind,
            RealOrdinaryConditionKind::Numeric(
                RealOrdinaryConditionMetric::Territory(_)
                    | RealOrdinaryConditionMetric::CompanyTerritory(_)
            )
        ) && candidate_name.is_some()
    });
    let mut notes = Vec::new();
    if raw_condition_id < 0 {
        notes.push("negative sentinel-style condition row id".to_string());
    }
    if candidate_name.is_some() {
        notes.push("condition row carries candidate-name side string".to_string());
    }
    if ordinary_metadata.is_none() && raw_condition_id >= 0 {
        notes.push(
            "ordinary condition id is not yet recovered in the checked-in condition table"
                .to_string(),
        );
    }
    if ordinary_metadata.is_some_and(|metadata| {
        matches!(
            metadata.kind,
            RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::CandidateAvailability)
        ) && candidate_name.is_none()
    }) {
        notes.push(
            "candidate-availability condition row is missing its candidate-name side string"
                .to_string(),
        );
    }
    if ordinary_metadata.is_some_and(|metadata| {
        matches!(
            metadata.kind,
            RealOrdinaryConditionKind::WorldState(
                RealWorldConditionKind::NamedLocomotiveAvailability
                    | RealWorldConditionKind::NamedLocomotiveCost
            )
        ) && candidate_name.is_none()
    }) {
        notes.push("named locomotive condition row is missing its side-string binding".to_string());
    }
    if ordinary_metadata.is_some_and(|metadata| {
        matches!(
            metadata.kind,
            RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::CargoProductionSlot)
        ) && candidate_name.is_none()
    }) {
        notes.push(
            "named cargo-production condition row is missing its side-string binding".to_string(),
        );
    }
    if ordinary_metadata.is_some_and(|metadata| {
        matches!(
            metadata.kind,
            RealOrdinaryConditionKind::WorldState(
                RealWorldConditionKind::FactoryProductionTotal
                    | RealWorldConditionKind::FarmMineProductionTotal
                    | RealWorldConditionKind::OtherCargoProductionTotal
            )
        )
    }) {
        notes.push(
            "checked-in RT3.lng label is known, but this cargo aggregate condition family is not yet lowered"
                .to_string(),
        );
    }
    if ordinary_metadata.is_some_and(|metadata| {
        matches!(
            metadata.kind,
            RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::CargoProductionSlot)
        ) && candidate_name_ref
            .and_then(recovered_cargo_production_slot_from_condition_name)
            .is_none()
    }) {
        notes.push(
            "named cargo-production condition side string does not yet map to a checked-in cargo slot"
                .to_string(),
        );
    }
    Some(SmpLoadedPackedEventConditionRowSummary {
        row_index,
        raw_condition_id,
        subtype,
        flag_bytes,
        candidate_name,
        comparator,
        metric,
        semantic_family: ordinary_metadata
            .map(|metadata| real_ordinary_condition_semantic_family(metadata).to_string()),
        semantic_preview: ordinary_metadata.and_then(|metadata| {
            threshold.map(|value| {
                let comparator_text = decode_real_condition_comparator(subtype)
                    .map(condition_comparator_symbol)
                    .unwrap_or("?");
                let metric_label =
                    real_ordinary_condition_metric_label(metadata, candidate_name_ref);
                format!("Test {} {} {}", metric_label, comparator_text, value)
            })
        }),
        recovered_cargo_slot: ordinary_metadata.and_then(|metadata| match metadata.kind {
            RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::CargoProductionSlot) => {
                candidate_name_ref.and_then(recovered_cargo_production_slot_from_condition_name)
            }
            _ => None,
        }),
        recovered_cargo_class: ordinary_metadata.and_then(|metadata| match metadata.kind {
            RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::CargoProductionSlot) => {
                candidate_name_ref
                    .and_then(recovered_cargo_production_slot_from_condition_name)
                    .and_then(known_cargo_slot_definition)
                    .map(|definition| runtime_cargo_class_name(definition.cargo_class).to_string())
            }
            RealOrdinaryConditionKind::WorldState(
                RealWorldConditionKind::FactoryProductionTotal,
            ) => Some("factory".to_string()),
            RealOrdinaryConditionKind::WorldState(
                RealWorldConditionKind::FarmMineProductionTotal,
            ) => Some("farm_mine".to_string()),
            RealOrdinaryConditionKind::WorldState(
                RealWorldConditionKind::OtherCargoProductionTotal,
            ) => Some("other".to_string()),
            _ => None,
        }),
        requires_candidate_name_binding,
        notes,
    })
}

pub(in crate::inspect::smp) fn decode_real_condition_rows(
    rows: &[SmpLoadedPackedEventConditionRowSummary],
    negative_sentinel_scope: Option<&SmpLoadedPackedEventNegativeSentinelScopeSummary>,
) -> Vec<RuntimeCondition> {
    rows.iter()
        .filter(|row| row.raw_condition_id >= 0)
        .filter_map(|row| decode_real_condition_row(row, negative_sentinel_scope))
        .collect()
}

pub(in crate::inspect::smp) fn decode_real_condition_row(
    row: &SmpLoadedPackedEventConditionRowSummary,
    negative_sentinel_scope: Option<&SmpLoadedPackedEventNegativeSentinelScopeSummary>,
) -> Option<RuntimeCondition> {
    let metadata = real_ordinary_condition_metadata(row.raw_condition_id)?;
    let comparator = decode_real_condition_comparator(row.subtype)?;
    let value = decode_real_condition_threshold(&row.flag_bytes)?;
    match metadata.kind {
        RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::WorldVariable(index)) => {
            Some(RuntimeCondition::WorldVariableThreshold {
                index,
                comparator,
                value,
            })
        }
        RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Company(metric)) => {
            Some(RuntimeCondition::CompanyNumericThreshold {
                target: RuntimeCompanyTarget::ConditionTrueCompany,
                metric,
                comparator,
                value,
            })
        }
        RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::CompanyVariable(index)) => {
            Some(RuntimeCondition::CompanyVariableThreshold {
                target: RuntimeCompanyTarget::ConditionTrueCompany,
                index,
                comparator,
                value,
            })
        }
        RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::PlayerVariable(index)) => {
            negative_sentinel_scope.and_then(|scope| {
                real_condition_player_target(scope).map(|target| {
                    RuntimeCondition::PlayerVariableThreshold {
                        target,
                        index,
                        comparator,
                        value,
                    }
                })
            })
        }
        RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Chairman(metric)) => {
            negative_sentinel_scope.and_then(|scope| {
                real_condition_chairman_target(scope).map(|target| {
                    RuntimeCondition::ChairmanNumericThreshold {
                        target,
                        metric,
                        comparator,
                        value,
                    }
                })
            })
        }
        RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::TerritoryVariable(
            index,
        )) => negative_sentinel_scope
            .filter(|scope| scope.territory_scope_selector_is_0x63)
            .map(|_| RuntimeCondition::TerritoryVariableThreshold {
                target: RuntimeTerritoryTarget::AllTerritories,
                index,
                comparator,
                value,
            }),
        RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::Territory(metric)) => {
            negative_sentinel_scope
                .filter(|scope| scope.territory_scope_selector_is_0x63)
                .map(|_| RuntimeCondition::TerritoryNumericThreshold {
                    target: RuntimeTerritoryTarget::AllTerritories,
                    metric,
                    comparator,
                    value,
                })
        }
        RealOrdinaryConditionKind::Numeric(RealOrdinaryConditionMetric::CompanyTerritory(
            metric,
        )) => negative_sentinel_scope
            .filter(|scope| scope.territory_scope_selector_is_0x63)
            .map(|_| RuntimeCondition::CompanyTerritoryNumericThreshold {
                target: RuntimeCompanyTarget::ConditionTrueCompany,
                territory: RuntimeTerritoryTarget::AllTerritories,
                metric,
                comparator,
                value,
            }),
        RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::SpecialCondition {
            label,
        }) => Some(RuntimeCondition::SpecialConditionThreshold {
            label: label.to_string(),
            comparator,
            value,
        }),
        RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::CandidateAvailability) => row
            .candidate_name
            .as_ref()
            .map(|name| RuntimeCondition::CandidateAvailabilityThreshold {
                name: name.clone(),
                comparator,
                value,
            }),
        RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::CargoProductionSlot) => {
            row.candidate_name.as_ref().and_then(|name| {
                recovered_cargo_production_slot_from_condition_name(name).map(|slot| {
                    let label = known_cargo_slot_definition(slot)
                        .map(|definition| definition.label.to_string())
                        .unwrap_or_else(|| name.clone());
                    RuntimeCondition::CargoProductionSlotThreshold {
                        slot,
                        label,
                        comparator,
                        value,
                    }
                })
            })
        }
        RealOrdinaryConditionKind::WorldState(
            RealWorldConditionKind::NamedLocomotiveAvailability,
        ) => row.candidate_name.as_ref().map(|name| {
            RuntimeCondition::NamedLocomotiveAvailabilityThreshold {
                name: name.clone(),
                comparator,
                value,
            }
        }),
        RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::NamedLocomotiveCost) => row
            .candidate_name
            .as_ref()
            .map(|name| RuntimeCondition::NamedLocomotiveCostThreshold {
                name: name.clone(),
                comparator,
                value,
            }),
        RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::CargoProductionTotal) => {
            Some(RuntimeCondition::CargoProductionTotalThreshold { comparator, value })
        }
        RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::FactoryProductionTotal) => {
            Some(RuntimeCondition::FactoryProductionTotalThreshold { comparator, value })
        }
        RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::FarmMineProductionTotal) => {
            Some(RuntimeCondition::FarmMineProductionTotalThreshold { comparator, value })
        }
        RealOrdinaryConditionKind::WorldState(
            RealWorldConditionKind::OtherCargoProductionTotal,
        ) => Some(RuntimeCondition::OtherCargoProductionTotalThreshold { comparator, value }),
        RealOrdinaryConditionKind::WorldState(
            RealWorldConditionKind::LimitedTrackBuildingAmount,
        ) => Some(RuntimeCondition::LimitedTrackBuildingAmountThreshold { comparator, value }),
        RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::TerritoryAccessCost) => {
            Some(RuntimeCondition::TerritoryAccessCostThreshold { comparator, value })
        }
        RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::EconomicStatus) => {
            Some(RuntimeCondition::EconomicStatusCodeThreshold { comparator, value })
        }
        RealOrdinaryConditionKind::WorldState(RealWorldConditionKind::WorldFlag { key }) => {
            decode_world_flag_condition(comparator, value, key)
        }
    }
}

pub(in crate::inspect::smp) fn decode_world_flag_condition(
    comparator: RuntimeConditionComparator,
    value: i64,
    key: &'static str,
) -> Option<RuntimeCondition> {
    let bool_value = match (comparator, value) {
        (RuntimeConditionComparator::Eq, 0) | (RuntimeConditionComparator::Ne, 1) => false,
        (RuntimeConditionComparator::Eq, 1) | (RuntimeConditionComparator::Ne, 0) => true,
        _ => return None,
    };
    Some(RuntimeCondition::WorldFlagEquals {
        key: key.to_string(),
        value: bool_value,
    })
}

pub(in crate::inspect::smp) fn real_condition_chairman_target(
    scope: &SmpLoadedPackedEventNegativeSentinelScopeSummary,
) -> Option<RuntimeChairmanTarget> {
    match scope.player_test_scope {
        RuntimePlayerConditionTestScope::AllPlayers => Some(RuntimeChairmanTarget::AllActive),
        RuntimePlayerConditionTestScope::SelectedPlayerOnly => {
            Some(RuntimeChairmanTarget::SelectedChairman)
        }
        RuntimePlayerConditionTestScope::AiPlayersOnly => Some(RuntimeChairmanTarget::AiChairmen),
        RuntimePlayerConditionTestScope::HumanPlayersOnly => {
            Some(RuntimeChairmanTarget::HumanChairmen)
        }
        RuntimePlayerConditionTestScope::Disabled => None,
    }
}

pub(in crate::inspect::smp) fn real_condition_player_target(
    scope: &SmpLoadedPackedEventNegativeSentinelScopeSummary,
) -> Option<RuntimePlayerTarget> {
    match scope.player_test_scope {
        RuntimePlayerConditionTestScope::AllPlayers => Some(RuntimePlayerTarget::AllActive),
        RuntimePlayerConditionTestScope::SelectedPlayerOnly => {
            Some(RuntimePlayerTarget::SelectedPlayer)
        }
        RuntimePlayerConditionTestScope::AiPlayersOnly => Some(RuntimePlayerTarget::AiPlayers),
        RuntimePlayerConditionTestScope::HumanPlayersOnly => {
            Some(RuntimePlayerTarget::HumanPlayers)
        }
        RuntimePlayerConditionTestScope::Disabled => None,
    }
}
