use super::*;

#[test]
fn parses_event_runtime_collection_summary_from_synthetic_chunks() {
    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, 5, 3, 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(&[0x14, 0x00]);
    bytes.extend_from_slice(&[0xaa, 0xbb, 0xcc, 0xdd]);
    bytes.extend_from_slice(&[0x11, 0x22, 0x33, 0x44]);
    bytes.extend_from_slice(&[0x55, 0x66, 0x77, 0x88]);
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_RECORDS_TAG.to_le_bytes());
    bytes.extend_from_slice(&[0xde, 0xad, 0xbe, 0xef]);
    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.packed_state_version, 0x3e9);
    assert_eq!(summary.live_id_bound, 5);
    assert_eq!(summary.live_record_count, 3);
    assert_eq!(summary.live_entry_ids, vec![1, 3, 5]);
    assert_eq!(summary.records_tag_offset, 96);
    assert_eq!(summary.decoded_record_count, 0);
    assert_eq!(summary.records_with_trigger_kind, 0);
    assert_eq!(summary.records_missing_trigger_kind, 3);
    assert_eq!(summary.nondirect_compact_record_count, 0);
    assert!(
        summary
            .add_building_dispatch_strip_record_indexes
            .is_empty()
    );
    assert!(
        summary
            .add_building_dispatch_strip_descriptor_labels
            .is_empty()
    );
    assert_eq!(summary.records.len(), 3);
    assert_eq!(summary.records[0].decode_status, "unsupported_framing");
}

#[test]
fn parses_event_runtime_collection_summary_from_u32_tag_chunks() {
    let mut bytes = Vec::new();
    bytes.extend_from_slice(&(EVENT_RUNTIME_COLLECTION_METADATA_TAG as u32).to_le_bytes());
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_PACKED_STATE_VERSION.to_le_bytes());

    let header_words = [1u32, 4, 0, 0, 5, 3, 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(&[0x14, 0x00]);
    bytes.extend_from_slice(&[0xaa, 0xbb, 0xcc, 0xdd]);
    bytes.extend_from_slice(&[0x11, 0x22, 0x33, 0x44]);
    bytes.extend_from_slice(&[0x55, 0x66, 0x77, 0x88]);
    bytes.extend_from_slice(&(EVENT_RUNTIME_COLLECTION_RECORDS_TAG as u32).to_le_bytes());
    bytes.extend_from_slice(&[0xde, 0xad, 0xbe, 0xef]);
    bytes.extend_from_slice(&(EVENT_RUNTIME_COLLECTION_CLOSE_TAG as u32).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 from u32 tags");

    assert_eq!(summary.packed_state_version, 0x3e9);
    assert_eq!(summary.live_id_bound, 5);
    assert_eq!(summary.live_record_count, 3);
    assert_eq!(summary.live_entry_ids, vec![1, 3, 5]);
    assert_eq!(summary.records_tag_offset, 98);
    assert_eq!(summary.decoded_record_count, 0);
    assert_eq!(summary.records_with_trigger_kind, 0);
    assert_eq!(summary.records_missing_trigger_kind, 3);
    assert_eq!(summary.nondirect_compact_record_count, 0);
    assert!(
        summary
            .add_building_dispatch_strip_record_indexes
            .is_empty()
    );
    assert!(
        summary
            .add_building_dispatch_strip_descriptor_labels
            .is_empty()
    );
    assert_eq!(summary.records.len(), 3);
    assert_eq!(summary.records[0].decode_status, "unsupported_framing");
}

#[test]
fn parses_nondirect_event_runtime_collection_summary_from_u32_tag_chunks() {
    let mut bytes = Vec::new();
    bytes.extend_from_slice(&(EVENT_RUNTIME_COLLECTION_METADATA_TAG as u32).to_le_bytes());
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_PACKED_STATE_VERSION.to_le_bytes());

    let header_words = [
        0u32, 6, 10, 20, 30, 3, 0, 1, 0, 0, 0, 0, 0, 1, 1, 23, 0, 0, 0,
    ];
    for word in header_words {
        bytes.extend_from_slice(&word.to_le_bytes());
    }

    bytes.extend_from_slice(&[0u8; 18]);
    bytes.extend_from_slice(&(EVENT_RUNTIME_COLLECTION_RECORDS_TAG as u32).to_le_bytes());
    bytes.extend_from_slice(&[0xde, 0xad, 0xbe, 0xef]);
    bytes.extend_from_slice(&(EVENT_RUNTIME_COLLECTION_CLOSE_TAG as u32).to_le_bytes());

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

    assert_eq!(
        summary.source_kind,
        "packed-event-runtime-collection-nondirect"
    );
    assert_eq!(summary.packed_state_version, 0x3e9);
    assert_eq!(summary.live_id_bound, 30);
    assert_eq!(summary.live_record_count, 3);
    assert_eq!(summary.live_entry_ids, vec![1, 2, 3]);
    assert_eq!(summary.records_tag_offset, 102);
    assert_eq!(summary.decoded_record_count, 0);
    assert_eq!(summary.records_with_trigger_kind, 0);
    assert_eq!(summary.records_missing_trigger_kind, 3);
    assert_eq!(summary.nondirect_compact_record_count, 0);
    assert_eq!(summary.nondirect_compact_records_missing_trigger_kind, 0);
    assert_eq!(summary.records.len(), 3);
    assert_eq!(summary.records[0].decode_status, "unsupported_framing");
}

