use super::*;

#[test]
fn looks_up_checked_in_world_flag_descriptor_metadata() {
    let metadata =
        real_grouped_effect_descriptor_metadata(110).expect("descriptor metadata should exist");

    assert_eq!(metadata.label, "Disable Stock Buying and Selling");
    assert_eq!(metadata.parameter_family, "world_flag_toggle");
    assert_eq!(
        metadata.runtime_key,
        Some("world.disable_stock_buying_and_selling")
    );
    assert!(metadata.executable_in_runtime);
}

#[test]
fn looks_up_checked_in_credit_rating_descriptor_metadata() {
    let metadata =
        real_grouped_effect_descriptor_metadata(56).expect("descriptor metadata should exist");

    assert_eq!(metadata.label, "Credit Rating");
    assert_eq!(metadata.target_mask_bits, 0x0b);
    assert_eq!(metadata.parameter_family, "company_governance_scalar");
    assert_eq!(
        real_grouped_effect_runtime_status_name(metadata.runtime_status),
        "executable"
    );
    assert!(metadata.executable_in_runtime);
}

#[test]
fn checked_in_event_effect_table_covers_the_full_exported_descriptor_set() {
    let rows = checked_in_event_effect_descriptor_rows();
    assert_eq!(rows.len(), 614);
    for descriptor_id in 0..614_u32 {
        assert!(
            real_grouped_effect_descriptor_metadata(descriptor_id).is_some(),
            "descriptor {descriptor_id} should be recoverable from the checked-in effect table"
        );
    }
}

#[test]
fn looks_up_checked_in_prime_rate_descriptor_metadata() {
    let metadata =
        real_grouped_effect_descriptor_metadata(57).expect("descriptor metadata should exist");

    assert_eq!(metadata.label, "Prime Rate");
    assert_eq!(metadata.target_mask_bits, 0x0b);
    assert_eq!(metadata.parameter_family, "company_governance_scalar");
    assert_eq!(
        real_grouped_effect_runtime_status_name(metadata.runtime_status),
        "executable"
    );
    assert!(metadata.executable_in_runtime);
}

#[test]
fn classifies_shell_owned_finance_descriptors_from_checked_in_effect_table() {
    let stock_prices =
        real_grouped_effect_descriptor_metadata(55).expect("descriptor metadata should exist");
    assert_eq!(stock_prices.label, "Stock Prices");
    assert_eq!(
        stock_prices.parameter_family,
        "company_finance_shell_scalar"
    );
    assert_eq!(
        real_grouped_effect_runtime_status_name(stock_prices.runtime_status),
        "shell_owned"
    );
    assert!(!stock_prices.executable_in_runtime);

    let merger_premium =
        real_grouped_effect_descriptor_metadata(58).expect("descriptor metadata should exist");
    assert_eq!(merger_premium.label, "Merger Premium");
    assert_eq!(
        merger_premium.parameter_family,
        "company_finance_shell_scalar"
    );
    assert_eq!(
        real_grouped_effect_runtime_status_name(merger_premium.runtime_status),
        "shell_owned"
    );
    assert!(!merger_premium.executable_in_runtime);
}

#[test]
fn decodes_credit_rating_descriptor_into_company_governance_scalar_effect() {
    let row_bytes = build_real_grouped_effect_row(RealGroupedEffectRowSpec {
        descriptor_id: 56,
        raw_scalar_value: 640,
        opcode: 3,
        value_byte_0x09: 0,
        value_dword_0x0d: 0,
        value_byte_0x11: 0,
        value_byte_0x12: 0,
        value_word_0x14: 0,
        value_word_0x16: 0,
        locomotive_name: None,
    });
    let record_body = build_real_event_record(
        [b"Gov", b"", b"", b"", b"", b""],
        Some(RealCompactControlSpec {
            mode_byte_0x7ef: 7,
            primary_selector_0x7f0: 0,
            grouped_mode_0x7f4: 2,
            one_shot_header_0x7f5: 0,
            modifier_flag_0x7f9: 0,
            modifier_flag_0x7fa: 0,
            grouped_target_scope_ordinals_0x7fb: [1, 0, 0, 0],
            grouped_scope_checkboxes_0x7ff: [1, 0, 0, 0],
            summary_toggle_0x800: 1,
            grouped_territory_selectors_0x80f: [-1, -1, -1, -1],
        }),
        &[],
        [&[row_bytes], &[], &[], &[]],
    );

    let mut bytes = Vec::new();
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_METADATA_TAG.to_le_bytes());
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_PACKED_STATE_VERSION.to_le_bytes());
    let header_words = [1u32, 4, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    for word in header_words {
        bytes.extend_from_slice(&word.to_le_bytes());
    }
    bytes.extend_from_slice(&[0x00, 0x00]);
    bytes.extend_from_slice(&[0xaa, 0xbb, 0xcc, 0xdd]);
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_RECORDS_TAG.to_le_bytes());
    bytes.extend_from_slice(&record_body);
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_CLOSE_TAG.to_le_bytes());

    let report = inspect_smp_bytes(&bytes);
    let summary = report
        .event_runtime_collection_summary
        .as_ref()
        .expect("event runtime collection summary should parse");

    assert_eq!(
        summary.records[0].grouped_effect_rows[0]
            .descriptor_label
            .as_deref(),
        Some("Credit Rating")
    );
    assert_eq!(
        summary.records[0].grouped_effect_rows[0]
            .grouped_target_subject
            .as_deref(),
        Some("company")
    );
    assert_eq!(
        summary.records[0].decoded_actions,
        vec![RuntimeEffect::SetCompanyGovernanceScalar {
            target: RuntimeCompanyTarget::SelectedCompany,
            metric: RuntimeCompanyMetric::CreditRating,
            value: 640,
        }]
    );
}

