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

pub(in crate::inspect::smp) fn try_parse_synthetic_event_runtime_record_summaries(
    records_payload: &[u8],
    records_payload_offset: usize,
    live_entry_ids: &[u32],
) -> Option<Vec<SmpLoadedPackedEventRecordSummary>> {
    if !records_payload.starts_with(PACKED_EVENT_RECORDS_SYNTHETIC_MAGIC) {
        return None;
    }

    let mut cursor = PACKED_EVENT_RECORDS_SYNTHETIC_MAGIC.len();
    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_len = usize::try_from(read_u32_at(records_payload, cursor)?).ok()?;
        cursor += 4;
        let record_body = records_payload.get(cursor..cursor + record_len)?;
        records.push(parse_synthetic_event_runtime_record_summary(
            record_body,
            records_payload_offset + cursor,
            record_index,
            live_entry_id,
        )?);
        cursor += record_len;
    }

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

    Some(records)
}

pub(in crate::inspect::smp) fn parse_synthetic_event_runtime_record_summary(
    record_body: &[u8],
    payload_offset: usize,
    record_index: usize,
    live_entry_id: u32,
) -> Option<SmpLoadedPackedEventRecordSummary> {
    if !record_body.starts_with(PACKED_EVENT_RECORD_SYNTHETIC_MAGIC) {
        return None;
    }

    let mut cursor = PACKED_EVENT_RECORD_SYNTHETIC_MAGIC.len();
    let trigger_kind = read_u8_at(record_body, cursor)?;
    cursor += 1;
    let flags = read_u8_at(record_body, cursor)?;
    cursor += 1;
    let standalone_condition_row_count = usize::from(read_u8_at(record_body, cursor)?);
    cursor += 1;
    let action_count = usize::from(read_u8_at(record_body, cursor)?);
    cursor += 1;

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

    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 mut decoded_actions = Vec::with_capacity(action_count);
    for _ in 0..action_count {
        decoded_actions.push(parse_synthetic_packed_event_action(
            record_body,
            &mut cursor,
        )?);
    }

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

    let executable_import_ready = decoded_actions
        .iter()
        .all(runtime_effect_supported_for_save_import);

    Some(SmpLoadedPackedEventRecordSummary {
        record_index,
        live_entry_id,
        payload_offset: Some(payload_offset),
        payload_len: Some(record_body.len()),
        decode_status: if executable_import_ready {
            "executable".to_string()
        } else {
            "parity_only".to_string()
        },
        payload_family: "synthetic_harness".to_string(),
        trigger_kind: Some(trigger_kind),
        active: Some(flags & 0x01 != 0),
        marks_collection_dirty: Some(flags & 0x02 != 0),
        one_shot: Some(flags & 0x04 != 0),
        compact_control: None,
        text_bands,
        standalone_condition_row_count,
        standalone_condition_rows: Vec::new(),
        negative_sentinel_scope: None,
        grouped_effect_row_counts,
        grouped_effect_rows: Vec::new(),
        decoded_conditions: Vec::new(),
        decoded_actions,
        executable_import_ready,
        notes: vec!["decoded from the current synthetic packed-event record harness".to_string()],
    })
}