#[test]
fn parses_nondirect_compact_event_runtime_record_rows() {
    let mut bytes = Vec::new();
    bytes.extend_from_slice(&(EVENT_RUNTIME_COLLECTION_METADATA_TAG as u32).to_le_bytes());
    bytes.extend_from_slice(&EVENT_RUNTIME_COLLECTION_PACKED_STATE_VERSION.to_le_bytes());

    let header_words = [
        0u32, 6, 10, 20, 30, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 23, 0, 0, 0,
    ];
    for word in header_words {
        bytes.extend_from_slice(&word.to_le_bytes());
    }

    bytes.extend_from_slice(&[0u8; 18]);
    bytes.extend_from_slice(&(EVENT_RUNTIME_COLLECTION_RECORDS_TAG as u32).to_le_bytes());

    bytes.extend_from_slice(&(PACKED_EVENT_REAL_CONDITION_MARKER as u32).to_le_bytes());
    bytes.extend_from_slice(&1u32.to_le_bytes());
    bytes.extend_from_slice(&u32::MAX.to_le_bytes());
    bytes.push(4);
    bytes.extend_from_slice(&0u32.to_le_bytes());
    bytes.extend_from_slice(&0u32.to_le_bytes());
    bytes.push(2);
    bytes.extend_from_slice(&0u32.to_le_bytes());
    bytes.extend_from_slice(&0u32.to_le_bytes());
    bytes.extend_from_slice(&(PACKED_EVENT_REAL_GROUPED_EFFECT_MARKER as u32).to_le_bytes());
    bytes.extend_from_slice(&1u32.to_le_bytes());
    bytes.extend_from_slice(&43u32.to_le_bytes());
    bytes.extend_from_slice(&1u32.to_le_bytes());
    bytes.push(4);
    bytes.extend_from_slice(&u32::MAX.to_le_bytes());
    bytes.extend_from_slice(&u32::MAX.to_le_bytes());
    bytes.extend_from_slice(&0u32.to_le_bytes());
    bytes.extend_from_slice(&0u32.to_le_bytes());
    bytes.extend_from_slice(&0u32.to_le_bytes());
    bytes.extend_from_slice(&0u32.to_le_bytes());
    bytes.extend_from_slice(&[0u8; 12]);
    bytes.extend_from_slice(&0u32.to_le_bytes());
    bytes.extend_from_slice(&0u32.to_le_bytes());
    bytes.extend_from_slice(&0u32.to_le_bytes());
    bytes.extend_from_slice(&(PACKED_EVENT_REAL_RECORD_TERMINATOR_MARKER as u32).to_le_bytes());
    bytes.extend_from_slice(&(EVENT_RUNTIME_COLLECTION_CLOSE_TAG as u32).to_le_bytes());

    let report = inspect_smp_bytes(&bytes);
    let summary = report
        .event_runtime_collection_summary
        .as_ref()
        .expect("non-direct event runtime collection summary should parse");
    assert_eq!(summary.records_with_trigger_kind, 0);
    assert_eq!(summary.records_missing_trigger_kind, 1);
    assert_eq!(summary.nondirect_compact_record_count, 1);
    assert_eq!(summary.nondirect_compact_records_missing_trigger_kind, 1);
    assert!(
        summary
            .add_building_dispatch_strip_record_indexes
            .is_empty()
    );
    assert!(
        summary
            .add_building_dispatch_strip_descriptor_labels
            .is_empty()
    );
    assert!(summary.control_lane_notes.iter().any(|line| {
        line.contains("all compact non-direct rows currently decode row bodies only")
    }));
    assert!(summary.control_lane_notes.iter().any(|line| {
        line.contains("records with grouped opcodes already in the 0x00431b20 dispatch strip = [0]")
    }));
    assert!(summary.control_lane_notes.iter().any(|line| {
        line.contains("decoded grouped rows already reach the 0x00431b20 dispatch strip")
    }));
    assert!(summary.control_lane_notes.iter().any(|line| {
        line.contains("every currently decoded dispatch-strip row")
            && line.contains("0x4e99/0x4e9a/0x4e9b")
            && line.contains("0x4e21/0x4e22")
    }));
    assert!(
        summary
            .control_lane_notes
            .iter()
            .any(|line| { line.contains("0x0042db20 allocates linked 0x1e/0x28 row nodes") })
    );
    assert!(
        summary.control_lane_notes.iter().any(|line| {
            line.contains("0x004dba23 sits under the event-editor duplication path")
        })
    );
    assert!(
        summary
            .control_lane_notes
            .iter()
            .any(|line| { line.contains("0x00430b50 allocates a fresh live runtime-effect row") })
    );
    assert!(summary.control_lane_notes.iter().any(|line| {
        line.contains("0x00443200..0x004436e3")
            && line.contains("0x005a57cf")
            && line.contains("New Beginnings")
            && line.contains("The American")
    }));
    assert!(summary.control_lane_notes.iter().any(|line| {
        line.contains("0x004323a0")
            && line.contains("[event+0x81f]")
            && line.contains("[event+0x7ef]")
            && line.contains("0x00432ca1..0x00432cb0")
            && line.contains("0x00438710")
    }));
    assert!(summary.control_lane_notes.iter().any(|line| {
        line.contains("0x00442c30")
            && line.contains("0x00444b50")
            && line.contains("0x0062be18/0x0062bae0")
            && line.contains("Open Aus")
            && line.contains("Win - Gold")
            && line.contains("Win - Silver")
    }));
    assert!(summary.control_lane_notes.iter().any(|line| {
        line.contains("SP - GOLD")
            && line.contains("0x00443526")
            && line.contains("1 to 5")
            && line.contains("Labor")
            && line.contains("0x00443601")
            && line.contains("0 to 2")
    }));
    assert!(summary.control_lane_notes.iter().any(|line| {
        line.contains("0x00431b20 dispatch-strip opcodes present in decoded grouped rows = [4]")
    }));
    assert!(summary.control_lane_notes.iter().any(|line| {
            line.contains(
                "decoded grouped descriptor labels present in the 0x00431b20 dispatch strip = [\"Company Variable 1\"]",
            )
        }));
    let record = summary
        .records
        .first()
        .expect("first compact non-direct record");

    assert_eq!(record.decode_status, "parity_only");
    assert_eq!(record.payload_family, "real_packed_nondirect_compact_v1");
    assert_eq!(record.standalone_condition_row_count, 1);
    assert_eq!(record.standalone_condition_rows.len(), 1);
    assert_eq!(record.standalone_condition_rows[0].raw_condition_id, -1);
    assert_eq!(record.standalone_condition_rows[0].subtype, 4);
    assert_eq!(record.grouped_effect_row_counts, vec![1, 0, 0, 0]);
    assert_eq!(record.grouped_effect_rows.len(), 1);
    assert_eq!(record.grouped_effect_rows[0].descriptor_id, 43);
    assert_eq!(record.grouped_effect_rows[0].raw_scalar_value, 1);
    assert_eq!(record.grouped_effect_rows[0].opcode, 4);
    assert!(record.notes.iter().any(|line| {
        line.contains("compact non-direct 0x4e99/0x4e9a/0x4e9b map-bundle row framing")
    }));
    assert!(
        record
            .notes
            .iter()
            .any(|line| { line.contains("does not materialize the compact control lane") })
    );
    assert!(
        record.notes.iter().any(|line| {
            line.contains("0x0042e050 reached from editor duplication at 0x004dba23")
        })
    );
    assert!(
        record
            .notes
            .iter()
            .any(|line| { line.contains("0x00430b50 allocates a fresh live runtime-effect row") })
    );
    assert!(
        record
            .notes
            .iter()
            .any(|line| { line.contains("compact signature family = nondirect-") })
    );
}