#[test]
fn looks_up_recovered_world_toggle_descriptor_metadata() {
    let metadata =
        real_grouped_effect_descriptor_metadata(111).expect("descriptor metadata should exist");

    assert_eq!(metadata.label, "Disable Margin Buying/Short Selling Stock");
    assert_eq!(metadata.parameter_family, "world_flag_toggle");
    assert_eq!(
        runtime_world_flag_key(metadata),
        Some("world.disable_margin_buying_short_selling_stock".to_string())
    );
    assert!(metadata.executable_in_runtime);
}

#[test]
fn looks_up_limited_track_building_amount_descriptor_metadata() {
    let metadata =
        real_grouped_effect_descriptor_metadata(122).expect("descriptor metadata should exist");

    assert_eq!(metadata.label, "Limited Track Building Amount");
    assert_eq!(metadata.parameter_family, "world_track_build_limit_scalar");
    assert_eq!(metadata.runtime_key, None);
    assert!(metadata.executable_in_runtime);
}

#[test]
fn looks_up_recovered_late_world_toggle_descriptor_metadata() {
    let metadata =
        real_grouped_effect_descriptor_metadata(143).expect("descriptor metadata should exist");

    assert_eq!(metadata.label, "Disable Train Crashes AND Breakdowns");
    assert_eq!(metadata.parameter_family, "world_flag_toggle");
    assert_eq!(
        runtime_world_flag_key(metadata),
        Some("world.disable_train_crashes_and_breakdowns".to_string())
    );
    assert!(metadata.executable_in_runtime);
}

#[test]
fn looks_up_recovered_locomotive_availability_descriptor_metadata() {
    let metadata =
        real_grouped_effect_descriptor_metadata(250).expect("descriptor metadata should exist");

    assert_eq!(metadata.label, "Big Boy 4-8-8-4 Availability");
    assert_eq!(metadata.target_mask_bits, 0x08);
    assert_eq!(metadata.parameter_family, "locomotive_availability_scalar");
    assert_eq!(metadata.runtime_key, None);
    assert!(metadata.executable_in_runtime);
}

#[test]
fn looks_up_upper_band_recovered_locomotive_availability_descriptor_metadata() {
    let metadata =
        real_grouped_effect_descriptor_metadata(457).expect("descriptor metadata should exist");

    assert_eq!(metadata.label, "Upper-Band Locomotive Availability Slot 1");
    assert_eq!(metadata.target_mask_bits, 0x08);
    assert_eq!(metadata.parameter_family, "locomotive_availability_scalar");
    assert_eq!(recovered_locomotive_availability_loco_id(457), None);
}

#[test]
fn looks_up_extended_lower_band_locomotive_availability_descriptor_metadata() {
    let metadata =
        real_grouped_effect_descriptor_metadata(301).expect("descriptor metadata should exist");

    assert_eq!(metadata.label, "Zephyr Availability");
    assert_eq!(metadata.parameter_family, "locomotive_availability_scalar");
    assert_eq!(recovered_locomotive_availability_loco_id(301), Some(61));
    assert!(metadata.executable_in_runtime);
}

#[test]
fn parses_recovered_locomotive_availability_row_with_structured_locomotive_id() {
    let row_bytes = build_real_grouped_effect_row(RealGroupedEffectRowSpec {
        descriptor_id: 250,
        raw_scalar_value: 1,
        opcode: 3,
        value_byte_0x09: 0,
        value_dword_0x0d: 0,
        value_byte_0x11: 0,
        value_byte_0x12: 0,
        value_word_0x14: 0,
        value_word_0x16: 0,
        locomotive_name: None,
    });

    let row =
        parse_real_grouped_effect_row_summary(&row_bytes, 0, 0, None).expect("row should parse");

    assert_eq!(row.descriptor_id, 250);
    assert_eq!(
        row.descriptor_label.as_deref(),
        Some("Big Boy 4-8-8-4 Availability")
    );
    assert_eq!(row.recovered_locomotive_id, Some(10));
    assert_eq!(
        row.parameter_family.as_deref(),
        Some("locomotive_availability_scalar")
    );
}

#[test]
fn looks_up_recovered_cargo_production_descriptor_metadata() {
    let metadata =
        real_grouped_effect_descriptor_metadata(230).expect("descriptor metadata should exist");

    assert_eq!(metadata.label, "Cargo Production Slot 1");
    assert_eq!(metadata.target_mask_bits, 0x08);
    assert_eq!(metadata.parameter_family, "cargo_production_scalar");
    assert_eq!(metadata.runtime_key, None);
    assert!(metadata.executable_in_runtime);
}

#[test]
fn looks_up_recovered_all_cargo_price_descriptor_metadata() {
    let metadata =
        real_grouped_effect_descriptor_metadata(105).expect("descriptor metadata should exist");

    assert_eq!(metadata.label, "All Cargo Prices");
    assert_eq!(metadata.target_mask_bits, 0x08);
    assert_eq!(metadata.parameter_family, "cargo_price_scalar");
    assert_eq!(metadata.runtime_key, None);
    assert!(metadata.executable_in_runtime);
}

