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

pub(in crate::inspect::smp) fn try_parse_real_event_runtime_record_summaries(
    records_payload: &[u8],
    records_payload_offset: usize,
    live_entry_ids: &[u32],
) -> Option<Vec<SmpLoadedPackedEventRecordSummary>> {
    let mut cursor = 0usize;
    let mut records = Vec::with_capacity(live_entry_ids.len());

    for (record_index, live_entry_id) in live_entry_ids.iter().copied().enumerate() {
        let (record, consumed_len) = parse_real_event_runtime_record_summary(
            records_payload.get(cursor..)?,
            records_payload_offset + cursor,
            record_index,
            live_entry_id,
        )?;
        records.push(record);
        cursor += consumed_len;
    }

    if cursor != records_payload.len() {
        return None;
    }

    Some(records)
}

pub(in crate::inspect::smp) fn parse_real_event_runtime_record_summary(
    record_body: &[u8],
    payload_offset: usize,
    record_index: usize,
    live_entry_id: u32,
) -> Option<(SmpLoadedPackedEventRecordSummary, usize)> {
    let mut cursor = 0usize;
    let mut text_bands = Vec::with_capacity(PACKED_EVENT_TEXT_BAND_LABELS.len());
    for label in PACKED_EVENT_TEXT_BAND_LABELS {
        let packed_len = usize::from(read_u16_at(record_body, cursor)?);
        cursor += 2;
        let band_bytes = record_body.get(cursor..cursor + packed_len)?;
        cursor += packed_len;
        text_bands.push(SmpLoadedPackedEventTextBandSummary {
            label: label.to_string(),
            packed_len,
            present: packed_len != 0,
            preview: ascii_preview(band_bytes),
        });
    }

    let compact_control = parse_optional_real_compact_control_summary(record_body, &mut cursor)?;

    if read_u16_at(record_body, cursor)? != PACKED_EVENT_REAL_CONDITION_MARKER {
        return None;
    }
    cursor += 2;
    let standalone_condition_row_count = usize::from(read_u16_at(record_body, cursor)?);
    cursor += 2;

    let mut standalone_condition_rows = Vec::with_capacity(standalone_condition_row_count);
    for row_index in 0..standalone_condition_row_count {
        let row_bytes = record_body.get(cursor..cursor + PACKED_EVENT_REAL_CONDITION_ROW_LEN)?;
        cursor += PACKED_EVENT_REAL_CONDITION_ROW_LEN;
        let candidate_name = parse_optional_u16_len_prefixed_string(record_body, &mut cursor)?;
        standalone_condition_rows.push(parse_real_condition_row_summary(
            row_bytes,
            row_index,
            candidate_name,
        )?);
    }

    if read_u16_at(record_body, cursor)? != PACKED_EVENT_REAL_GROUPED_EFFECT_MARKER {
        return None;
    }
    cursor += 2;

    let mut grouped_effect_row_counts = Vec::with_capacity(4);
    for _ in 0..4 {
        grouped_effect_row_counts.push(usize::from(read_u16_at(record_body, cursor)?));
        cursor += 2;
    }

    let mut grouped_effect_rows =
        Vec::with_capacity(grouped_effect_row_counts.iter().sum::<usize>());
    for (group_index, row_count) in grouped_effect_row_counts.iter().copied().enumerate() {
        for row_index in 0..row_count {
            let row_bytes =
                record_body.get(cursor..cursor + PACKED_EVENT_REAL_GROUPED_EFFECT_ROW_LEN)?;
            cursor += PACKED_EVENT_REAL_GROUPED_EFFECT_ROW_LEN;
            let locomotive_name = parse_optional_u16_len_prefixed_string(record_body, &mut cursor)?;
            grouped_effect_rows.push(parse_real_grouped_effect_row_summary(
                row_bytes,
                group_index,
                row_index,
                locomotive_name,
            )?);
        }
    }
    if let Some(control) = compact_control.as_ref() {
        for row in &mut grouped_effect_rows {
            let target_subject = derive_real_grouped_target_subject(row, control);
            let target_scope_ordinal = control
                .grouped_target_scope_ordinals_0x7fb
                .get(row.group_index)
                .copied();
            row.grouped_target_subject = target_subject
                .map(real_grouped_target_subject_name)
                .map(str::to_string);
            row.grouped_target_scope = derive_real_grouped_target_scope_name(
                row,
                control,
                target_subject,
                target_scope_ordinal,
            );
            let company_target_present = control
                .grouped_target_scope_ordinals_0x7fb
                .get(row.group_index)
                .copied()
                .and_then(real_grouped_company_target)
                .is_some();
            let chairman_target_present = control
                .grouped_target_scope_ordinals_0x7fb
                .get(row.group_index)
                .copied()
                .is_some_and(real_grouped_chairman_target_supported_in_runtime);
            let territory_target_present = control
                .grouped_territory_selectors_0x80f
                .get(row.group_index)
                .is_some_and(|selector| *selector >= 0);
            if row.descriptor_id == 15
                && row.row_shape == "bool_toggle"
                && row.raw_scalar_value != 0
                && !company_target_present
                && !territory_target_present
            {
                row.notes
                    .push("retire train row is missing company and territory scope".to_string());
            }
            if row.descriptor_id == 3
                && row.row_shape == "bool_toggle"
                && row.raw_scalar_value != 0
                && (!company_target_present || !territory_target_present)
            {
                row.notes
                    .push("territory access row is missing company or territory scope".to_string());
            }
            if matches!(target_subject, Some(RealGroupedTargetSubject::Chairman))
                && !chairman_target_present
            {
                let ordinal = target_scope_ordinal.unwrap_or(u8::MAX);
                row.notes.push(format!(
                    "chairman row uses unsupported grouped target scope ordinal {ordinal}"
                ));
            }
        }
    }

    let negative_sentinel_scope = compact_control.as_ref().and_then(|control| {
        derive_negative_sentinel_scope_summary(&standalone_condition_rows, control)
    });
    let decoded_conditions =
        decode_real_condition_rows(&standalone_condition_rows, negative_sentinel_scope.as_ref());
    let decoded_actions = compact_control
        .as_ref()
        .map(|control| decode_real_grouped_effect_actions(&grouped_effect_rows, control))
        .unwrap_or_default();
    let ordinary_condition_row_count = standalone_condition_rows
        .iter()
        .filter(|row| row.raw_condition_id >= 0)
        .count();
    let executable_import_ready = !grouped_effect_rows.is_empty()
        && decoded_actions.len() == grouped_effect_rows.len()
        && decoded_conditions.len() == ordinary_condition_row_count
        && decoded_actions
            .iter()
            .all(runtime_effect_supported_for_save_import)
        && decoded_conditions
            .iter()
            .all(runtime_condition_supported_for_save_import);
    let consumed_len = cursor;
    Some((
        SmpLoadedPackedEventRecordSummary {
            record_index,
            live_entry_id,
            payload_offset: Some(payload_offset),
            payload_len: Some(consumed_len),
            decode_status: "parity_only".to_string(),
            payload_family: "real_packed_v1".to_string(),
            trigger_kind: compact_control.as_ref().map(|control| control.mode_byte_0x7ef),
            active: None,
            marks_collection_dirty: None,
            one_shot: compact_control
                .as_ref()
                .map(|control| control.one_shot_header_0x7f5 != 0),
            compact_control,
            text_bands,
            standalone_condition_row_count,
            standalone_condition_rows,
            negative_sentinel_scope,
            grouped_effect_row_counts,
            grouped_effect_rows,
            decoded_conditions,
            decoded_actions,
            executable_import_ready,
            notes: vec![
                "decoded from grounded real 0x4e9a row framing".to_string(),
                "grouped descriptor labels and target masks come from the checked-in effect table recovered around 0x006103a0".to_string(),
            ],
        },
        consumed_len,
    ))
}