#[test]

fn parses_synthetic_event_runtime_record_summaries_and_actions() {
    let append_template = encode_template(
        99,
        0x0a,
        0x01,
        &[encode_action_set_special_condition("Imported Follow-On", 1)],
    );
    let record_body = build_synthetic_event_record(
        7,
        0x03,
        1,
        [0, 1, 0, 0],
        [b"Alpha", b"", b"", b"", b"", b""],
        &[
            encode_action_set_world_flag("from_packed_root", true),
            encode_action_append_template(append_template),
        ],
    );

    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(PACKED_EVENT_RECORDS_SYNTHETIC_MAGIC);
    bytes.extend_from_slice(&(record_body.len() as u32).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.decoded_record_count, 1);
    assert_eq!(summary.imported_runtime_record_count, 1);
    assert_eq!(summary.records_with_trigger_kind, 1);
    assert_eq!(summary.records_missing_trigger_kind, 0);
    assert_eq!(summary.trigger_kinds_present, vec![7]);
    assert_eq!(summary.records.len(), 1);
    assert_eq!(summary.records[0].decode_status, "executable");
    assert_eq!(summary.records[0].payload_family, "synthetic_harness");
    assert_eq!(summary.records[0].trigger_kind, Some(7));
    assert_eq!(summary.records[0].text_bands[0].preview, "Alpha");
    assert_eq!(summary.records[0].standalone_condition_row_count, 1);
    assert_eq!(
        summary.records[0].grouped_effect_row_counts,
        vec![0, 1, 0, 0]
    );
    assert_eq!(summary.records[0].decoded_actions.len(), 2);
    match &summary.records[0].decoded_actions[1] {
        RuntimeEffect::AppendEventRecord { record } => {
            assert_eq!(record.record_id, 99);
            assert_eq!(record.trigger_kind, 0x0a);
        }
        other => panic!("unexpected decoded action: {other:?}"),
    }
}