#[test]
fn looks_up_named_cargo_price_slot_descriptor_metadata() {
    let metadata =
        real_grouped_effect_descriptor_metadata(106).expect("descriptor metadata should exist");

    assert_eq!(metadata.label, "Alcohol Price");
    assert_eq!(metadata.target_mask_bits, 0x08);
    assert_eq!(metadata.parameter_family, "cargo_price_scalar");
    assert_eq!(metadata.runtime_key, None);
    assert!(metadata.executable_in_runtime);
}

#[test]
fn looks_up_runtime_variable_descriptor_metadata() {
    let metadata =
        real_grouped_effect_descriptor_metadata(43).expect("descriptor metadata should exist");

    assert_eq!(metadata.label, "Company Variable 1");
    assert_eq!(metadata.target_mask_bits, 0x01);
    assert_eq!(metadata.parameter_family, "runtime_variable_scalar");
    assert!(metadata.executable_in_runtime);
}

#[test]
fn looks_up_recovered_aggregate_cargo_production_descriptor_metadata() {
    let metadata =
        real_grouped_effect_descriptor_metadata(177).expect("descriptor metadata should exist");

    assert_eq!(metadata.label, "All Cargo Production");
    assert_eq!(metadata.target_mask_bits, 0x08);
    assert_eq!(metadata.parameter_family, "cargo_production_scalar");
    assert_eq!(metadata.runtime_key, None);
    assert!(metadata.executable_in_runtime);
}

#[test]
fn looks_up_grounded_named_cargo_production_descriptor_metadata() {
    let metadata =
        real_grouped_effect_descriptor_metadata(180).expect("descriptor metadata should exist");

    assert_eq!(metadata.label, "Alcohol Production");
    assert_eq!(metadata.target_mask_bits, 0x08);
    assert_eq!(metadata.parameter_family, "cargo_production_scalar");
    assert_eq!(metadata.runtime_key, None);
    assert!(metadata.executable_in_runtime);
}

#[test]
fn looks_up_recovered_lower_band_locomotive_cost_descriptor_metadata() {
    let metadata =
        real_grouped_effect_descriptor_metadata(352).expect("descriptor metadata should exist");

    assert_eq!(metadata.label, "2-D-2 Cost");
    assert_eq!(metadata.target_mask_bits, 0x08);
    assert_eq!(metadata.parameter_family, "locomotive_cost_scalar");
    assert_eq!(metadata.runtime_key, None);
    assert_eq!(recovered_locomotive_cost_loco_id(352), Some(1));
    assert!(metadata.executable_in_runtime);
}

#[test]
fn looks_up_recovered_upper_band_locomotive_cost_descriptor_metadata() {
    let metadata =
        real_grouped_effect_descriptor_metadata(475).expect("descriptor metadata should exist");

    assert_eq!(metadata.label, "Upper-Band Locomotive Cost Slot 1");
    assert_eq!(metadata.parameter_family, "locomotive_cost_scalar");
    assert_eq!(recovered_locomotive_cost_loco_id(475), None);
    assert!(!metadata.executable_in_runtime);
}

#[test]
fn looks_up_extended_lower_band_locomotive_cost_descriptor_metadata() {
    let metadata =
        real_grouped_effect_descriptor_metadata(412).expect("descriptor metadata should exist");

    assert_eq!(metadata.label, "Zephyr Cost");
    assert_eq!(metadata.parameter_family, "locomotive_cost_scalar");
    assert_eq!(recovered_locomotive_cost_loco_id(412), Some(61));
    assert!(metadata.executable_in_runtime);
}

#[test]
fn looks_up_recovered_territory_access_cost_descriptor_metadata() {
    let metadata =
        real_grouped_effect_descriptor_metadata(453).expect("descriptor metadata should exist");

    assert_eq!(metadata.label, "Territory Access Cost");
    assert_eq!(metadata.target_mask_bits, 0x08);
    assert_eq!(metadata.parameter_family, "territory_access_cost_scalar");
    assert_eq!(metadata.runtime_key, None);
    assert!(metadata.executable_in_runtime);
}

#[test]
fn parses_recovered_locomotive_cost_row_with_structured_locomotive_id() {
    let row_bytes = build_real_grouped_effect_row(RealGroupedEffectRowSpec {
        descriptor_id: 352,
        raw_scalar_value: 250000,
        opcode: 3,
        value_byte_0x09: 0,
        value_dword_0x0d: 0,
        value_byte_0x11: 0,
        value_byte_0x12: 0,
        value_word_0x14: 0,
        value_word_0x16: 0,
        locomotive_name: None,
    });

    let row =
        parse_real_grouped_effect_row_summary(&row_bytes, 0, 0, None).expect("row should parse");

    assert_eq!(row.descriptor_id, 352);
    assert_eq!(row.descriptor_label.as_deref(), Some("2-D-2 Cost"));
    assert_eq!(row.recovered_locomotive_id, Some(1));
    assert_eq!(
        row.parameter_family.as_deref(),
        Some("locomotive_cost_scalar")
    );
}

#[test]
fn parses_grounded_named_cargo_production_row_with_label() {
    let row_bytes = build_real_grouped_effect_row(RealGroupedEffectRowSpec {
        descriptor_id: 180,
        raw_scalar_value: 160,
        opcode: 3,
        value_byte_0x09: 0,
        value_dword_0x0d: 0,
        value_byte_0x11: 0,
        value_byte_0x12: 0,
        value_word_0x14: 0,
        value_word_0x16: 0,
        locomotive_name: None,
    });

    let row =
        parse_real_grouped_effect_row_summary(&row_bytes, 0, 0, None).expect("row should parse");

    assert_eq!(row.descriptor_id, 180);
    assert_eq!(row.descriptor_label.as_deref(), Some("Alcohol Production"));
    assert_eq!(row.recovered_cargo_label.as_deref(), Some("Alcohol"));
    assert_eq!(
        row.parameter_family.as_deref(),
        Some("cargo_production_scalar")
    );
}

