use crate::inspect::smp::bundle::SmpContainerProfile;
use crate::inspect::smp::catalog::offsets::world::{
    RT3_SAVE_WORLD_BLOCK_ABSOLUTE_COUNTER_MIRROR_RELATIVE_OFFSET,
    RT3_SAVE_WORLD_BLOCK_ABSOLUTE_COUNTER_RELATIVE_OFFSET,
    RT3_SAVE_WORLD_BLOCK_BANKRUPTCY_POLICY_RELATIVE_OFFSET,
    RT3_SAVE_WORLD_BLOCK_BOND_POLICY_RELATIVE_OFFSET,
    RT3_SAVE_WORLD_BLOCK_BUILDING_DENSITY_GROWTH_RELATIVE_OFFSET, RT3_SAVE_WORLD_BLOCK_CHUNK_TAG,
    RT3_SAVE_WORLD_BLOCK_CURRENT_CALENDAR_TUPLE_WORD_2_RELATIVE_OFFSET,
    RT3_SAVE_WORLD_BLOCK_CURRENT_CALENDAR_TUPLE_WORD_RELATIVE_OFFSET,
    RT3_SAVE_WORLD_BLOCK_DIVIDEND_POLICY_RELATIVE_OFFSET,
    RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_CANDIDATE_FIELDS,
    RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_ROOT_RELATIVE_OFFSET,
    RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_WINDOW_LEN_DWORDS, RT3_SAVE_WORLD_BLOCK_LEN,
    RT3_SAVE_WORLD_BLOCK_NEXT_CHUNK_TAG, RT3_SAVE_WORLD_BLOCK_STOCK_POLICY_RELATIVE_OFFSET,
};
use crate::inspect::smp::common::SmpSaveDwordCandidate;
use crate::inspect::smp::common::{find_u32_le_offsets, read_u8_at, read_u16_at, read_u32_at};
use crate::inspect::smp::world::{
    SmpSaveWorldFinanceNeighborhoodProbe, build_save_dword_candidate,
};