pub(in crate::inspect::smp) fn parse_synthetic_packed_event_action(
    bytes: &[u8],
    cursor: &mut usize,
) -> Option<RuntimeEffect> {
    let opcode = read_u8_at(bytes, *cursor)?;
    *cursor += 1;
    match opcode {
        0x01 => {
            let key = parse_len_prefixed_string(bytes, cursor)?;
            let value = read_u8_at(bytes, *cursor)? != 0;
            *cursor += 1;
            Some(RuntimeEffect::SetWorldFlag { key, value })
        }
        0x02 => {
            let target = parse_synthetic_company_target(bytes, cursor)?;
            let delta = read_i64_at(bytes, *cursor)?;
            *cursor += 8;
            Some(RuntimeEffect::AdjustCompanyCash { target, delta })
        }
        0x03 => {
            let target = parse_synthetic_company_target(bytes, cursor)?;
            let delta = read_i64_at(bytes, *cursor)?;
            *cursor += 8;
            Some(RuntimeEffect::AdjustCompanyDebt { target, delta })
        }
        0x04 => {
            let name = parse_len_prefixed_string(bytes, cursor)?;
            let value = read_u32_at(bytes, *cursor)?;
            *cursor += 4;
            Some(RuntimeEffect::SetCandidateAvailability { name, value })
        }
        0x05 => {
            let label = parse_len_prefixed_string(bytes, cursor)?;
            let value = read_u32_at(bytes, *cursor)?;
            *cursor += 4;
            Some(RuntimeEffect::SetSpecialCondition { label, value })
        }
        0x06 => {
            let template_len = usize::try_from(read_u32_at(bytes, *cursor)?).ok()?;
            *cursor += 4;
            let template_bytes = bytes.get(*cursor..*cursor + template_len)?;
            let record = parse_synthetic_event_runtime_record_template(template_bytes)?;
            *cursor += template_len;
            Some(RuntimeEffect::AppendEventRecord {
                record: Box::new(record),
            })
        }
        0x07 => {
            let record_id = read_u32_at(bytes, *cursor)?;
            *cursor += 4;
            Some(RuntimeEffect::ActivateEventRecord { record_id })
        }
        0x08 => {
            let record_id = read_u32_at(bytes, *cursor)?;
            *cursor += 4;
            Some(RuntimeEffect::DeactivateEventRecord { record_id })
        }
        0x09 => {
            let record_id = read_u32_at(bytes, *cursor)?;
            *cursor += 4;
            Some(RuntimeEffect::RemoveEventRecord { record_id })
        }
        _ => None,
    }
}

pub(in crate::inspect::smp) fn parse_synthetic_event_runtime_record_template(
    bytes: &[u8],
) -> Option<RuntimeEventRecordTemplate> {
    if !bytes.starts_with(PACKED_EVENT_RECORD_TEMPLATE_SYNTHETIC_MAGIC) {
        return None;
    }

    let mut cursor = PACKED_EVENT_RECORD_TEMPLATE_SYNTHETIC_MAGIC.len();
    let record_id = read_u32_at(bytes, cursor)?;
    cursor += 4;
    let trigger_kind = read_u8_at(bytes, cursor)?;
    cursor += 1;
    let flags = read_u8_at(bytes, cursor)?;
    cursor += 1;
    let action_count = usize::from(read_u8_at(bytes, cursor)?);
    cursor += 1;
    cursor += 1;

    let mut effects = Vec::with_capacity(action_count);
    for _ in 0..action_count {
        effects.push(parse_synthetic_packed_event_action(bytes, &mut cursor)?);
    }

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

    Some(RuntimeEventRecordTemplate {
        record_id,
        trigger_kind,
        active: flags & 0x01 != 0,
        marks_collection_dirty: flags & 0x02 != 0,
        one_shot: flags & 0x04 != 0,
        conditions: Vec::new(),
        effects,
    })
}

pub(in crate::inspect::smp) fn parse_synthetic_company_target(
    bytes: &[u8],
    cursor: &mut usize,
) -> Option<RuntimeCompanyTarget> {
    let target_kind = read_u8_at(bytes, *cursor)?;
    *cursor += 1;
    match target_kind {
        0x00 => Some(RuntimeCompanyTarget::AllActive),
        0x01 => {
            let count = usize::from(read_u8_at(bytes, *cursor)?);
            *cursor += 1;
            let mut ids = Vec::with_capacity(count);
            for _ in 0..count {
                ids.push(read_u32_at(bytes, *cursor)?);
                *cursor += 4;
            }
            Some(RuntimeCompanyTarget::Ids { ids })
        }
        _ => None,
    }
}

pub(in crate::inspect::smp) fn parse_len_prefixed_string(
    bytes: &[u8],
    cursor: &mut usize,
) -> Option<String> {
    let len = usize::from(read_u8_at(bytes, *cursor)?);
    *cursor += 1;
    let text_bytes = bytes.get(*cursor..*cursor + len)?;
    *cursor += len;
    Some(String::from_utf8_lossy(text_bytes).into_owned())
}

pub(in crate::inspect::smp) fn parse_optional_u16_len_prefixed_string(
    bytes: &[u8],
    cursor: &mut usize,
) -> Option<Option<String>> {
    let len = usize::from(read_u16_at(bytes, *cursor)?);
    *cursor += 2;
    if len == 0 {
        return Some(None);
    }
    let text_bytes = bytes.get(*cursor..*cursor + len)?;
    *cursor += len;
    Some(Some(String::from_utf8_lossy(text_bytes).into_owned()))
}