#[test]
fn parses_grounded_named_cargo_price_row_with_label() {
    let row_bytes = build_real_grouped_effect_row(RealGroupedEffectRowSpec {
        descriptor_id: 106,
        raw_scalar_value: 140,
        opcode: 3,
        value_byte_0x09: 0,
        value_dword_0x0d: 0,
        value_byte_0x11: 0,
        value_byte_0x12: 0,
        value_word_0x14: 0,
        value_word_0x16: 0,
        locomotive_name: None,
    });

    let row =
        parse_real_grouped_effect_row_summary(&row_bytes, 0, 0, None).expect("row should parse");

    assert_eq!(row.descriptor_id, 106);
    assert_eq!(row.descriptor_label.as_deref(), Some("Alcohol Price"));
    assert_eq!(row.recovered_cargo_label.as_deref(), Some("Alcohol"));
    assert_eq!(row.parameter_family.as_deref(), Some("cargo_price_scalar"));
}

#[test]
fn looks_up_recovered_locomotive_policy_descriptor_metadata() {
    let metadata =
        real_grouped_effect_descriptor_metadata(454).expect("descriptor metadata should exist");

    assert_eq!(metadata.label, "All Steam Locos Avail.");
    assert_eq!(metadata.parameter_family, "world_flag_toggle");
    assert_eq!(
        metadata.runtime_key,
        Some("world.all_steam_locos_available")
    );
    assert!(metadata.executable_in_runtime);
}

#[test]
fn decodes_recovered_locomotive_policy_descriptor_from_checked_in_metadata() {
    let grouped_row = build_real_grouped_effect_row(RealGroupedEffectRowSpec {
        descriptor_id: 454,
        opcode: 0,
        raw_scalar_value: 1,
        value_byte_0x09: 0,
        value_dword_0x0d: 0,
        value_byte_0x11: 0,
        value_byte_0x12: 0,
        value_word_0x14: 0,
        value_word_0x16: 0,
        locomotive_name: None,
    });
    let group0_rows = vec![grouped_row];
    let record_body = build_real_event_record(
        [b"Locos", b"", b"", b"", b"", b""],
        Some(RealCompactControlSpec {
            mode_byte_0x7ef: 6,
            primary_selector_0x7f0: 0,
            grouped_mode_0x7f4: 2,
            one_shot_header_0x7f5: 0,
            modifier_flag_0x7f9: 0,
            modifier_flag_0x7fa: 0,
            grouped_target_scope_ordinals_0x7fb: [0, 0, 0, 0],
            grouped_scope_checkboxes_0x7ff: [1, 0, 0, 0],
            summary_toggle_0x800: 1,
            grouped_territory_selectors_0x80f: [-1, -1, -1, -1],
        }),
        &[],
        [&group0_rows, &[], &[], &[]],
    );

    let mut bytes = Vec::new();
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_METADATA_TAG.to_le_bytes());
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_PACKED_STATE_VERSION.to_le_bytes());
    let header_words = [1u32, 4, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    for word in header_words {
        bytes.extend_from_slice(&word.to_le_bytes());
    }
    bytes.extend_from_slice(&[0x00, 0x00]);
    bytes.extend_from_slice(&[0xaa, 0xbb, 0xcc, 0xdd]);
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_RECORDS_TAG.to_le_bytes());
    bytes.extend_from_slice(&record_body);
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_CLOSE_TAG.to_le_bytes());

    let report = inspect_smp_bytes(&bytes);
    let summary = report
        .event_runtime_collection_summary
        .as_ref()
        .expect("event runtime collection summary should parse");

    assert_eq!(
        summary.records[0].grouped_effect_rows[0]
            .descriptor_label
            .as_deref(),
        Some("All Steam Locos Avail.")
    );
    assert_eq!(
        summary.records[0].grouped_effect_rows[0]
            .parameter_family
            .as_deref(),
        Some("world_flag_toggle")
    );
    assert_eq!(
        summary.records[0].decoded_actions,
        vec![RuntimeEffect::SetWorldFlag {
            key: "world.all_steam_locos_available".to_string(),
            value: true,
        }]
    );
    assert!(summary.records[0].executable_import_ready);
}

#[test]
fn looks_up_checked_in_deactivate_player_descriptor_metadata() {
    let metadata =
        real_grouped_effect_descriptor_metadata(14).expect("descriptor metadata should exist");

    assert_eq!(metadata.label, "Deactivate Player");
    assert_eq!(metadata.parameter_family, "player_lifecycle_toggle");
    assert_eq!(metadata.runtime_key, None);
    assert!(metadata.executable_in_runtime);
}