#[test]
fn decodes_company_targeted_synthetic_records_as_parity_only() {
    let record_body = build_synthetic_event_record(
        8,
        0x01,
        0,
        [0, 0, 0, 0],
        [b"", b"", b"", b"", b"", b""],
        &[encode_action_adjust_company_cash_ids(&[7], 25)],
    );

    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(PACKED_EVENT_RECORDS_SYNTHETIC_MAGIC);
    bytes.extend_from_slice(&(record_body.len() as u32).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.decoded_record_count, 1);
    assert_eq!(summary.imported_runtime_record_count, 1);
    assert_eq!(summary.records_with_trigger_kind, 1);
    assert_eq!(summary.records_missing_trigger_kind, 0);
    assert_eq!(summary.trigger_kinds_present, vec![8]);
    assert_eq!(summary.records[0].decode_status, "executable");
    assert_eq!(summary.records[0].payload_family, "synthetic_harness");
    assert!(summary.records[0].executable_import_ready);
}

#[test]
fn parses_real_style_event_runtime_record_with_zero_rows() {
    let record_body = build_real_event_record(
        [b"Alpha", b"", b"", b"", b"", b""],
        Some(RealCompactControlSpec {
            mode_byte_0x7ef: 7,
            primary_selector_0x7f0: 0x63,
            grouped_mode_0x7f4: 2,
            one_shot_header_0x7f5: 1,
            modifier_flag_0x7f9: 1,
            modifier_flag_0x7fa: 0,
            grouped_target_scope_ordinals_0x7fb: [0, 1, 2, 3],
            grouped_scope_checkboxes_0x7ff: [1, 0, 1, 0],
            summary_toggle_0x800: 1,
            grouped_territory_selectors_0x80f: [-1, 10, -1, 22],
        }),
        &[],
        [&[], &[], &[], &[]],
    );

    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.decoded_record_count, 1);
    assert_eq!(summary.imported_runtime_record_count, 0);
    assert_eq!(summary.records[0].decode_status, "parity_only");
    assert_eq!(summary.records[0].payload_family, "real_packed_v1");
    assert_eq!(summary.records[0].trigger_kind, Some(7));
    assert_eq!(summary.records[0].one_shot, Some(true));
    assert_eq!(
        summary.records[0]
            .compact_control
            .as_ref()
            .expect("real compact control should parse")
            .primary_selector_0x7f0,
        0x63
    );
    assert_eq!(summary.records[0].text_bands[0].preview, "Alpha");
    assert_eq!(summary.records[0].standalone_condition_row_count, 0);
    assert_eq!(summary.records[0].standalone_condition_rows.len(), 0);
    assert!(summary.records[0].negative_sentinel_scope.is_none());
    assert_eq!(
        summary.records[0].grouped_effect_row_counts,
        vec![0, 0, 0, 0]
    );
    assert_eq!(summary.records[0].grouped_effect_rows.len(), 0);
}

