use super::offsets::*;
use crate::inspect::smp::common::{
    SmpSaveDwordCandidate, SmpSaveTaggedCollectionHeaderProbe, read_ascii_c_string_at, read_u8_at,
    read_u32_at,
};
use crate::inspect::smp::world::is_save_name_char;

pub(in crate::inspect::smp) fn detect_save_company_record_start_offset(
    bytes: &[u8],
    header_probe: &SmpSaveTaggedCollectionHeaderProbe,
) -> Option<usize> {
    let observed_entry_count = header_probe.live_record_count as usize;
    let record_stride = header_probe.direct_record_stride as usize;
    let scan_limit = SAVE_COMPANY_RECORD_START_SCAN_LIMIT.min(record_stride);
    let base_offset = header_probe.metadata_tag_offset.checked_add(4)?;
    let mut best_start = None;
    let mut best_score = 0usize;

    for start in 0..scan_limit {
        let mut score = 0usize;
        let mut seen_ids = std::collections::BTreeSet::new();
        let mut valid = true;
        for index in 0usize..observed_entry_count {
            let record_offset = match base_offset
                .checked_add(start)
                .and_then(|offset| offset.checked_add(index.checked_mul(record_stride)?))
            {
                Some(offset) => offset,
                None => {
                    valid = false;
                    break;
                }
            };
            let company_id = match read_u32_at(bytes, record_offset) {
                Some(value)
                    if value >= 1
                        && value <= header_probe.live_id_bound
                        && seen_ids.insert(value) =>
                {
                    value
                }
                _ => {
                    valid = false;
                    break;
                }
            };
            let active = match read_u8_at(bytes, record_offset + SAVE_COMPANY_RECORD_ACTIVE_OFFSET)
            {
                Some(0 | 1) => {
                    score += 1;
                    true
                }
                _ => {
                    valid = false;
                    break;
                }
            };
            let linked = match read_u32_at(
                bytes,
                record_offset + SAVE_COMPANY_RECORD_LINKED_CHAIRMAN_OFFSET,
            ) {
                Some(value) if value <= 0x100 => value,
                _ => {
                    valid = false;
                    break;
                }
            };
            let name = match read_ascii_c_string_at(
                bytes,
                record_offset + SAVE_COMPANY_RECORD_NAME_OFFSET,
                SAVE_COMPANY_RECORD_NAME_MAX_LEN,
            ) {
                Some(name) if !name.is_empty() && name.chars().all(is_save_name_char) => name,
                _ => {
                    valid = false;
                    break;
                }
            };
            score += name.len();
            if active {
                score += 8;
            }
            if linked != 0 {
                score += 2;
            }
            if company_id == (index + 1) as u32 {
                score += 4;
            }
        }
        if valid && score > best_score {
            best_score = score;
            best_start = Some(start);
        }
    }

    best_start
}

pub(in crate::inspect::smp) fn build_save_dword_candidate(
    bytes: &[u8],
    record_offset: usize,
    label: &str,
    relative_offset: usize,
) -> Option<SmpSaveDwordCandidate> {
    let raw_u32 = read_u32_at(bytes, record_offset + relative_offset)?;
    Some(SmpSaveDwordCandidate {
        label: label.to_string(),
        relative_offset,
        relative_offset_hex: format!("0x{relative_offset:x}"),
        raw_u32,
        raw_u32_hex: format!("0x{raw_u32:08x}"),
        value_i32: raw_u32 as i32,
        value_f32: f32::from_bits(raw_u32),
    })
}