#[test]
fn decodes_real_deactivate_player_descriptor_from_checked_in_metadata() {
    let grouped_row = build_real_grouped_effect_row(RealGroupedEffectRowSpec {
        descriptor_id: 14,
        opcode: 1,
        raw_scalar_value: 1,
        value_byte_0x09: 0,
        value_dword_0x0d: 0,
        value_byte_0x11: 0,
        value_byte_0x12: 0,
        value_word_0x14: 0,
        value_word_0x16: 0,
        locomotive_name: None,
    });
    let group0_rows = vec![grouped_row];
    let record_body = build_real_event_record(
        [b"Players", b"", b"", b"", b"", b""],
        Some(RealCompactControlSpec {
            mode_byte_0x7ef: 7,
            primary_selector_0x7f0: 0,
            grouped_mode_0x7f4: 2,
            one_shot_header_0x7f5: 0,
            modifier_flag_0x7f9: 0,
            modifier_flag_0x7fa: 0,
            grouped_target_scope_ordinals_0x7fb: [1, 0, 0, 0],
            grouped_scope_checkboxes_0x7ff: [1, 0, 0, 0],
            summary_toggle_0x800: 1,
            grouped_territory_selectors_0x80f: [-1, -1, -1, -1],
        }),
        &[],
        [&group0_rows, &[], &[], &[]],
    );

    let mut bytes = Vec::new();
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_METADATA_TAG.to_le_bytes());
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_PACKED_STATE_VERSION.to_le_bytes());
    let header_words = [1u32, 4, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    for word in header_words {
        bytes.extend_from_slice(&word.to_le_bytes());
    }
    bytes.extend_from_slice(&[0x00, 0x00]);
    bytes.extend_from_slice(&[0xaa, 0xbb, 0xcc, 0xdd]);
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_RECORDS_TAG.to_le_bytes());
    bytes.extend_from_slice(&record_body);
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_CLOSE_TAG.to_le_bytes());

    let report = inspect_smp_bytes(&bytes);
    let summary = report
        .event_runtime_collection_summary
        .as_ref()
        .expect("event runtime collection summary should parse");

    assert_eq!(
        summary.records[0].grouped_effect_rows[0]
            .descriptor_label
            .as_deref(),
        Some("Deactivate Player")
    );
    assert_eq!(
        summary.records[0].grouped_effect_rows[0]
            .parameter_family
            .as_deref(),
        Some("player_lifecycle_toggle")
    );
    assert_eq!(
        summary.records[0].decoded_actions,
        vec![RuntimeEffect::DeactivatePlayer {
            target: RuntimePlayerTarget::SelectedPlayer,
        }]
    );
    assert!(summary.records[0].executable_import_ready);
}

#[test]
fn decodes_real_world_flag_descriptor_with_checked_in_runtime_key() {
    let grouped_row = build_real_grouped_effect_row(RealGroupedEffectRowSpec {
        descriptor_id: 110,
        opcode: 0,
        raw_scalar_value: 1,
        value_byte_0x09: 0,
        value_dword_0x0d: 0,
        value_byte_0x11: 0,
        value_byte_0x12: 0,
        value_word_0x14: 0,
        value_word_0x16: 0,
        locomotive_name: None,
    });
    let group0_rows = vec![grouped_row];
    let record_body = build_real_event_record(
        [b"World", b"", b"", b"", b"", b""],
        Some(RealCompactControlSpec {
            mode_byte_0x7ef: 7,
            primary_selector_0x7f0: 0,
            grouped_mode_0x7f4: 2,
            one_shot_header_0x7f5: 0,
            modifier_flag_0x7f9: 0,
            modifier_flag_0x7fa: 0,
            grouped_target_scope_ordinals_0x7fb: [0, 0, 0, 0],
            grouped_scope_checkboxes_0x7ff: [1, 0, 0, 0],
            summary_toggle_0x800: 1,
            grouped_territory_selectors_0x80f: [-1, -1, -1, -1],
        }),
        &[],
        [&group0_rows, &[], &[], &[]],
    );

    let mut bytes = Vec::new();
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_METADATA_TAG.to_le_bytes());
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_PACKED_STATE_VERSION.to_le_bytes());
    let header_words = [1u32, 4, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    for word in header_words {
        bytes.extend_from_slice(&word.to_le_bytes());
    }
    bytes.extend_from_slice(&[0x00, 0x00]);
    bytes.extend_from_slice(&[0xaa, 0xbb, 0xcc, 0xdd]);
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_RECORDS_TAG.to_le_bytes());
    bytes.extend_from_slice(&record_body);
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_CLOSE_TAG.to_le_bytes());

    let report = inspect_smp_bytes(&bytes);
    let summary = report
        .event_runtime_collection_summary
        .as_ref()
        .expect("event runtime collection summary should parse");

    assert_eq!(
        summary.records[0].grouped_effect_rows[0]
            .descriptor_label
            .as_deref(),
        Some("Disable Stock Buying and Selling")
    );
    assert_eq!(
        summary.records[0].grouped_effect_rows[0]
            .parameter_family
            .as_deref(),
        Some("world_flag_toggle")
    );
    assert_eq!(
        summary.records[0].decoded_actions,
        vec![RuntimeEffect::SetWorldFlag {
            key: "world.disable_stock_buying_and_selling".to_string(),
            value: true,
        }]
    );
    assert!(summary.records[0].executable_import_ready);
}