pub(in crate::inspect::smp) fn parse_save_world_finance_neighborhood_probe(
    bytes: &[u8],
    file_extension_hint: Option<&str>,
    container_profile: Option<&SmpContainerProfile>,
) -> Option<SmpSaveWorldFinanceNeighborhoodProbe> {
    if file_extension_hint != Some("gms") {
        return None;
    }
    let profile = container_profile?;
    let supported = matches!(
        profile.profile_family.as_str(),
        "rt3-classic-save-container-v1"
            | "rt3-105-save-container-v1"
            | "rt3-105-scenario-save-container-v1"
            | "rt3-105-alt-save-container-v1"
    );
    if !supported {
        return None;
    }

    for chunk_tag_offset in find_u32_le_offsets(bytes, RT3_SAVE_WORLD_BLOCK_CHUNK_TAG) {
        let payload_offset = chunk_tag_offset + 4;
        let next_chunk_tag_offset = payload_offset.checked_add(RT3_SAVE_WORLD_BLOCK_LEN)?;
        if read_u32_at(bytes, next_chunk_tag_offset) != Some(RT3_SAVE_WORLD_BLOCK_NEXT_CHUNK_TAG) {
            continue;
        }

        let current_calendar_tuple_word_lane = build_save_dword_candidate(
            bytes,
            payload_offset,
            "current_calendar_tuple_word",
            RT3_SAVE_WORLD_BLOCK_CURRENT_CALENDAR_TUPLE_WORD_RELATIVE_OFFSET,
        )?;
        let packed_year_word_raw_u16 = read_u16_at(
            bytes,
            payload_offset + RT3_SAVE_WORLD_BLOCK_CURRENT_CALENDAR_TUPLE_WORD_RELATIVE_OFFSET,
        )?;
        let partial_year_progress_raw_u8 = read_u8_at(
            bytes,
            payload_offset + RT3_SAVE_WORLD_BLOCK_CURRENT_CALENDAR_TUPLE_WORD_RELATIVE_OFFSET + 2,
        )?;
        let current_calendar_tuple_word_2_lane = build_save_dword_candidate(
            bytes,
            payload_offset,
            "current_calendar_tuple_word_2",
            RT3_SAVE_WORLD_BLOCK_CURRENT_CALENDAR_TUPLE_WORD_2_RELATIVE_OFFSET,
        )?;
        let absolute_counter_lane = build_save_dword_candidate(
            bytes,
            payload_offset,
            "absolute_calendar_counter",
            RT3_SAVE_WORLD_BLOCK_ABSOLUTE_COUNTER_RELATIVE_OFFSET,
        )?;
        let absolute_counter_mirror_lane = build_save_dword_candidate(
            bytes,
            payload_offset,
            "absolute_calendar_counter_mirror",
            RT3_SAVE_WORLD_BLOCK_ABSOLUTE_COUNTER_MIRROR_RELATIVE_OFFSET,
        )?;
        let stock_policy_raw_u8 = read_u8_at(
            bytes,
            payload_offset + RT3_SAVE_WORLD_BLOCK_STOCK_POLICY_RELATIVE_OFFSET,
        )?;
        let bond_policy_raw_u8 = read_u8_at(
            bytes,
            payload_offset + RT3_SAVE_WORLD_BLOCK_BOND_POLICY_RELATIVE_OFFSET,
        )?;
        let bankruptcy_policy_raw_u8 = read_u8_at(
            bytes,
            payload_offset + RT3_SAVE_WORLD_BLOCK_BANKRUPTCY_POLICY_RELATIVE_OFFSET,
        )?;
        let dividend_policy_raw_u8 = read_u8_at(
            bytes,
            payload_offset + RT3_SAVE_WORLD_BLOCK_DIVIDEND_POLICY_RELATIVE_OFFSET,
        )?;
        let building_density_growth_setting_lane = build_save_dword_candidate(
            bytes,
            payload_offset,
            "building_density_growth_setting",
            RT3_SAVE_WORLD_BLOCK_BUILDING_DENSITY_GROWTH_RELATIVE_OFFSET,
        )?;
        let dword_candidates =
            build_save_world_finance_neighborhood_candidates(bytes, payload_offset)?;

        return Some(SmpSaveWorldFinanceNeighborhoodProbe {
            profile_family: profile.profile_family.clone(),
            source_kind: "save-direct-world-block".to_string(),
            semantic_family: "scenario-save-world-finance-neighborhood".to_string(),
            chunk_tag_offset,
            payload_offset,
            payload_len: RT3_SAVE_WORLD_BLOCK_LEN,
            payload_len_hex: format!("0x{:x}", RT3_SAVE_WORLD_BLOCK_LEN),
            packed_year_word_raw_u16,
            packed_year_word_raw_hex: format!("0x{packed_year_word_raw_u16:04x}"),
            partial_year_progress_raw_u8,
            partial_year_progress_raw_hex: format!("0x{partial_year_progress_raw_u8:02x}"),
            current_calendar_tuple_word_lane,
            current_calendar_tuple_word_2_lane,
            absolute_counter_lane,
            absolute_counter_mirror_lane,
            stock_policy_raw_u8,
            stock_policy_raw_hex: format!("0x{stock_policy_raw_u8:02x}"),
            bond_policy_raw_u8,
            bond_policy_raw_hex: format!("0x{bond_policy_raw_u8:02x}"),
            bankruptcy_policy_raw_u8,
            bankruptcy_policy_raw_hex: format!("0x{bankruptcy_policy_raw_u8:02x}"),
            dividend_policy_raw_u8,
            dividend_policy_raw_hex: format!("0x{dividend_policy_raw_u8:02x}"),
            building_density_growth_setting_lane,
            dword_candidates,
            evidence: vec![
                format!(
                    "chunk tag 0x32c8 at 0x{chunk_tag_offset:x} matches the fixed [world+0x04] save block"
                ),
                format!(
                    "next chunk tag 0x32c9 appears at 0x{next_chunk_tag_offset:x}, matching the documented 0x4f2c payload span"
                ),
                format!(
                    "payload +0x{:x}/+0x{:x}/+0x{:x} carry the saved world calendar tuple and absolute counter lanes that later company stock-issue cooldown readers compare against",
                    RT3_SAVE_WORLD_BLOCK_CURRENT_CALENDAR_TUPLE_WORD_RELATIVE_OFFSET,
                    RT3_SAVE_WORLD_BLOCK_CURRENT_CALENDAR_TUPLE_WORD_2_RELATIVE_OFFSET,
                    RT3_SAVE_WORLD_BLOCK_ABSOLUTE_COUNTER_RELATIVE_OFFSET
                ),
                format!(
                    "payload +0x{:x}/+0x{:x}/+0x{:x}/+0x{:x} carry the stock, bond, bankruptcy, and dividend finance-policy bytes mirrored from scenario offsets 0x4a87/0x4a8b/0x4a8f/0x4a93",
                    RT3_SAVE_WORLD_BLOCK_STOCK_POLICY_RELATIVE_OFFSET,
                    RT3_SAVE_WORLD_BLOCK_BOND_POLICY_RELATIVE_OFFSET,
                    RT3_SAVE_WORLD_BLOCK_BANKRUPTCY_POLICY_RELATIVE_OFFSET,
                    RT3_SAVE_WORLD_BLOCK_DIVIDEND_POLICY_RELATIVE_OFFSET
                ),
                format!(
                    "payload +0x{:x} carries the fixed-world building-density growth setting mirrored from `[world+0x4c7c]`, which the annual repurchase and dividend policy helpers both read directly",
                    RT3_SAVE_WORLD_BLOCK_BUILDING_DENSITY_GROWTH_RELATIVE_OFFSET
                ),
                "finance-neighborhood candidates cover the fixed dword strip around the grounded world calendar tuple, absolute-counter, selection-context, and issue-0x37 lanes so broader finance reader closure can build on one rehosted owner surface.".to_string(),
            ],
        });
    }

    None
}

pub(in crate::inspect::smp) fn build_save_world_finance_neighborhood_candidates(
    bytes: &[u8],
    payload_offset: usize,
) -> Option<Vec<SmpSaveDwordCandidate>> {
    (0..RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_WINDOW_LEN_DWORDS)
        .map(|index| {
            let relative_offset =
                RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_ROOT_RELATIVE_OFFSET + index * 4;
            let label = RT3_SAVE_WORLD_BLOCK_FINANCE_NEIGHBORHOOD_CANDIDATE_FIELDS
                .iter()
                .find(|(_, named_offset)| *named_offset == relative_offset)
                .map(|(name, _)| (*name).to_string())
                .unwrap_or_else(|| format!("finance_neighborhood_word_{:02}", index + 1));
            build_save_dword_candidate(bytes, payload_offset, &label, relative_offset)
        })
        .collect()
}
