use std::collections::BTreeMap;

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::{build_save_dword_candidate, detect_save_company_record_start_offset};
use super::stat_bands::build_save_company_stat_band_candidates;
use crate::inspect::smp::common::{
    SmpSaveTaggedCollectionHeaderProbe, parse_nonzero_u32, read_ascii_c_string_at, read_f32_at,
    read_i32_at, read_u8_at, read_u32_at,
};
use crate::inspect::smp::world::*;
use crate::inspect::smp::*;

pub(in crate::inspect::smp) fn build_save_company_record_analysis_entries(
    bytes: &[u8],
    header_probe: Option<&SmpSaveTaggedCollectionHeaderProbe>,
) -> Option<Vec<SmpSaveCompanyRecordAnalysisEntry>> {
    let Some(header_probe) = header_probe else {
        return Some(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(header_probe.live_record_count as usize);
    for index in 0usize..header_probe.live_record_count as usize {
        let record_offset = base_offset.checked_add(index.checked_mul(record_stride)?)?;
        let company_id = read_u32_at(bytes, record_offset)?;
        let name = read_ascii_c_string_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_NAME_OFFSET,
            SAVE_COMPANY_RECORD_NAME_MAX_LEN,
        )?;
        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 company_value_scalar_f32 = read_f32_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_COMPANY_VALUE_OFFSET,
        )?;
        let cached_share_support_scalar_f32 = read_f32_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_SUPPORT_SCALAR_OFFSET,
        )?;
        let cached_share_price_f32 = read_f32_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 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_autoroute_site_score_cache_refresh_absolute_counter = read_u32_at(
            bytes,
            record_offset
                + SAVE_COMPANY_RECORD_LINKED_TRANSIT_AUTOROUTE_SITE_SCORE_CACHE_REFRESH_ABSOLUTE_COUNTER_OFFSET,
        )?;
        let linked_transit_site_peer_cache_refresh_absolute_counter = read_u32_at(
            bytes,
            record_offset
                + SAVE_COMPANY_RECORD_LINKED_TRANSIT_SITE_PEER_CACHE_REFRESH_ABSOLUTE_COUNTER_OFFSET,
        )?;
        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 = read_u32_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_MERGER_COOLDOWN_OFFSET,
        )?;
        let takeover_cooldown_year = read_u32_at(
            bytes,
            record_offset + SAVE_COMPANY_RECORD_TAKEOVER_COOLDOWN_OFFSET,
        )?;
        let scalar_dword_candidates = SAVE_COMPANY_RECORD_SCALAR_CANDIDATE_FIELDS
            .iter()
            .map(|(label, relative_offset)| {
                build_save_dword_candidate(bytes, record_offset, label, *relative_offset)
            })
            .collect::<Option<Vec<_>>>()?;
        let post_capacity_dword_candidates = SAVE_COMPANY_RECORD_POST_CAPACITY_CANDIDATE_FIELDS
            .iter()
            .map(|(label, relative_offset)| {
                build_save_dword_candidate(bytes, record_offset, label, *relative_offset)
            })
            .collect::<Option<Vec<_>>>()?;
        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,
        )?;
        entries.push(SmpSaveCompanyRecordAnalysisEntry {
            company_id,
            name,
            active,
            linked_chairman_profile_id,
            outstanding_shares,
            debt,
            bond_count,
            live_bond_slots,
            largest_live_bond_principal,
            highest_coupon_live_bond_principal,
            available_track_laying_capacity,
            company_value_scalar_f32,
            cached_share_support_scalar_f32,
            cached_share_price_f32,
            chairman_salary_baseline,
            chairman_salary_current,
            chairman_bonus_year,
            chairman_bonus_amount,
            founding_year,
            last_bankruptcy_year,
            last_dividend_year,
            preferred_locomotive_engine_type_raw_u8,
            preferred_locomotive_engine_type_raw_hex: format!(
                "0x{preferred_locomotive_engine_type_raw_u8:02x}"
            ),
            city_connection_latch,
            linked_transit_latch,
            linked_transit_autoroute_site_score_cache_refresh_absolute_counter,
            linked_transit_site_peer_cache_refresh_absolute_counter,
            linked_transit_route_anchor_entry_id,
            linked_transit_route_anchor_fallback_counts,
            merger_cooldown_year,
            takeover_cooldown_year,
            scalar_dword_candidates,
            post_capacity_dword_candidates,
            stat_band_root_0cfb_candidates,
            stat_band_root_0d7f_candidates,
            stat_band_root_1c47_candidates,
        });
    }
    Some(entries)
}

pub(in crate::inspect::smp) fn build_company_share_prices(
    company_entries: &[SmpSaveCompanyRecordAnalysisEntry],
) -> BTreeMap<u32, i64> {
    company_entries
        .iter()
        .filter_map(|entry| {
            round_f64_to_i64(entry.cached_share_price_f32 as f64)
                .map(|share_price| (entry.company_id, share_price))
        })
        .collect()
}