#[test]
fn decodes_recovered_world_toggle_descriptor_family() {
    let grouped_row = build_real_grouped_effect_row(RealGroupedEffectRowSpec {
        descriptor_id: 131,
        opcode: 0,
        raw_scalar_value: 1,
        value_byte_0x09: 0,
        value_dword_0x0d: 0,
        value_byte_0x11: 0,
        value_byte_0x12: 0,
        value_word_0x14: 0,
        value_word_0x16: 0,
        locomotive_name: None,
    });
    let group0_rows = vec![grouped_row];
    let record_body = build_real_event_record(
        [b"World", b"", b"", b"", b"", b""],
        Some(RealCompactControlSpec {
            mode_byte_0x7ef: 7,
            primary_selector_0x7f0: 0,
            grouped_mode_0x7f4: 2,
            one_shot_header_0x7f5: 0,
            modifier_flag_0x7f9: 0,
            modifier_flag_0x7fa: 0,
            grouped_target_scope_ordinals_0x7fb: [0, 0, 0, 0],
            grouped_scope_checkboxes_0x7ff: [1, 0, 0, 0],
            summary_toggle_0x800: 1,
            grouped_territory_selectors_0x80f: [-1, -1, -1, -1],
        }),
        &[],
        [&group0_rows, &[], &[], &[]],
    );

    let mut bytes = Vec::new();
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_METADATA_TAG.to_le_bytes());
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_PACKED_STATE_VERSION.to_le_bytes());
    let header_words = [1u32, 4, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    for word in header_words {
        bytes.extend_from_slice(&word.to_le_bytes());
    }
    bytes.extend_from_slice(&[0x00, 0x00]);
    bytes.extend_from_slice(&[0xaa, 0xbb, 0xcc, 0xdd]);
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_RECORDS_TAG.to_le_bytes());
    bytes.extend_from_slice(&record_body);
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_CLOSE_TAG.to_le_bytes());

    let report = inspect_smp_bytes(&bytes);
    let summary = report
        .event_runtime_collection_summary
        .as_ref()
        .expect("event runtime collection summary should parse");

    assert_eq!(
        summary.records[0].grouped_effect_rows[0]
            .descriptor_label
            .as_deref(),
        Some("Disable Starting Any Companies")
    );
    assert_eq!(
        summary.records[0].grouped_effect_rows[0]
            .parameter_family
            .as_deref(),
        Some("world_flag_toggle")
    );
    assert_eq!(
        summary.records[0].decoded_actions,
        vec![RuntimeEffect::SetWorldFlag {
            key: "world.disable_starting_any_companies".to_string(),
            value: true,
        }]
    );
    assert!(summary.records[0].executable_import_ready);
}

#[test]
fn decodes_limited_track_building_amount_descriptor_family() {
    let grouped_row = build_real_grouped_effect_row(RealGroupedEffectRowSpec {
        descriptor_id: 122,
        opcode: 3,
        raw_scalar_value: 18,
        value_byte_0x09: 0,
        value_dword_0x0d: 0,
        value_byte_0x11: 0,
        value_byte_0x12: 0,
        value_word_0x14: 0,
        value_word_0x16: 0,
        locomotive_name: None,
    });
    let group0_rows = vec![grouped_row];
    let record_body = build_real_event_record(
        [b"World", b"", b"", b"", b"", b""],
        Some(RealCompactControlSpec {
            mode_byte_0x7ef: 6,
            primary_selector_0x7f0: 0,
            grouped_mode_0x7f4: 2,
            one_shot_header_0x7f5: 0,
            modifier_flag_0x7f9: 0,
            modifier_flag_0x7fa: 0,
            grouped_target_scope_ordinals_0x7fb: [0, 0, 0, 0],
            grouped_scope_checkboxes_0x7ff: [1, 0, 0, 0],
            summary_toggle_0x800: 1,
            grouped_territory_selectors_0x80f: [-1, -1, -1, -1],
        }),
        &[],
        [&group0_rows, &[], &[], &[]],
    );

    let mut bytes = Vec::new();
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_METADATA_TAG.to_le_bytes());
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_PACKED_STATE_VERSION.to_le_bytes());
    let header_words = [1u32, 4, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    for word in header_words {
        bytes.extend_from_slice(&word.to_le_bytes());
    }
    bytes.extend_from_slice(&[0x00, 0x00]);
    bytes.extend_from_slice(&[0xaa, 0xbb, 0xcc, 0xdd]);
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_RECORDS_TAG.to_le_bytes());
    bytes.extend_from_slice(&record_body);
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_CLOSE_TAG.to_le_bytes());

    let report = inspect_smp_bytes(&bytes);
    let summary = report
        .event_runtime_collection_summary
        .as_ref()
        .expect("event runtime collection summary should parse");

    assert_eq!(
        summary.records[0].grouped_effect_rows[0]
            .descriptor_label
            .as_deref(),
        Some("Limited Track Building Amount")
    );
    assert_eq!(
        summary.records[0].grouped_effect_rows[0]
            .parameter_family
            .as_deref(),
        Some("world_track_build_limit_scalar")
    );
    assert_eq!(
        summary.records[0].decoded_actions,
        vec![RuntimeEffect::SetLimitedTrackBuildingAmount { value: 18 }]
    );
    assert!(summary.records[0].executable_import_ready);
}