pub(in crate::inspect::smp) fn parse_optional_real_compact_control_summary(
    record_body: &[u8],
    cursor: &mut usize,
) -> Option<Option<SmpLoadedPackedEventCompactControlSummary>> {
    if read_u16_at(record_body, *cursor)? == PACKED_EVENT_REAL_CONDITION_MARKER {
        return Some(None);
    }

    let end = cursor.checked_add(PACKED_EVENT_REAL_COMPACT_CONTROL_LEN)?;
    let bytes = record_body.get(*cursor..end)?;
    let mut local = 0usize;
    let mode_byte_0x7ef = read_u8_at(bytes, local)?;
    local += 1;
    let primary_selector_0x7f0 = read_u32_at(bytes, local)?;
    local += 4;
    let grouped_mode_0x7f4 = read_u8_at(bytes, local)?;
    local += 1;
    let one_shot_header_0x7f5 = read_u32_at(bytes, local)?;
    local += 4;
    let modifier_flag_0x7f9 = read_u8_at(bytes, local)?;
    local += 1;
    let modifier_flag_0x7fa = read_u8_at(bytes, local)?;
    local += 1;

    let mut grouped_target_scope_ordinals_0x7fb = Vec::with_capacity(PACKED_EVENT_REAL_GROUP_COUNT);
    for _ in 0..PACKED_EVENT_REAL_GROUP_COUNT {
        grouped_target_scope_ordinals_0x7fb.push(read_u8_at(bytes, local)?);
        local += 1;
    }

    let mut grouped_scope_checkboxes_0x7ff = Vec::with_capacity(PACKED_EVENT_REAL_GROUP_COUNT);
    for _ in 0..PACKED_EVENT_REAL_GROUP_COUNT {
        grouped_scope_checkboxes_0x7ff.push(read_u8_at(bytes, local)?);
        local += 1;
    }

    let summary_toggle_0x800 = read_u8_at(bytes, local)?;
    local += 1;

    let mut grouped_territory_selectors_0x80f = Vec::with_capacity(PACKED_EVENT_REAL_GROUP_COUNT);
    for _ in 0..PACKED_EVENT_REAL_GROUP_COUNT {
        grouped_territory_selectors_0x80f.push(read_i32_at(bytes, local)?);
        local += 4;
    }

    if local != bytes.len() {
        return None;
    }
    if read_u16_at(record_body, end)? != PACKED_EVENT_REAL_CONDITION_MARKER {
        return None;
    }

    *cursor = end;
    Some(Some(SmpLoadedPackedEventCompactControlSummary {
        mode_byte_0x7ef,
        primary_selector_0x7f0,
        grouped_mode_0x7f4,
        one_shot_header_0x7f5,
        modifier_flag_0x7f9,
        modifier_flag_0x7fa,
        grouped_target_scope_ordinals_0x7fb,
        grouped_scope_checkboxes_0x7ff,
        summary_toggle_0x800,
        grouped_territory_selectors_0x80f,
    }))
}