#[test]
fn parses_real_style_rows_and_side_strings() {
    let condition_row = build_real_condition_row(-1, 4, 0x30, Some("AutoPlant"));
    let grouped_row = 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 group0_rows = vec![grouped_row];
    let record_body = build_real_event_record(
        [b"Gamma", b"", b"", b"", b"", b""],
        Some(RealCompactControlSpec {
            mode_byte_0x7ef: 6,
            primary_selector_0x7f0: 0x2a,
            grouped_mode_0x7f4: 1,
            one_shot_header_0x7f5: 0,
            modifier_flag_0x7f9: 2,
            modifier_flag_0x7fa: 3,
            grouped_target_scope_ordinals_0x7fb: [1, 4, 7, 8],
            grouped_scope_checkboxes_0x7ff: [0, 1, 0, 1],
            summary_toggle_0x800: 0,
            grouped_territory_selectors_0x80f: [11, -1, 33, -1],
        }),
        &[condition_row],
        [&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].standalone_condition_rows.len(), 1);
    assert_eq!(
        summary.records[0]
            .compact_control
            .as_ref()
            .expect("real compact control should parse")
            .grouped_target_scope_ordinals_0x7fb,
        vec![1, 4, 7, 8]
    );
    assert_eq!(
        summary.records[0].standalone_condition_rows[0].raw_condition_id,
        -1
    );
    assert_eq!(
        summary.records[0].standalone_condition_rows[0]
            .candidate_name
            .as_deref(),
        Some("AutoPlant")
    );
    let negative_sentinel_scope = summary.records[0]
        .negative_sentinel_scope
        .as_ref()
        .expect("negative-sentinel scope summary should decode");
    assert_eq!(
        negative_sentinel_scope.company_test_scope,
        RuntimeCompanyConditionTestScope::SelectedCompanyOnly
    );
    assert_eq!(
        negative_sentinel_scope.player_test_scope,
        RuntimePlayerConditionTestScope::AiPlayersOnly
    );
    assert!(!negative_sentinel_scope.territory_scope_selector_is_0x63);
    assert_eq!(negative_sentinel_scope.source_row_indexes, vec![0]);
    assert_eq!(summary.records[0].grouped_effect_rows.len(), 1);
    assert_eq!(summary.records[0].grouped_effect_rows[0].opcode, 8);
    assert_eq!(
        summary.records[0].grouped_effect_rows[0]
            .descriptor_label
            .as_deref(),
        Some("Company Cash")
    );
    assert_eq!(
        summary.records[0].grouped_effect_rows[0].target_mask_bits,
        Some(0x01)
    );
    assert_eq!(
        summary.records[0].grouped_effect_rows[0].row_shape,
        "multivalue_scalar"
    );
    assert_eq!(
        summary.records[0].grouped_effect_rows[0]
            .semantic_family
            .as_deref(),
        Some("multivalue_scalar")
    );
    assert_eq!(
        summary.records[0].grouped_effect_rows[0]
            .semantic_preview
            .as_deref(),
        Some("Set Company Cash to 7 with aux [2, 3, 24, 36]")
    );
    assert_eq!(
        summary.records[0].grouped_effect_rows[0]
            .locomotive_name
            .as_deref(),
        Some("Mikado")
    );
    assert_eq!(
        summary.records[0].decoded_actions,
        vec![RuntimeEffect::SetCompanyCash {
            target: RuntimeCompanyTarget::SelectedCompany,
            value: 7,
        }]
    );
}