#[test]
fn decodes_recovered_late_world_toggle_descriptor_family() {
    let grouped_row = build_real_grouped_effect_row(RealGroupedEffectRowSpec {
        descriptor_id: 144,
        opcode: 0,
        raw_scalar_value: 1,
        value_byte_0x09: 0,
        value_dword_0x0d: 0,
        value_byte_0x11: 0,
        value_byte_0x12: 0,
        value_word_0x14: 0,
        value_word_0x16: 0,
        locomotive_name: None,
    });
    let group0_rows = vec![grouped_row];
    let record_body = build_real_event_record(
        [b"World", b"", b"", b"", b"", b""],
        Some(RealCompactControlSpec {
            mode_byte_0x7ef: 7,
            primary_selector_0x7f0: 0,
            grouped_mode_0x7f4: 2,
            one_shot_header_0x7f5: 0,
            modifier_flag_0x7f9: 0,
            modifier_flag_0x7fa: 0,
            grouped_target_scope_ordinals_0x7fb: [0, 0, 0, 0],
            grouped_scope_checkboxes_0x7ff: [1, 0, 0, 0],
            summary_toggle_0x800: 1,
            grouped_territory_selectors_0x80f: [-1, -1, -1, -1],
        }),
        &[],
        [&group0_rows, &[], &[], &[]],
    );

    let mut bytes = Vec::new();
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_METADATA_TAG.to_le_bytes());
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_PACKED_STATE_VERSION.to_le_bytes());
    let header_words = [1u32, 4, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    for word in header_words {
        bytes.extend_from_slice(&word.to_le_bytes());
    }
    bytes.extend_from_slice(&[0x00, 0x00]);
    bytes.extend_from_slice(&[0xaa, 0xbb, 0xcc, 0xdd]);
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_RECORDS_TAG.to_le_bytes());
    bytes.extend_from_slice(&record_body);
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_CLOSE_TAG.to_le_bytes());

    let report = inspect_smp_bytes(&bytes);
    let summary = report
        .event_runtime_collection_summary
        .as_ref()
        .expect("event runtime collection summary should parse");

    assert_eq!(
        summary.records[0].grouped_effect_rows[0]
            .descriptor_label
            .as_deref(),
        Some("AI Ignore Territories At Startup")
    );
    assert_eq!(
        summary.records[0].grouped_effect_rows[0]
            .parameter_family
            .as_deref(),
        Some("world_flag_toggle")
    );
    assert_eq!(
        summary.records[0].decoded_actions,
        vec![RuntimeEffect::SetWorldFlag {
            key: "world.ai_ignore_territories_at_startup".to_string(),
            value: true,
        }]
    );
    assert!(summary.records[0].executable_import_ready);
}

#[test]
fn decodes_negative_sentinel_scope_modifiers_and_territory_marker() {
    for (value, expected) in [
        (0, RuntimeCompanyConditionTestScope::Disabled),
        (1, RuntimeCompanyConditionTestScope::AllCompanies),
        (2, RuntimeCompanyConditionTestScope::SelectedCompanyOnly),
        (3, RuntimeCompanyConditionTestScope::AiCompaniesOnly),
        (4, RuntimeCompanyConditionTestScope::HumanCompaniesOnly),
    ] {
        assert_eq!(decode_company_condition_test_scope(value), Some(expected));
    }
    for (value, expected) in [
        (0, RuntimePlayerConditionTestScope::Disabled),
        (1, RuntimePlayerConditionTestScope::AllPlayers),
        (2, RuntimePlayerConditionTestScope::SelectedPlayerOnly),
        (3, RuntimePlayerConditionTestScope::AiPlayersOnly),
        (4, RuntimePlayerConditionTestScope::HumanPlayersOnly),
    ] {
        assert_eq!(decode_player_condition_test_scope(value), Some(expected));
    }

    let rows = vec![SmpLoadedPackedEventConditionRowSummary {
        row_index: 0,
        raw_condition_id: -1,
        subtype: 4,
        flag_bytes: vec![0x30; 25],
        candidate_name: Some("AutoPlant".to_string()),
        comparator: None,
        metric: None,
        semantic_family: None,
        semantic_preview: None,
        recovered_cargo_slot: None,
        recovered_cargo_class: None,
        requires_candidate_name_binding: false,
        notes: vec![],
    }];
    let summary = derive_negative_sentinel_scope_summary(
        &rows,
        &SmpLoadedPackedEventCompactControlSummary {
            mode_byte_0x7ef: 6,
            primary_selector_0x7f0: 0x63,
            grouped_mode_0x7f4: 2,
            one_shot_header_0x7f5: 1,
            modifier_flag_0x7f9: 4,
            modifier_flag_0x7fa: 2,
            grouped_target_scope_ordinals_0x7fb: vec![0, 1, 2, 3],
            grouped_scope_checkboxes_0x7ff: vec![1, 0, 0, 0],
            summary_toggle_0x800: 1,
            grouped_territory_selectors_0x80f: vec![-1, -1, -1, -1],
        },
    )
    .expect("negative sentinel summary should derive");
    assert_eq!(
        summary.company_test_scope,
        RuntimeCompanyConditionTestScope::HumanCompaniesOnly
    );
    assert_eq!(
        summary.player_test_scope,
        RuntimePlayerConditionTestScope::SelectedPlayerOnly
    );
    assert!(summary.territory_scope_selector_is_0x63);
    assert_eq!(summary.source_row_indexes, vec![0]);
}

