use super::bonds::{
    parse_save_company_highest_coupon_live_bond_principal,
    parse_save_company_largest_live_bond_principal, parse_save_company_live_bond_slots,
    parse_save_company_total_debt,
};
use super::capacity::parse_save_company_available_track_laying_capacity;
use super::offsets::*;
use super::scan::detect_save_company_record_start_offset;
use super::stat_bands::{
    build_save_company_stat_band_candidates, build_save_company_stat_qword_bits,
    build_save_i32_term_strip, build_save_u32_field_map,
    decode_save_company_current_year_stat_slot, runtime_company_stat_band_candidate_from_save,
};
use crate::inspect::smp::common::{
    SmpSaveTaggedCollectionHeaderProbe, parse_nonzero_u32, read_i32_at, read_u8_at, read_u32_at,
    read_u64_at,
};
use crate::inspect::smp::world::*;
use crate::inspect::smp::*;

pub(in crate::inspect::smp) fn parse_save_company_roster_probe(
    bytes: &[u8],
    header_probe: Option<&SmpSaveTaggedCollectionHeaderProbe>,
    selection_probe: Option<&SmpSaveWorldSelectionContextProbe>,
) -> Option<SmpLoadedCompanyRoster> {
    let header_probe = header_probe?;
    let observed_entry_count = header_probe.live_record_count as usize;
    if observed_entry_count == 0 {
        return Some(SmpLoadedCompanyRoster {
            source_kind: "save-company-direct-records".to_string(),
            semantic_family: "scenario-save-company-direct-records".to_string(),
            observed_entry_count,
            selected_company_id: selection_probe.map(|probe| probe.selected_company_id),
            entries: Vec::new(),
        });
    }

    let record_start_offset = detect_save_company_record_start_offset(bytes, header_probe)?;
    let record_stride = header_probe.direct_record_stride as usize;
    let base_offset = header_probe
        .metadata_tag_offset
        .checked_add(4)?
        .checked_add(record_start_offset)?;

    let mut entries = Vec::with_capacity(observed_entry_count);
    for index in 0usize..observed_entry_count {
        let record_offset = base_offset.checked_add(index.checked_mul(record_stride)?)?;
        let company_id = read_u32_at(bytes, record_offset)?;
        let active = read_u8_at(bytes, record_offset + SAVE_COMPANY_RECORD_ACTIVE_OFFSET)? != 0;
        let linked_chairman_profile_id = parse_nonzero_u32(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_LINKED_CHAIRMAN_OFFSET,
        )?;
        let outstanding_shares = read_u32_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_OUTSTANDING_SHARES_OFFSET,
        )?;
        let debt = parse_save_company_total_debt(bytes, record_offset)?;
        let bond_count = read_u8_at(bytes, record_offset + SAVE_COMPANY_RECORD_BOND_COUNT_OFFSET)?;
        let live_bond_slots = parse_save_company_live_bond_slots(bytes, record_offset)?;
        let largest_live_bond_principal =
            parse_save_company_largest_live_bond_principal(bytes, record_offset)?;
        let highest_coupon_live_bond_principal =
            parse_save_company_highest_coupon_live_bond_principal(bytes, record_offset)?;
        let available_track_laying_capacity =
            parse_save_company_available_track_laying_capacity(bytes, record_offset)?;
        let mutable_support_scalar_raw_u32 = read_u32_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_SUPPORT_SCALAR_OFFSET,
        )?;
        let young_company_support_scalar_raw_u32 = read_u32_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_COMPANY_VALUE_OFFSET,
        )?;
        let support_progress_word = read_u32_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_SUPPORT_PROGRESS_OFFSET,
        )?;
        let recent_per_share_cache_absolute_counter = read_u32_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0CFB_OFFSET,
        )?;
        let recent_per_share_cached_value_bits = read_u64_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0CFB_OFFSET + 4,
        )?;
        let recent_per_share_subscore_raw_u32 = read_u32_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_RECENT_PER_SHARE_SUBSCORE_OFFSET,
        )?;
        let cached_share_price_raw_u32 = read_u32_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_CACHED_SHARE_PRICE_OFFSET,
        )?;
        let chairman_salary_baseline = read_u32_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_CHAIRMAN_SALARY_BASELINE_OFFSET,
        )?;
        let chairman_salary_current = read_u32_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_CHAIRMAN_SALARY_CURRENT_OFFSET,
        )?;
        let chairman_bonus_year = read_u32_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_CHAIRMAN_BONUS_YEAR_OFFSET,
        )?;
        let chairman_bonus_amount = read_i32_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_CHAIRMAN_BONUS_AMOUNT_OFFSET,
        )?;
        let founding_year = read_u32_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_FOUNDING_YEAR_OFFSET,
        )?;
        let last_bankruptcy_year = read_u32_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_LAST_BANKRUPTCY_YEAR_OFFSET,
        )?;
        let last_dividend_year = read_u32_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_LAST_DIVIDEND_YEAR_OFFSET,
        )?;
        let current_issue_calendar_word = read_u32_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_CURRENT_ISSUE_CALENDAR_OFFSET,
        )?;
        let current_issue_calendar_word_2 = read_u32_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_CURRENT_ISSUE_CALENDAR_OFFSET_2,
        )?;
        let prior_issue_calendar_word = read_u32_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_PRIOR_ISSUE_CALENDAR_OFFSET,
        )?;
        let prior_issue_calendar_word_2 = read_u32_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_PRIOR_ISSUE_CALENDAR_OFFSET_2,
        )?;
        let preferred_locomotive_engine_type_raw_u8 = read_u8_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_PREFERRED_LOCOMOTIVE_ENGINE_TYPE_OFFSET,
        )?;
        let city_connection_latch = read_u8_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_CITY_CONNECTION_LATCH_OFFSET,
        )? != 0;
        let linked_transit_latch = read_u8_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_LINKED_TRANSIT_LATCH_OFFSET,
        )? != 0;
        let linked_transit_route_anchor_entry_id = parse_nonzero_u32(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_LINKED_TRANSIT_ROUTE_ANCHOR_ENTRY_ID_OFFSET,
        )?;
        let linked_transit_route_anchor_fallback_counts =
            SAVE_COMPANY_RECORD_LINKED_TRANSIT_ROUTE_ANCHOR_FALLBACK_COUNT_OFFSETS
                .iter()
                .map(|relative_offset| read_u32_at(bytes, record_offset + *relative_offset))
                .collect::<Option<Vec<_>>>()?;
        let merger_cooldown_year = parse_nonzero_u32(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_MERGER_COOLDOWN_OFFSET,
        )?;
        let takeover_cooldown_year = parse_nonzero_u32(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_TAKEOVER_COOLDOWN_OFFSET,
        )?;
        let stat_band_root_0cfb_candidates = build_save_company_stat_band_candidates(
            bytes,
            record_offset,
            SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0CFB_OFFSET,
            "stat_band_0cfb",
            SAVE_COMPANY_RECORD_STAT_BAND_ROOT_WINDOW_LEN_DWORDS,
        )?;
        let stat_band_root_0d7f_candidates = build_save_company_stat_band_candidates(
            bytes,
            record_offset,
            SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0D7F_OFFSET,
            "stat_band_0d7f",
            SAVE_COMPANY_RECORD_STAT_BAND_ROOT_WINDOW_LEN_DWORDS,
        )?;
        let stat_band_root_1c47_candidates = build_save_company_stat_band_candidates(
            bytes,
            record_offset,
            SAVE_COMPANY_RECORD_STAT_BAND_ROOT_1C47_OFFSET,
            "stat_band_1c47",
            SAVE_COMPANY_RECORD_STAT_BAND_ROOT_WINDOW_LEN_DWORDS,
        )?;
        let year_stat_family_qword_bits = build_save_company_stat_qword_bits(
            bytes,
            record_offset,
            SAVE_COMPANY_RECORD_STAT_BAND_ROOT_0D7F_OFFSET,
            SAVE_COMPANY_RECORD_YEAR_STAT_FAMILY_QWORD_COUNT,
        )?;
        let special_stat_family_232a_qword_bits = build_save_company_stat_qword_bits(
            bytes,
            record_offset,
            SAVE_COMPANY_RECORD_STAT_BAND_ROOT_1C47_OFFSET,
            SAVE_COMPANY_RECORD_SPECIAL_STAT_FAMILY_232A_QWORD_COUNT,
        )?;
        let direct_control_transfer_float_fields_raw_u32 = build_save_u32_field_map(
            bytes,
            record_offset,
            &SAVE_COMPANY_RECORD_DIRECT_CONTROL_TRANSFER_FLOAT_FIELDS,
        )?;
        let direct_control_transfer_int_fields_raw_u32 = build_save_u32_field_map(
            bytes,
            record_offset,
            &SAVE_COMPANY_RECORD_DIRECT_CONTROL_TRANSFER_INT_FIELDS,
        )?;
        let issue_opinion_terms_raw_i32 = build_save_i32_term_strip(
            bytes,
            record_offset,
            SAVE_COMPANY_RECORD_ISSUE_OPINION_TERMS_OFFSET,
            SAVE_COMPANY_RECORD_ISSUE_OPINION_TERM_COUNT,
        )?;
        let current_cash = decode_save_company_current_year_stat_slot(
            &year_stat_family_qword_bits,
            crate::event::metrics::RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH,
        )
        .and_then(round_f64_to_i64)
        .unwrap_or(0);
        entries.push(SmpLoadedCompanyRosterEntry {
            company_id,
            active,
            controller_kind: RuntimeCompanyControllerKind::Unknown,
            current_cash,
            debt,
            credit_rating_score: None,
            prime_rate: None,
            available_track_laying_capacity,
            track_piece_counts: RuntimeTrackPieceCounts::default(),
            linked_chairman_profile_id,
            book_value_per_share: 0,
            investor_confidence: 0,
            management_attitude: 0,
            takeover_cooldown_year,
            merger_cooldown_year,
            preferred_locomotive_engine_type_raw_u8: Some(preferred_locomotive_engine_type_raw_u8),
            market_state: Some(RuntimeCompanyMarketState {
                outstanding_shares,
                bond_count,
                live_bond_slots,
                largest_live_bond_principal,
                highest_coupon_live_bond_principal,
                mutable_support_scalar_raw_u32,
                young_company_support_scalar_raw_u32,
                support_progress_word,
                recent_per_share_cache_absolute_counter,
                recent_per_share_cached_value_bits,
                recent_per_share_subscore_raw_u32,
                cached_share_price_raw_u32,
                chairman_salary_baseline,
                chairman_salary_current,
                chairman_bonus_year,
                chairman_bonus_amount,
                founding_year,
                last_bankruptcy_year,
                last_dividend_year,
                current_issue_calendar_word,
                current_issue_calendar_word_2,
                prior_issue_calendar_word,
                prior_issue_calendar_word_2,
                city_connection_latch,
                linked_transit_latch,
                linked_transit_route_anchor_entry_id,
                linked_transit_route_anchor_fallback_counts,
                stat_band_root_0cfb_candidates: stat_band_root_0cfb_candidates
                    .iter()
                    .map(runtime_company_stat_band_candidate_from_save)
                    .collect(),
                stat_band_root_0d7f_candidates: stat_band_root_0d7f_candidates
                    .iter()
                    .map(runtime_company_stat_band_candidate_from_save)
                    .collect(),
                stat_band_root_1c47_candidates: stat_band_root_1c47_candidates
                    .iter()
                    .map(runtime_company_stat_band_candidate_from_save)
                    .collect(),
                year_stat_family_qword_bits,
                special_stat_family_232a_qword_bits,
                issue_opinion_terms_raw_i32,
                direct_control_transfer_float_fields_raw_u32,
                direct_control_transfer_int_fields_raw_u32,
            }),
        });
    }

    Some(SmpLoadedCompanyRoster {
        source_kind: "save-company-direct-records".to_string(),
        semantic_family: "scenario-save-company-direct-records".to_string(),
        observed_entry_count,
        selected_company_id: selection_probe.map(|probe| probe.selected_company_id),
        entries,
    })
}