#[test]
fn classifies_real_grouped_row_semantic_families() {
    let grouped_rows = vec![
        build_real_grouped_effect_row(RealGroupedEffectRowSpec {
            descriptor_id: 2,
            opcode: 1,
            raw_scalar_value: 1,
            value_byte_0x09: 0,
            value_dword_0x0d: 0,
            value_byte_0x11: 0,
            value_byte_0x12: 0,
            value_word_0x14: 0,
            value_word_0x16: 0,
            locomotive_name: None,
        }),
        build_real_grouped_effect_row(RealGroupedEffectRowSpec {
            descriptor_id: 2,
            opcode: 4,
            raw_scalar_value: 25,
            value_byte_0x09: 0,
            value_dword_0x0d: 0,
            value_byte_0x11: 0,
            value_byte_0x12: 0,
            value_word_0x14: 2,
            value_word_0x16: 6,
            locomotive_name: None,
        }),
        build_real_grouped_effect_row(RealGroupedEffectRowSpec {
            descriptor_id: 2,
            opcode: 3,
            raw_scalar_value: 250,
            value_byte_0x09: 0,
            value_dword_0x0d: 0,
            value_byte_0x11: 0,
            value_byte_0x12: 0,
            value_word_0x14: 0,
            value_word_0x16: 0,
            locomotive_name: None,
        }),
        build_real_grouped_effect_row(RealGroupedEffectRowSpec {
            descriptor_id: 2,
            opcode: 8,
            raw_scalar_value: 7,
            value_byte_0x09: 1,
            value_dword_0x0d: 12,
            value_byte_0x11: 2,
            value_byte_0x12: 3,
            value_word_0x14: 24,
            value_word_0x16: 36,
            locomotive_name: Some("Mikado"),
        }),
    ];
    let record_body = build_real_event_record(
        [b"Semantic", b"", b"", b"", b"", b""],
        Some(RealCompactControlSpec {
            mode_byte_0x7ef: 7,
            primary_selector_0x7f0: 0x63,
            grouped_mode_0x7f4: 1,
            one_shot_header_0x7f5: 0,
            modifier_flag_0x7f9: 0,
            modifier_flag_0x7fa: 0,
            grouped_target_scope_ordinals_0x7fb: [1, 1, 1, 1],
            grouped_scope_checkboxes_0x7ff: [0, 0, 0, 0],
            summary_toggle_0x800: 0,
            grouped_territory_selectors_0x80f: [-1, -1, -1, -1],
        }),
        &[],
        [&grouped_rows, &[], &[], &[]],
    );

    let mut bytes = Vec::new();
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_METADATA_TAG.to_le_bytes());
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_PACKED_STATE_VERSION.to_le_bytes());
    let header_words = [1u32, 4, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    for word in header_words {
        bytes.extend_from_slice(&word.to_le_bytes());
    }
    bytes.extend_from_slice(&[0x00, 0x00]);
    bytes.extend_from_slice(&[0xaa, 0xbb, 0xcc, 0xdd]);
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_RECORDS_TAG.to_le_bytes());
    bytes.extend_from_slice(&record_body);
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_CLOSE_TAG.to_le_bytes());

    let report = inspect_smp_bytes(&bytes);
    let summary = report
        .event_runtime_collection_summary
        .as_ref()
        .expect("event runtime collection summary should parse");
    let families = summary.records[0]
        .grouped_effect_rows
        .iter()
        .map(|row| row.semantic_family.as_deref().unwrap_or(""))
        .collect::<Vec<_>>();
    assert_eq!(
        families,
        vec![
            "bool_toggle",
            "timed_duration",
            "scalar_assignment",
            "multivalue_scalar",
        ]
    );
    assert_eq!(
        summary.records[0].grouped_effect_rows[0]
            .semantic_preview
            .as_deref(),
        Some("Set Company Cash to TRUE")
    );
    assert_eq!(
        summary.records[0].grouped_effect_rows[1]
            .semantic_preview
            .as_deref(),
        Some("Set Company Cash to 25 for 2 years 6 months")
    );
    assert_eq!(
        summary.records[0].grouped_effect_rows[2]
            .semantic_preview
            .as_deref(),
        Some("Set Company Cash to 250")
    );
    assert_eq!(
        summary.records[0].grouped_effect_rows[3]
            .semantic_preview
            .as_deref(),
        Some("Set Company Cash to 7 with aux [2, 3, 24, 36]")
    );
}

#[test]
fn rejects_truncated_real_style_event_runtime_record() {
    let mut record_body = build_real_event_record(
        [b"Oops", b"", b"", b"", b"", b""],
        Some(RealCompactControlSpec {
            mode_byte_0x7ef: 5,
            primary_selector_0x7f0: 0,
            grouped_mode_0x7f4: 0,
            one_shot_header_0x7f5: 0,
            modifier_flag_0x7f9: 0,
            modifier_flag_0x7fa: 0,
            grouped_target_scope_ordinals_0x7fb: [0, 0, 0, 0],
            grouped_scope_checkboxes_0x7ff: [0, 0, 0, 0],
            summary_toggle_0x800: 0,
            grouped_territory_selectors_0x80f: [0, 0, 0, 0],
        }),
        &[],
        [&[], &[], &[], &[]],
    );
    record_body.pop();

    let mut bytes = Vec::new();
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_METADATA_TAG.to_le_bytes());
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_PACKED_STATE_VERSION.to_le_bytes());
    let header_words = [1u32, 4, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    for word in header_words {
        bytes.extend_from_slice(&word.to_le_bytes());
    }
    bytes.extend_from_slice(&[0x00, 0x00]);
    bytes.extend_from_slice(&[0xaa, 0xbb, 0xcc, 0xdd]);
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_RECORDS_TAG.to_le_bytes());
    bytes.extend_from_slice(&record_body);
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_CLOSE_TAG.to_le_bytes());

    let report = inspect_smp_bytes(&bytes);
    let summary = report
        .event_runtime_collection_summary
        .as_ref()
        .expect("event runtime collection summary should parse");

    assert_eq!(summary.records[0].decode_status, "unsupported_framing");
    assert_eq!(summary.records[0].payload_family, "unsupported_framing");
}
