use super::*;

#[test]
fn skips_company_periodic_route_preference_override_without_electric_preference() {
    let mut state = RuntimeState {
        calendar: CalendarPoint {
            year: 1845,
            month_slot: 0,
            phase_slot: 0,
            tick_slot: 0,
        },
        world_flags: BTreeMap::new(),
        save_profile: RuntimeSaveProfileState::default(),
        world_restore: RuntimeWorldRestoreState {
            auto_show_grade_during_track_lay_raw_u8: Some(3),
            ..RuntimeWorldRestoreState::default()
        },
        metadata: BTreeMap::new(),
        companies: vec![RuntimeCompany {
            company_id: 8,
            current_cash: 0,
            debt: 0,
            credit_rating_score: None,
            prime_rate: None,
            active: true,
            available_track_laying_capacity: None,
            controller_kind: RuntimeCompanyControllerKind::Unknown,
            linked_chairman_profile_id: None,
            book_value_per_share: 0,
            investor_confidence: 0,
            management_attitude: 0,
            takeover_cooldown_year: None,
            merger_cooldown_year: None,
            track_piece_counts: RuntimeTrackPieceCounts::default(),
        }],
        selected_company_id: Some(8),
        players: Vec::new(),
        selected_player_id: None,
        chairman_profiles: Vec::new(),
        selected_chairman_profile_id: None,
        trains: Vec::new(),
        locomotive_catalog: Vec::new(),
        cargo_catalog: Vec::new(),
        territories: Vec::new(),
        company_territory_track_piece_counts: Vec::new(),
        company_territory_access: Vec::new(),
        packed_event_collection: None,
        event_runtime_records: Vec::new(),
        candidate_availability: BTreeMap::new(),
        named_locomotive_availability: BTreeMap::new(),
        named_locomotive_cost: BTreeMap::new(),
        all_cargo_price_override: None,
        named_cargo_price_overrides: BTreeMap::new(),
        all_cargo_production_override: None,
        factory_cargo_production_override: None,
        farm_mine_cargo_production_override: None,
        named_cargo_production_overrides: BTreeMap::new(),
        cargo_production_overrides: BTreeMap::new(),
        world_runtime_variables: BTreeMap::new(),
        company_runtime_variables: BTreeMap::new(),
        player_runtime_variables: BTreeMap::new(),
        territory_runtime_variables: BTreeMap::new(),
        world_scalar_overrides: BTreeMap::new(),
        special_conditions: BTreeMap::new(),
        service_state: RuntimeServiceState {
            company_market_state: BTreeMap::from([(
                8,
                RuntimeCompanyMarketState {
                    city_connection_latch: true,
                    linked_transit_latch: false,
                    ..RuntimeCompanyMarketState::default()
                },
            )]),
            ..RuntimeServiceState::default()
        },
    };

    assert_eq!(
        runtime_apply_company_periodic_route_preference_override(&mut state, 8),
        None
    );
    assert_eq!(
        state.world_restore.auto_show_grade_during_track_lay_raw_u8,
        Some(3)
    );
    assert_eq!(
        state
            .service_state
            .active_periodic_route_preference_override,
        None
    );
    assert_eq!(
        state.service_state.last_periodic_route_preference_override,
        None
    );
}

#[test]
fn derives_annual_creditor_pressure_from_rehosted_finance_owner_state() {
    let mut year_stat_family_qword_bits = vec![
        0u64;
        (RUNTIME_COMPANY_STAT_SLOT_COUNT * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)
            as usize
    ];
    let write_current_value = |bits: &mut Vec<u64>, slot_id: u32, value: f64| {
        let index = (slot_id * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN) as usize;
        bits[index] = value.to_bits();
    };
    let write_prior_year_value =
        |bits: &mut Vec<u64>, slot_id: u32, year_delta: u32, value: f64| {
            let index = (slot_id * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN + year_delta) as usize;
            bits[index] = value.to_bits();
        };
    write_current_value(&mut year_stat_family_qword_bits, 0x09, -50_000.0);
    write_current_value(&mut year_stat_family_qword_bits, 0x0d, -700_000.0);
    write_current_value(&mut year_stat_family_qword_bits, 0x12, 0.0);
    write_prior_year_value(&mut year_stat_family_qword_bits, 0x01, 1, 100_000.0);
    write_prior_year_value(&mut year_stat_family_qword_bits, 0x01, 2, 90_000.0);
    write_prior_year_value(&mut year_stat_family_qword_bits, 0x01, 3, 80_000.0);
    write_prior_year_value(&mut year_stat_family_qword_bits, 0x09, 1, -115_000.0);
    write_prior_year_value(&mut year_stat_family_qword_bits, 0x09, 2, -110_000.0);
    write_prior_year_value(&mut year_stat_family_qword_bits, 0x09, 3, -110_000.0);

    let state = RuntimeState {
        calendar: CalendarPoint {
            year: 1845,
            month_slot: 0,
            phase_slot: 0,
            tick_slot: 0,
        },
        world_flags: BTreeMap::new(),
        save_profile: RuntimeSaveProfileState::default(),
        world_restore: RuntimeWorldRestoreState {
            packed_year_word_raw_u16: Some(1845),
            partial_year_progress_raw_u8: Some(0x0c),
            bankruptcy_policy_raw_u8: Some(0),
            bankruptcy_allowed: Some(true),
            ..RuntimeWorldRestoreState::default()
        },
        metadata: BTreeMap::new(),
        companies: vec![RuntimeCompany {
            company_id: 7,
            current_cash: 0,
            debt: 0,
            credit_rating_score: None,
            prime_rate: None,
            active: true,
            available_track_laying_capacity: None,
            controller_kind: RuntimeCompanyControllerKind::Unknown,
            linked_chairman_profile_id: None,
            book_value_per_share: 0,
            investor_confidence: 0,
            management_attitude: 0,
            takeover_cooldown_year: None,
            merger_cooldown_year: None,
            track_piece_counts: RuntimeTrackPieceCounts::default(),
        }],
        selected_company_id: None,
        players: Vec::new(),
        selected_player_id: None,
        chairman_profiles: Vec::new(),
        selected_chairman_profile_id: None,
        trains: Vec::new(),
        locomotive_catalog: Vec::new(),
        cargo_catalog: Vec::new(),
        territories: Vec::new(),
        company_territory_track_piece_counts: Vec::new(),
        company_territory_access: Vec::new(),
        packed_event_collection: None,
        event_runtime_records: Vec::new(),
        candidate_availability: BTreeMap::new(),
        named_locomotive_availability: BTreeMap::new(),
        named_locomotive_cost: BTreeMap::new(),
        all_cargo_price_override: None,
        named_cargo_price_overrides: BTreeMap::new(),
        all_cargo_production_override: None,
        factory_cargo_production_override: None,
        farm_mine_cargo_production_override: None,
        named_cargo_production_overrides: BTreeMap::new(),
        cargo_production_overrides: BTreeMap::new(),
        world_runtime_variables: BTreeMap::new(),
        company_runtime_variables: BTreeMap::new(),
        player_runtime_variables: BTreeMap::new(),
        territory_runtime_variables: BTreeMap::new(),
        world_scalar_overrides: BTreeMap::new(),
        special_conditions: BTreeMap::new(),
        service_state: RuntimeServiceState {
            company_market_state: BTreeMap::from([(
                7,
                RuntimeCompanyMarketState {
                    founding_year: 1841,
                    last_bankruptcy_year: 1832,
                    cached_share_price_raw_u32: 25.0f32.to_bits(),
                    year_stat_family_qword_bits,
                    ..RuntimeCompanyMarketState::default()
                },
            )]),
            ..RuntimeServiceState::default()
        },
    };

    assert_eq!(runtime_world_annual_finance_mode_active(&state), Some(true));
    assert_eq!(runtime_world_bankruptcy_allowed(&state), Some(true));
    let pressure_state =
        runtime_company_annual_creditor_pressure_state(&state, 7).expect("creditor pressure state");
    assert_eq!(pressure_state.recent_bad_net_profit_year_count, 3);
    assert_eq!(pressure_state.recent_peak_revenue, Some(100_000));
    assert_eq!(
        pressure_state.recent_three_year_net_profit_total,
        Some(-65_000)
    );
    assert_eq!(pressure_state.pressure_ladder_cash_floor, Some(-600_000));
    assert_eq!(
        pressure_state.current_cash_plus_slot_12_total,
        Some(-700_000)
    );
    assert_eq!(pressure_state.support_adjusted_share_price_floor, Some(20));
    assert_eq!(pressure_state.support_adjusted_share_price_scalar, Some(25));
    assert_eq!(pressure_state.current_fuel_cost, Some(-50_000));
    assert_eq!(pressure_state.current_fuel_cost_floor, Some(-48_000));
    assert!(pressure_state.eligible_for_bankruptcy_branch);
}

#[test]
fn derives_annual_deep_distress_bankruptcy_fallback_from_rehosted_finance_owner_state() {
    let mut year_stat_family_qword_bits = vec![
        0u64;
        ((RUNTIME_COMPANY_STAT_SLOT_COUNT + 2) * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)
            as usize
    ];
    let write_current_value = |bits: &mut Vec<u64>, slot_id: u32, value: f64| {
        let index = (slot_id * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN) as usize;
        bits[index] = value.to_bits();
    };
    let write_prior_year_value =
        |bits: &mut Vec<u64>, slot_id: u32, year_delta: u32, value: f64| {
            let index = (slot_id * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN + year_delta) as usize;
            bits[index] = value.to_bits();
        };
    write_current_value(&mut year_stat_family_qword_bits, 0x0d, -350_000.0);
    write_prior_year_value(&mut year_stat_family_qword_bits, 0x01, 1, 10_000.0);
    write_prior_year_value(&mut year_stat_family_qword_bits, 0x01, 2, 15_000.0);
    write_prior_year_value(&mut year_stat_family_qword_bits, 0x01, 3, 12_000.0);
    write_prior_year_value(&mut year_stat_family_qword_bits, 0x09, 1, -35_000.0);
    write_prior_year_value(&mut year_stat_family_qword_bits, 0x09, 2, -38_000.0);
    write_prior_year_value(&mut year_stat_family_qword_bits, 0x09, 3, -33_000.0);

    let state = RuntimeState {
        calendar: CalendarPoint {
            year: 1845,
            month_slot: 0,
            phase_slot: 0,
            tick_slot: 0,
        },
        world_flags: BTreeMap::new(),
        save_profile: RuntimeSaveProfileState::default(),
        world_restore: RuntimeWorldRestoreState {
            packed_year_word_raw_u16: Some(1845),
            bankruptcy_policy_raw_u8: Some(0),
            bankruptcy_allowed: Some(true),
            ..RuntimeWorldRestoreState::default()
        },
        metadata: BTreeMap::new(),
        companies: vec![RuntimeCompany {
            company_id: 9,
            current_cash: 0,
            debt: 0,
            credit_rating_score: None,
            prime_rate: None,
            active: true,
            available_track_laying_capacity: None,
            controller_kind: RuntimeCompanyControllerKind::Unknown,
            linked_chairman_profile_id: None,
            book_value_per_share: 0,
            investor_confidence: 0,
            management_attitude: 0,
            takeover_cooldown_year: None,
            merger_cooldown_year: None,
            track_piece_counts: RuntimeTrackPieceCounts::default(),
        }],
        selected_company_id: None,
        players: Vec::new(),
        selected_player_id: None,
        chairman_profiles: Vec::new(),
        selected_chairman_profile_id: None,
        trains: Vec::new(),
        locomotive_catalog: Vec::new(),
        cargo_catalog: Vec::new(),
        territories: Vec::new(),
        company_territory_track_piece_counts: Vec::new(),
        company_territory_access: Vec::new(),
        packed_event_collection: None,
        event_runtime_records: Vec::new(),
        candidate_availability: BTreeMap::new(),
        named_locomotive_availability: BTreeMap::new(),
        named_locomotive_cost: BTreeMap::new(),
        all_cargo_price_override: None,
        named_cargo_price_overrides: BTreeMap::new(),
        all_cargo_production_override: None,
        factory_cargo_production_override: None,
        farm_mine_cargo_production_override: None,
        named_cargo_production_overrides: BTreeMap::new(),
        cargo_production_overrides: BTreeMap::new(),
        world_runtime_variables: BTreeMap::new(),
        company_runtime_variables: BTreeMap::new(),
        player_runtime_variables: BTreeMap::new(),
        territory_runtime_variables: BTreeMap::new(),
        world_scalar_overrides: BTreeMap::new(),
        special_conditions: BTreeMap::new(),
        service_state: RuntimeServiceState {
            company_market_state: BTreeMap::from([(
                9,
                RuntimeCompanyMarketState {
                    founding_year: 1841,
                    last_bankruptcy_year: 1840,
                    year_stat_family_qword_bits,
                    ..RuntimeCompanyMarketState::default()
                },
            )]),
            ..RuntimeServiceState::default()
        },
    };

    let pressure_state =
        runtime_company_annual_deep_distress_state(&state, 9).expect("deep distress state");
    assert_eq!(pressure_state.current_cash, Some(-350_000));
    assert_eq!(
        pressure_state.recent_first_three_net_profit_years,
        vec![-25_000, -23_000, -21_000]
    );
    assert_eq!(pressure_state.deep_distress_cash_floor, Some(-300_000));
    assert_eq!(pressure_state.deep_distress_net_profit_floor, Some(-20_000));
    assert!(pressure_state.eligible_for_bankruptcy_fallback);
}

#[test]
fn derives_annual_stock_repurchase_state_from_rehosted_owner_state() {
    let mut year_stat_family_qword_bits = vec![
        0u64;
        ((RUNTIME_COMPANY_STAT_SLOT_COUNT + 2) * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)
            as usize
    ];
    let write_current_value = |bits: &mut Vec<u64>, slot_id: u32, value: f64| {
        let index = (slot_id * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN) as usize;
        bits[index] = value.to_bits();
    };
    write_current_value(&mut year_stat_family_qword_bits, 0x0d, 1_600_000.0);

    let state = RuntimeState {
        calendar: CalendarPoint {
            year: 1845,
            month_slot: 0,
            phase_slot: 0,
            tick_slot: 0,
        },
        world_flags: BTreeMap::new(),
        save_profile: RuntimeSaveProfileState::default(),
        world_restore: RuntimeWorldRestoreState {
            partial_year_progress_raw_u8: Some(0x0c),
            stock_issue_and_buyback_policy_raw_u8: Some(0),
            stock_issue_and_buyback_allowed: Some(true),
            building_density_growth_setting_raw_u32: Some(1),
            ..RuntimeWorldRestoreState::default()
        },
        metadata: BTreeMap::new(),
        companies: vec![RuntimeCompany {
            company_id: 12,
            current_cash: 0,
            debt: 0,
            credit_rating_score: None,
            prime_rate: None,
            active: true,
            available_track_laying_capacity: None,
            controller_kind: RuntimeCompanyControllerKind::Unknown,
            linked_chairman_profile_id: Some(3),
            book_value_per_share: 0,
            investor_confidence: 0,
            management_attitude: 0,
            takeover_cooldown_year: None,
            merger_cooldown_year: None,
            track_piece_counts: RuntimeTrackPieceCounts::default(),
        }],
        selected_company_id: None,
        players: Vec::new(),
        selected_player_id: None,
        chairman_profiles: vec![RuntimeChairmanProfile {
            profile_id: 3,
            name: "Jay".to_string(),
            active: true,
            current_cash: 200,
            linked_company_id: Some(12),
            company_holdings: BTreeMap::from([(12, 14_500)]),
            holdings_value_total: 0,
            net_worth_total: 0,
            purchasing_power_total: 0,
        }],
        selected_chairman_profile_id: None,
        trains: Vec::new(),
        locomotive_catalog: Vec::new(),
        cargo_catalog: Vec::new(),
        territories: Vec::new(),
        company_territory_track_piece_counts: Vec::new(),
        company_territory_access: Vec::new(),
        packed_event_collection: None,
        event_runtime_records: Vec::new(),
        candidate_availability: BTreeMap::new(),
        named_locomotive_availability: BTreeMap::new(),
        named_locomotive_cost: BTreeMap::new(),
        all_cargo_price_override: None,
        named_cargo_price_overrides: BTreeMap::new(),
        all_cargo_production_override: None,
        factory_cargo_production_override: None,
        farm_mine_cargo_production_override: None,
        named_cargo_production_overrides: BTreeMap::new(),
        cargo_production_overrides: BTreeMap::new(),
        world_runtime_variables: BTreeMap::new(),
        company_runtime_variables: BTreeMap::new(),
        player_runtime_variables: BTreeMap::new(),
        territory_runtime_variables: BTreeMap::new(),
        world_scalar_overrides: BTreeMap::new(),
        special_conditions: BTreeMap::new(),
        service_state: RuntimeServiceState {
            company_market_state: BTreeMap::from([(
                12,
                RuntimeCompanyMarketState {
                    outstanding_shares: 20_000,
                    cached_share_price_raw_u32: 20.0f32.to_bits(),
                    founding_year: 1835,
                    city_connection_latch: true,
                    year_stat_family_qword_bits,
                    ..RuntimeCompanyMarketState::default()
                },
            )]),
            chairman_personality_raw_u8: BTreeMap::from([(3, 20)]),
            ..RuntimeServiceState::default()
        },
    };

    let repurchase_state =
        runtime_company_annual_stock_repurchase_state(&state, 12).expect("stock repurchase state");
    assert_eq!(repurchase_state.building_density_growth_setting, Some(1));
    assert_eq!(
        repurchase_state.linked_chairman_personality_raw_u8,
        Some(20)
    );
    assert_eq!(repurchase_state.repurchase_batch_size, Some(1_000));
    assert_eq!(repurchase_state.repurchase_factor_basis_points, Some(432));
    assert_eq!(repurchase_state.current_cash, Some(1_600_000));
    assert_eq!(
        repurchase_state.stock_value_gate_cash_floor,
        Some(3_456_000)
    );
    assert_eq!(
        repurchase_state.support_adjusted_share_price_scalar,
        Some(20)
    );
    assert_eq!(repurchase_state.affordability_cash_floor, Some(103_680));
    assert_eq!(repurchase_state.unassigned_share_pool, Some(5_500));
    assert!(!repurchase_state.eligible_for_single_batch_repurchase);
}

#[test]
fn derives_annual_bond_policy_state_from_rehosted_owner_state() {
    let mut year_stat_family_qword_bits = vec![
        0u64;
        ((RUNTIME_COMPANY_STAT_SLOT_COUNT + 2) * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)
            as usize
    ];
    let write_current_value = |bits: &mut Vec<u64>, slot_id: u32, value: f64| {
        let index = (slot_id * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN) as usize;
        bits[index] = value.to_bits();
    };
    write_current_value(&mut year_stat_family_qword_bits, 0x0d, -400_000.0);

    let state = RuntimeState {
        calendar: CalendarPoint {
            year: 1845,
            month_slot: 0,
            phase_slot: 0,
            tick_slot: 0,
        },
        world_flags: BTreeMap::new(),
        save_profile: RuntimeSaveProfileState::default(),
        world_restore: RuntimeWorldRestoreState {
            partial_year_progress_raw_u8: Some(0x0c),
            bond_issue_and_repayment_policy_raw_u8: Some(0),
            bond_issue_and_repayment_allowed: Some(true),
            ..RuntimeWorldRestoreState::default()
        },
        metadata: BTreeMap::new(),
        companies: vec![RuntimeCompany {
            company_id: 11,
            current_cash: 0,
            debt: 0,
            credit_rating_score: None,
            prime_rate: None,
            active: true,
            available_track_laying_capacity: None,
            controller_kind: RuntimeCompanyControllerKind::Unknown,
            linked_chairman_profile_id: None,
            book_value_per_share: 0,
            investor_confidence: 0,
            management_attitude: 0,
            takeover_cooldown_year: None,
            merger_cooldown_year: None,
            track_piece_counts: RuntimeTrackPieceCounts::default(),
        }],
        selected_company_id: None,
        players: Vec::new(),
        selected_player_id: None,
        chairman_profiles: Vec::new(),
        selected_chairman_profile_id: None,
        trains: Vec::new(),
        locomotive_catalog: Vec::new(),
        cargo_catalog: Vec::new(),
        territories: Vec::new(),
        company_territory_track_piece_counts: Vec::new(),
        company_territory_access: Vec::new(),
        packed_event_collection: None,
        event_runtime_records: Vec::new(),
        candidate_availability: BTreeMap::new(),
        named_locomotive_availability: BTreeMap::new(),
        named_locomotive_cost: BTreeMap::new(),
        all_cargo_price_override: None,
        named_cargo_price_overrides: BTreeMap::new(),
        all_cargo_production_override: None,
        factory_cargo_production_override: None,
        farm_mine_cargo_production_override: None,
        named_cargo_production_overrides: BTreeMap::new(),
        cargo_production_overrides: BTreeMap::new(),
        world_runtime_variables: BTreeMap::new(),
        company_runtime_variables: BTreeMap::new(),
        player_runtime_variables: BTreeMap::new(),
        territory_runtime_variables: BTreeMap::new(),
        world_scalar_overrides: BTreeMap::new(),
        special_conditions: BTreeMap::new(),
        service_state: RuntimeServiceState {
            company_market_state: BTreeMap::from([(
                11,
                RuntimeCompanyMarketState {
                    bond_count: 2,
                    linked_transit_latch: true,
                    live_bond_slots: vec![
                        RuntimeCompanyBondSlot {
                            slot_index: 0,
                            principal: 200_000,
                            maturity_year: 0,
                            coupon_rate_raw_u32: 0.09f32.to_bits(),
                        },
                        RuntimeCompanyBondSlot {
                            slot_index: 1,
                            principal: 150_000,
                            maturity_year: 0,
                            coupon_rate_raw_u32: 0.08f32.to_bits(),
                        },
                    ],
                    year_stat_family_qword_bits,
                    ..RuntimeCompanyMarketState::default()
                },
            )]),
            ..RuntimeServiceState::default()
        },
    };

    let bond_state =
        runtime_company_annual_bond_policy_state(&state, 11).expect("bond policy state");
    assert_eq!(bond_state.live_bond_count, Some(2));
    assert_eq!(bond_state.live_bond_principal_total, Some(350_000));
    assert_eq!(bond_state.matured_live_bond_count, Some(0));
    assert_eq!(bond_state.matured_live_bond_principal_total, Some(0));
    assert_eq!(bond_state.next_live_bond_maturity_year, None);
    assert_eq!(bond_state.live_bond_coupon_burden_total, Some(30_000));
    assert_eq!(bond_state.current_cash, Some(-400_000));
    assert_eq!(bond_state.cash_after_full_repayment, Some(-750_000));
    assert_eq!(bond_state.issue_cash_floor, Some(-30_000));
    assert_eq!(bond_state.issue_principal_step, Some(500_000));
    assert_eq!(bond_state.proposed_issue_bond_count, Some(2));
    assert_eq!(bond_state.proposed_issue_total_principal, Some(1_000_000));
    assert_eq!(bond_state.proposed_issue_years_to_maturity, Some(30));
    assert!(bond_state.eligible_for_bond_issue_branch);
}

#[test]
fn classifies_annual_bond_principal_flow_relation() {
    assert_eq!(
        runtime_annual_bond_principal_flow_relation_label(0, 0),
        None
    );
    assert_eq!(
        runtime_annual_bond_principal_flow_relation_label(350_000, 0),
        Some("retired_only")
    );
    assert_eq!(
        runtime_annual_bond_principal_flow_relation_label(0, 500_000),
        Some("issued_only")
    );
    assert_eq!(
        runtime_annual_bond_principal_flow_relation_label(350_000, 1_000_000),
        Some("issued_exceeds_retired")
    );
    assert_eq!(
        runtime_annual_bond_principal_flow_relation_label(500_000, 500_000),
        Some("retired_equals_issued")
    );
}

#[test]
fn maps_annual_bond_news_selector_from_principal_flow_relation() {
    assert_eq!(
        runtime_annual_finance_news_family_candidate_label(
            RuntimeCompanyAnnualFinancePolicyAction::BondIssue,
            500_000,
            500_000,
            0,
            0,
        ),
        Some("2882")
    );
    assert_eq!(
        runtime_annual_finance_news_family_candidate_label(
            RuntimeCompanyAnnualFinancePolicyAction::BondIssue,
            350_000,
            1_000_000,
            0,
            0,
        ),
        Some("2883")
    );
    assert_eq!(
        runtime_annual_finance_news_family_candidate_label(
            RuntimeCompanyAnnualFinancePolicyAction::BondIssue,
            900_000,
            500_000,
            0,
            0,
        ),
        Some("2884")
    );
    assert_eq!(
        runtime_annual_finance_news_family_candidate_label(
            RuntimeCompanyAnnualFinancePolicyAction::BondIssue,
            350_000,
            0,
            0,
            0,
        ),
        Some("2885")
    );
    assert_eq!(
        runtime_annual_finance_news_family_candidate_label(
            RuntimeCompanyAnnualFinancePolicyAction::BondIssue,
            0,
            500_000,
            0,
            0,
        ),
        Some("2886")
    );
}

#[test]
fn annual_bond_policy_stays_eligible_for_repayment_without_new_issue() {
    let mut year_stat_family_qword_bits = vec![
        0u64;
        ((RUNTIME_COMPANY_STAT_SLOT_COUNT + 2) * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)
            as usize
    ];
    let write_current_value = |bits: &mut Vec<u64>, slot_id: u32, value: f64| {
        let index = (slot_id * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN) as usize;
        bits[index] = value.to_bits();
    };
    write_current_value(&mut year_stat_family_qword_bits, 0x0d, 900_000.0);

    let state = RuntimeState {
        calendar: CalendarPoint {
            year: 1845,
            month_slot: 0,
            phase_slot: 0,
            tick_slot: 0,
        },
        world_flags: BTreeMap::new(),
        save_profile: RuntimeSaveProfileState::default(),
        world_restore: RuntimeWorldRestoreState {
            partial_year_progress_raw_u8: Some(0x0c),
            bond_issue_and_repayment_policy_raw_u8: Some(0),
            bond_issue_and_repayment_allowed: Some(true),
            ..RuntimeWorldRestoreState::default()
        },
        metadata: BTreeMap::new(),
        companies: vec![RuntimeCompany {
            company_id: 12,
            current_cash: 0,
            debt: 0,
            credit_rating_score: None,
            prime_rate: None,
            active: true,
            available_track_laying_capacity: None,
            controller_kind: RuntimeCompanyControllerKind::Unknown,
            linked_chairman_profile_id: None,
            book_value_per_share: 0,
            investor_confidence: 0,
            management_attitude: 0,
            takeover_cooldown_year: None,
            merger_cooldown_year: None,
            track_piece_counts: RuntimeTrackPieceCounts::default(),
        }],
        selected_company_id: None,
        players: Vec::new(),
        selected_player_id: None,
        chairman_profiles: Vec::new(),
        selected_chairman_profile_id: None,
        trains: Vec::new(),
        locomotive_catalog: Vec::new(),
        cargo_catalog: Vec::new(),
        territories: Vec::new(),
        company_territory_track_piece_counts: Vec::new(),
        company_territory_access: Vec::new(),
        packed_event_collection: None,
        event_runtime_records: Vec::new(),
        candidate_availability: BTreeMap::new(),
        named_locomotive_availability: BTreeMap::new(),
        named_locomotive_cost: BTreeMap::new(),
        all_cargo_price_override: None,
        named_cargo_price_overrides: BTreeMap::new(),
        all_cargo_production_override: None,
        factory_cargo_production_override: None,
        farm_mine_cargo_production_override: None,
        named_cargo_production_overrides: BTreeMap::new(),
        cargo_production_overrides: BTreeMap::new(),
        world_runtime_variables: BTreeMap::new(),
        company_runtime_variables: BTreeMap::new(),
        player_runtime_variables: BTreeMap::new(),
        territory_runtime_variables: BTreeMap::new(),
        world_scalar_overrides: BTreeMap::new(),
        special_conditions: BTreeMap::new(),
        service_state: RuntimeServiceState {
            company_market_state: BTreeMap::from([(
                12,
                RuntimeCompanyMarketState {
                    bond_count: 2,
                    live_bond_slots: vec![
                        RuntimeCompanyBondSlot {
                            slot_index: 0,
                            principal: 200_000,
                            maturity_year: 1845,
                            coupon_rate_raw_u32: 0.09f32.to_bits(),
                        },
                        RuntimeCompanyBondSlot {
                            slot_index: 1,
                            principal: 150_000,
                            maturity_year: 1847,
                            coupon_rate_raw_u32: 0.08f32.to_bits(),
                        },
                    ],
                    year_stat_family_qword_bits,
                    ..RuntimeCompanyMarketState::default()
                },
            )]),
            ..RuntimeServiceState::default()
        },
    };

    let bond_state =
        runtime_company_annual_bond_policy_state(&state, 12).expect("bond policy state");
    assert_eq!(bond_state.live_bond_principal_total, Some(350_000));
    assert_eq!(bond_state.cash_after_full_repayment, Some(550_000));
    assert_eq!(bond_state.proposed_issue_bond_count, Some(0));
    assert!(bond_state.eligible_for_bond_issue_branch);
}

#[test]
fn derives_annual_stock_issue_state_from_rehosted_owner_state() {
    let mut year_stat_family_qword_bits = vec![
        0u64;
        ((RUNTIME_COMPANY_STAT_SLOT_COUNT + 2) * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)
            as usize
    ];
    let write_current_value = |bits: &mut Vec<u64>, slot_id: u32, value: f64| {
        let index = (slot_id * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN) as usize;
        bits[index] = value.to_bits();
    };
    write_current_value(&mut year_stat_family_qword_bits, 0x0d, 250_000.0);

    let state = RuntimeState {
        calendar: CalendarPoint {
            year: 1845,
            month_slot: 0,
            phase_slot: 0,
            tick_slot: 0,
        },
        world_flags: BTreeMap::new(),
        save_profile: RuntimeSaveProfileState::default(),
        world_restore: RuntimeWorldRestoreState {
            partial_year_progress_raw_u8: Some(0x0c),
            stock_issue_and_buyback_policy_raw_u8: Some(0),
            stock_issue_and_buyback_allowed: Some(true),
            bond_issue_and_repayment_policy_raw_u8: Some(0),
            bond_issue_and_repayment_allowed: Some(true),
            issue_37_value: Some(2),
            issue_37_multiplier_raw_u32: Some(1.0f32.to_bits()),
            issue_37_multiplier_value_f32_text: Some("1.000000".to_string()),
            absolute_counter_raw_u32: Some(885_911_040),
            ..RuntimeWorldRestoreState::default()
        },
        metadata: BTreeMap::new(),
        companies: vec![RuntimeCompany {
            company_id: 14,
            current_cash: 0,
            debt: 0,
            credit_rating_score: None,
            prime_rate: None,
            active: true,
            available_track_laying_capacity: None,
            controller_kind: RuntimeCompanyControllerKind::Unknown,
            linked_chairman_profile_id: Some(8),
            book_value_per_share: 0,
            investor_confidence: 0,
            management_attitude: 0,
            takeover_cooldown_year: None,
            merger_cooldown_year: None,
            track_piece_counts: RuntimeTrackPieceCounts::default(),
        }],
        selected_company_id: None,
        players: Vec::new(),
        selected_player_id: None,
        chairman_profiles: vec![RuntimeChairmanProfile {
            profile_id: 8,
            name: "Taylor".to_string(),
            active: true,
            current_cash: 200,
            linked_company_id: Some(14),
            company_holdings: BTreeMap::from([(14, 14_000)]),
            holdings_value_total: 0,
            net_worth_total: 0,
            purchasing_power_total: 0,
        }],
        selected_chairman_profile_id: None,
        trains: Vec::new(),
        locomotive_catalog: Vec::new(),
        cargo_catalog: Vec::new(),
        territories: Vec::new(),
        company_territory_track_piece_counts: Vec::new(),
        company_territory_access: Vec::new(),
        packed_event_collection: None,
        event_runtime_records: Vec::new(),
        candidate_availability: BTreeMap::new(),
        named_locomotive_availability: BTreeMap::new(),
        named_locomotive_cost: BTreeMap::new(),
        all_cargo_price_override: None,
        named_cargo_price_overrides: BTreeMap::new(),
        all_cargo_production_override: None,
        factory_cargo_production_override: None,
        farm_mine_cargo_production_override: None,
        named_cargo_production_overrides: BTreeMap::new(),
        cargo_production_overrides: BTreeMap::new(),
        world_runtime_variables: BTreeMap::new(),
        company_runtime_variables: BTreeMap::new(),
        player_runtime_variables: BTreeMap::new(),
        territory_runtime_variables: BTreeMap::new(),
        world_scalar_overrides: BTreeMap::new(),
        special_conditions: BTreeMap::new(),
        service_state: RuntimeServiceState {
            world_issue_opinion_base_terms_raw_i32: vec![0; 0x3b],
            chairman_personality_raw_u8: BTreeMap::from([(8, 20)]),
            company_market_state: BTreeMap::from([(
                14,
                RuntimeCompanyMarketState {
                    outstanding_shares: 20_000,
                    bond_count: 2,
                    highest_coupon_live_bond_principal: Some(300_000),
                    current_issue_calendar_word: 0x0101_0725,
                    current_issue_calendar_word_2: 0x0001_0001,
                    founding_year: 1840,
                    cached_share_price_raw_u32: 35.0f32.to_bits(),
                    recent_per_share_cache_absolute_counter: 885_911_040,
                    recent_per_share_cached_value_bits: 34.0f64.to_bits(),
                    city_connection_latch: false,
                    live_bond_slots: vec![
                        RuntimeCompanyBondSlot {
                            slot_index: 0,
                            principal: 300_000,
                            maturity_year: 0,
                            coupon_rate_raw_u32: 0.11f32.to_bits(),
                        },
                        RuntimeCompanyBondSlot {
                            slot_index: 1,
                            principal: 200_000,
                            maturity_year: 0,
                            coupon_rate_raw_u32: 0.07f32.to_bits(),
                        },
                    ],
                    direct_control_transfer_float_fields_raw_u32: BTreeMap::from([(
                        0x32f,
                        30.0f32.to_bits(),
                    )]),
                    year_stat_family_qword_bits,
                    ..RuntimeCompanyMarketState::default()
                },
            )]),
            ..RuntimeServiceState::default()
        },
    };

    let stock_issue_state =
        runtime_company_annual_stock_issue_state(&state, 14).expect("stock issue state");
    assert_eq!(stock_issue_state.live_bond_count, Some(2));
    assert_eq!(stock_issue_state.initial_issue_batch_size, Some(2_000));
    assert_eq!(stock_issue_state.trimmed_issue_batch_size, Some(2_000));
    assert_eq!(stock_issue_state.share_pressure_basis_points, Some(-1_000));
    assert_eq!(
        stock_issue_state.pressured_support_adjusted_share_price_scalar,
        Some(35)
    );
    assert_eq!(stock_issue_state.pressured_proceeds, Some(70_000));
    assert_eq!(
        stock_issue_state.book_value_per_share_floor_applied,
        Some(30)
    );
    assert_eq!(
        stock_issue_state.price_to_book_ratio_basis_points,
        Some(11_667)
    );
    assert_eq!(
        stock_issue_state.highest_coupon_live_bond_rate_basis_points,
        Some(1_100)
    );
    assert_eq!(
        stock_issue_state.minimum_price_to_book_ratio_basis_points,
        Some(8_000)
    );
    assert_eq!(stock_issue_state.current_cash, Some(250_000));
    assert_eq!(
        stock_issue_state.highest_coupon_live_bond_principal,
        Some(300_000)
    );
    assert_eq!(
        stock_issue_state.current_issue_age_absolute_counter_delta,
        Some(967_680)
    );
    assert_eq!(
        stock_issue_state.current_issue_cooldown_floor,
        Some(483_840)
    );
    assert_eq!(stock_issue_state.passes_share_price_floor, Some(true));
    assert_eq!(stock_issue_state.passes_proceeds_floor, Some(true));
    assert_eq!(stock_issue_state.passes_cash_gate, Some(true));
    assert_eq!(stock_issue_state.passes_issue_cooldown_gate, Some(true));
    assert_eq!(
        stock_issue_state.passes_coupon_price_to_book_gate,
        Some(true)
    );
    assert!(stock_issue_state.eligible_for_double_tranche_issue);
}

#[test]
fn derives_annual_dividend_policy_state_from_rehosted_owner_state() {
    let mut year_stat_family_qword_bits = vec![
        0u64;
        ((RUNTIME_COMPANY_STAT_SLOT_COUNT + 2) * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)
            as usize
    ];
    let write_current_value = |bits: &mut Vec<u64>, slot_id: u32, value: f64| {
        let index = (slot_id * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN) as usize;
        bits[index] = value.to_bits();
    };
    let write_prior_year_value =
        |bits: &mut Vec<u64>, slot_id: u32, year_delta: u32, value: f64| {
            let index = (slot_id * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN + year_delta) as usize;
            bits[index] = value.to_bits();
        };
    write_current_value(&mut year_stat_family_qword_bits, 0x0d, 300_000.0);
    write_current_value(&mut year_stat_family_qword_bits, 0x01, 300_000.0);
    write_current_value(&mut year_stat_family_qword_bits, 0x09, -180_000.0);
    write_prior_year_value(&mut year_stat_family_qword_bits, 0x01, 1, 280_000.0);
    write_prior_year_value(&mut year_stat_family_qword_bits, 0x09, 1, -190_000.0);
    write_prior_year_value(&mut year_stat_family_qword_bits, 0x01, 2, 260_000.0);
    write_prior_year_value(&mut year_stat_family_qword_bits, 0x09, 2, -200_000.0);
    write_prior_year_value(&mut year_stat_family_qword_bits, 0x1c, 1, 5.0);

    let state = RuntimeState {
        calendar: CalendarPoint {
            year: 1845,
            month_slot: 0,
            phase_slot: 0,
            tick_slot: 0,
        },
        world_flags: BTreeMap::new(),
        save_profile: RuntimeSaveProfileState::default(),
        world_restore: RuntimeWorldRestoreState {
            packed_year_word_raw_u16: Some(1845),
            partial_year_progress_raw_u8: Some(0x0c),
            dividend_policy_raw_u8: Some(0),
            dividend_adjustment_allowed: Some(true),
            stock_issue_and_buyback_policy_raw_u8: Some(0),
            stock_issue_and_buyback_allowed: Some(true),
            bond_issue_and_repayment_policy_raw_u8: Some(0),
            bond_issue_and_repayment_allowed: Some(true),
            bankruptcy_policy_raw_u8: Some(0),
            bankruptcy_allowed: Some(true),
            building_density_growth_setting_raw_u32: Some(1),
            ..RuntimeWorldRestoreState::default()
        },
        metadata: BTreeMap::new(),
        companies: vec![RuntimeCompany {
            company_id: 15,
            current_cash: 0,
            debt: 0,
            credit_rating_score: None,
            prime_rate: None,
            active: true,
            available_track_laying_capacity: None,
            controller_kind: RuntimeCompanyControllerKind::Unknown,
            linked_chairman_profile_id: None,
            book_value_per_share: 0,
            investor_confidence: 0,
            management_attitude: 0,
            takeover_cooldown_year: None,
            merger_cooldown_year: None,
            track_piece_counts: RuntimeTrackPieceCounts::default(),
        }],
        selected_company_id: None,
        players: Vec::new(),
        selected_player_id: None,
        chairman_profiles: vec![RuntimeChairmanProfile {
            profile_id: 3,
            name: "Chairman Three".to_string(),
            active: true,
            current_cash: 0,
            linked_company_id: Some(15),
            company_holdings: BTreeMap::from([(15, 9_500)]),
            holdings_value_total: 0,
            net_worth_total: 0,
            purchasing_power_total: 0,
        }],
        selected_chairman_profile_id: None,
        trains: Vec::new(),
        locomotive_catalog: Vec::new(),
        cargo_catalog: Vec::new(),
        territories: Vec::new(),
        company_territory_track_piece_counts: Vec::new(),
        company_territory_access: Vec::new(),
        packed_event_collection: None,
        event_runtime_records: Vec::new(),
        candidate_availability: BTreeMap::new(),
        named_locomotive_availability: BTreeMap::new(),
        named_locomotive_cost: BTreeMap::new(),
        all_cargo_price_override: None,
        named_cargo_price_overrides: BTreeMap::new(),
        all_cargo_production_override: None,
        factory_cargo_production_override: None,
        farm_mine_cargo_production_override: None,
        named_cargo_production_overrides: BTreeMap::new(),
        cargo_production_overrides: BTreeMap::new(),
        world_runtime_variables: BTreeMap::new(),
        company_runtime_variables: BTreeMap::new(),
        player_runtime_variables: BTreeMap::new(),
        territory_runtime_variables: BTreeMap::new(),
        world_scalar_overrides: BTreeMap::new(),
        special_conditions: BTreeMap::new(),
        service_state: RuntimeServiceState {
            company_market_state: BTreeMap::from([(
                15,
                RuntimeCompanyMarketState {
                    outstanding_shares: 10_000,
                    founding_year: 1840,
                    last_dividend_year: 1844,
                    year_stat_family_qword_bits,
                    direct_control_transfer_float_fields_raw_u32: BTreeMap::from([(
                        0x33f,
                        0.4f32.to_bits(),
                    )]),
                    ..RuntimeCompanyMarketState::default()
                },
            )]),
            ..RuntimeServiceState::default()
        },
    };

    let dividend_state = runtime_company_annual_dividend_policy_state(&state, 15)
        .expect("annual dividend policy state");
    assert_eq!(dividend_state.years_since_last_dividend, Some(1));
    assert_eq!(dividend_state.years_since_founding, Some(5));
    assert_eq!(dividend_state.outstanding_shares, Some(10_000));
    assert_eq!(dividend_state.unassigned_share_pool, Some(500));
    assert_eq!(
        dividend_state.weighted_recent_net_profit_total,
        Some(600_000)
    );
    assert_eq!(
        dividend_state.weighted_recent_net_profit_average,
        Some(100_000)
    );
    assert_eq!(dividend_state.current_cash, Some(300_000));
    assert!(dividend_state.tiny_unassigned_share_cash_supplement_branch);
    assert_eq!(
        dividend_state.tentative_target_dividend_per_share_tenths,
        Some(133)
    );
    assert_eq!(dividend_state.current_dividend_per_share_tenths, Some(4));
    assert_eq!(
        dividend_state.growth_adjusted_current_dividend_per_share_tenths,
        Some(3)
    );
    assert_eq!(
        dividend_state.board_approved_dividend_rate_ceiling_tenths,
        Some(18)
    );
    assert_eq!(dividend_state.proposed_dividend_per_share_tenths, Some(18));
    assert!(dividend_state.eligible_for_dividend_adjustment_branch);
}

#[test]
fn derives_annual_finance_policy_action_from_branch_priority_order() {
    let mut year_stat_family_qword_bits = vec![
        0u64;
        ((RUNTIME_COMPANY_STAT_SLOT_COUNT + 2) * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)
            as usize
    ];
    let write_current_value = |bits: &mut Vec<u64>, slot_id: u32, value: f64| {
        let index = (slot_id * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN) as usize;
        bits[index] = value.to_bits();
    };
    let write_prior_year_value =
        |bits: &mut Vec<u64>, slot_id: u32, year_delta: u32, value: f64| {
            let index = (slot_id * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN + year_delta) as usize;
            bits[index] = value.to_bits();
        };
    write_current_value(&mut year_stat_family_qword_bits, 0x0d, 300_000.0);
    write_current_value(&mut year_stat_family_qword_bits, 0x01, 300_000.0);
    write_current_value(&mut year_stat_family_qword_bits, 0x09, -180_000.0);
    write_prior_year_value(&mut year_stat_family_qword_bits, 0x01, 1, 280_000.0);
    write_prior_year_value(&mut year_stat_family_qword_bits, 0x09, 1, -190_000.0);
    write_prior_year_value(&mut year_stat_family_qword_bits, 0x01, 2, 260_000.0);
    write_prior_year_value(&mut year_stat_family_qword_bits, 0x09, 2, -200_000.0);
    write_prior_year_value(&mut year_stat_family_qword_bits, 0x1c, 1, 5.0);

    let state = RuntimeState {
        calendar: CalendarPoint {
            year: 1845,
            month_slot: 0,
            phase_slot: 0,
            tick_slot: 0,
        },
        world_flags: BTreeMap::new(),
        save_profile: RuntimeSaveProfileState::default(),
        world_restore: RuntimeWorldRestoreState {
            packed_year_word_raw_u16: Some(1845),
            partial_year_progress_raw_u8: Some(0x0c),
            dividend_policy_raw_u8: Some(0),
            dividend_adjustment_allowed: Some(true),
            stock_issue_and_buyback_policy_raw_u8: Some(0),
            stock_issue_and_buyback_allowed: Some(true),
            bond_issue_and_repayment_policy_raw_u8: Some(0),
            bond_issue_and_repayment_allowed: Some(true),
            bankruptcy_policy_raw_u8: Some(0),
            bankruptcy_allowed: Some(true),
            building_density_growth_setting_raw_u32: Some(1),
            ..RuntimeWorldRestoreState::default()
        },
        metadata: BTreeMap::new(),
        companies: vec![RuntimeCompany {
            company_id: 16,
            current_cash: 0,
            debt: 0,
            credit_rating_score: None,
            prime_rate: None,
            active: true,
            available_track_laying_capacity: None,
            controller_kind: RuntimeCompanyControllerKind::Unknown,
            linked_chairman_profile_id: Some(4),
            book_value_per_share: 0,
            investor_confidence: 0,
            management_attitude: 0,
            takeover_cooldown_year: None,
            merger_cooldown_year: None,
            track_piece_counts: RuntimeTrackPieceCounts::default(),
        }],
        selected_company_id: Some(16),
        players: Vec::new(),
        selected_player_id: None,
        chairman_profiles: vec![RuntimeChairmanProfile {
            profile_id: 4,
            name: "Chairman Four".to_string(),
            active: true,
            current_cash: 0,
            linked_company_id: Some(16),
            company_holdings: BTreeMap::from([(16, 9_500)]),
            holdings_value_total: 0,
            net_worth_total: 0,
            purchasing_power_total: 0,
        }],
        selected_chairman_profile_id: None,
        trains: Vec::new(),
        locomotive_catalog: Vec::new(),
        cargo_catalog: Vec::new(),
        territories: Vec::new(),
        company_territory_track_piece_counts: Vec::new(),
        company_territory_access: Vec::new(),
        packed_event_collection: None,
        event_runtime_records: Vec::new(),
        candidate_availability: BTreeMap::new(),
        named_locomotive_availability: BTreeMap::new(),
        named_locomotive_cost: BTreeMap::new(),
        all_cargo_price_override: None,
        named_cargo_price_overrides: BTreeMap::new(),
        all_cargo_production_override: None,
        factory_cargo_production_override: None,
        farm_mine_cargo_production_override: None,
        named_cargo_production_overrides: BTreeMap::new(),
        cargo_production_overrides: BTreeMap::new(),
        world_runtime_variables: BTreeMap::new(),
        company_runtime_variables: BTreeMap::new(),
        player_runtime_variables: BTreeMap::new(),
        territory_runtime_variables: BTreeMap::new(),
        world_scalar_overrides: BTreeMap::new(),
        special_conditions: BTreeMap::new(),
        service_state: RuntimeServiceState {
            chairman_personality_raw_u8: BTreeMap::from([(4, 20)]),
            company_market_state: BTreeMap::from([(
                16,
                RuntimeCompanyMarketState {
                    outstanding_shares: 10_000,
                    founding_year: 1840,
                    last_dividend_year: 1844,
                    city_connection_latch: true,
                    year_stat_family_qword_bits,
                    direct_control_transfer_float_fields_raw_u32: BTreeMap::from([(
                        0x33f,
                        0.4f32.to_bits(),
                    )]),
                    ..RuntimeCompanyMarketState::default()
                },
            )]),
            ..RuntimeServiceState::default()
        },
    };

    let policy_state = runtime_company_annual_finance_policy_state(&state, 16)
        .expect("annual finance policy state");
    assert_eq!(
        policy_state.action,
        RuntimeCompanyAnnualFinancePolicyAction::DividendAdjustment
    );
    assert!(!policy_state.stock_repurchase_eligible);
    assert!(!policy_state.stock_issue_eligible);
    assert!(policy_state.dividend_adjustment_eligible);
}

#[test]
fn reads_company_market_metrics_from_annual_finance_reader() {
    let current_issue_calendar_word = 0x0101_0726;
    let current_issue_calendar_word_2 = 0x0001_0001;
    let prior_issue_calendar_word = 0x0101_0725;
    let prior_issue_calendar_word_2 = 0x0001_0001;
    let state = RuntimeState {
        calendar: CalendarPoint {
            year: 1830,
            month_slot: 0,
            phase_slot: 0,
            tick_slot: 0,
        },
        world_flags: BTreeMap::new(),
        save_profile: RuntimeSaveProfileState::default(),
        world_restore: RuntimeWorldRestoreState {
            absolute_counter_raw_u32: Some(885_427_260),
            ..RuntimeWorldRestoreState::default()
        },
        metadata: BTreeMap::new(),
        companies: vec![RuntimeCompany {
            company_id: 7,
            current_cash: 0,
            debt: 0,
            credit_rating_score: None,
            prime_rate: None,
            active: true,
            available_track_laying_capacity: None,
            controller_kind: RuntimeCompanyControllerKind::Unknown,
            linked_chairman_profile_id: None,
            book_value_per_share: 0,
            investor_confidence: 0,
            management_attitude: 0,
            takeover_cooldown_year: None,
            merger_cooldown_year: None,
            track_piece_counts: RuntimeTrackPieceCounts::default(),
        }],
        selected_company_id: None,
        players: Vec::new(),
        selected_player_id: None,
        chairman_profiles: vec![RuntimeChairmanProfile {
            profile_id: 1,
            name: "Chairman One".to_string(),
            active: true,
            current_cash: 0,
            linked_company_id: Some(7),
            company_holdings: BTreeMap::from([(7, 12_000)]),
            holdings_value_total: 0,
            net_worth_total: 0,
            purchasing_power_total: 0,
        }],
        selected_chairman_profile_id: None,
        trains: Vec::new(),
        locomotive_catalog: Vec::new(),
        cargo_catalog: Vec::new(),
        territories: Vec::new(),
        company_territory_track_piece_counts: Vec::new(),
        company_territory_access: Vec::new(),
        packed_event_collection: None,
        event_runtime_records: Vec::new(),
        candidate_availability: BTreeMap::new(),
        named_locomotive_availability: BTreeMap::new(),
        named_locomotive_cost: BTreeMap::new(),
        all_cargo_price_override: None,
        named_cargo_price_overrides: BTreeMap::new(),
        all_cargo_production_override: None,
        factory_cargo_production_override: None,
        farm_mine_cargo_production_override: None,
        named_cargo_production_overrides: BTreeMap::new(),
        cargo_production_overrides: BTreeMap::new(),
        world_runtime_variables: BTreeMap::new(),
        company_runtime_variables: BTreeMap::new(),
        player_runtime_variables: BTreeMap::new(),
        territory_runtime_variables: BTreeMap::new(),
        world_scalar_overrides: BTreeMap::new(),
        special_conditions: BTreeMap::new(),
        service_state: RuntimeServiceState {
            company_market_state: BTreeMap::from([(
                7,
                RuntimeCompanyMarketState {
                    outstanding_shares: 20_000,
                    bond_count: 2,
                    largest_live_bond_principal: Some(500_000),
                    highest_coupon_live_bond_principal: Some(300_000),
                    cached_share_price_raw_u32: 0x42200000,
                    chairman_salary_baseline: 18,
                    chairman_salary_current: 27,
                    chairman_bonus_year: 1843,
                    chairman_bonus_amount: 625,
                    founding_year: 1832,
                    last_bankruptcy_year: 0,
                    last_dividend_year: 1842,
                    current_issue_calendar_word,
                    current_issue_calendar_word_2,
                    prior_issue_calendar_word,
                    prior_issue_calendar_word_2,
                    ..RuntimeCompanyMarketState::default()
                },
            )]),
            ..RuntimeServiceState::default()
        },
    };

    assert_eq!(
        runtime_company_market_metric_value(
            &state,
            7,
            RuntimeCompanyMarketMetric::OutstandingShares
        ),
        Some(20_000)
    );
    assert_eq!(
        runtime_company_market_metric_value(&state, 7, RuntimeCompanyMarketMetric::BondCount),
        Some(2)
    );
    assert_eq!(
        runtime_company_market_metric_value(
            &state,
            7,
            RuntimeCompanyMarketMetric::LargestLiveBondPrincipal
        ),
        Some(500_000)
    );
    assert_eq!(
        runtime_company_market_metric_value(
            &state,
            7,
            RuntimeCompanyMarketMetric::HighestCouponLiveBondPrincipal
        ),
        Some(300_000)
    );
    assert_eq!(
        runtime_company_market_metric_value(
            &state,
            7,
            RuntimeCompanyMarketMetric::AssignedSharePool
        ),
        Some(12_000)
    );
    assert_eq!(
        runtime_company_market_metric_value(
            &state,
            7,
            RuntimeCompanyMarketMetric::UnassignedSharePool
        ),
        Some(8_000)
    );
    assert_eq!(
        runtime_company_market_metric_value(
            &state,
            7,
            RuntimeCompanyMarketMetric::CachedSharePrice
        ),
        Some(40)
    );
    assert_eq!(
        runtime_company_market_metric_value(
            &state,
            7,
            RuntimeCompanyMarketMetric::CurrentIssueAbsoluteCounter
        ),
        Some(885_427_200)
    );
    assert_eq!(
        runtime_company_market_metric_value(
            &state,
            7,
            RuntimeCompanyMarketMetric::PriorIssueAbsoluteCounter
        ),
        Some(884_943_360)
    );
    assert_eq!(
        runtime_company_market_metric_value(
            &state,
            7,
            RuntimeCompanyMarketMetric::CurrentIssueAgeAbsoluteCounterDelta
        ),
        Some(60)
    );
    assert_eq!(
        runtime_company_market_metric_value(
            &state,
            7,
            RuntimeCompanyMarketMetric::CurrentIssueCalendarWord
        ),
        Some(i64::from(current_issue_calendar_word))
    );
    assert_eq!(
        runtime_company_market_metric_value(
            &state,
            7,
            RuntimeCompanyMarketMetric::CurrentIssueCalendarWord2
        ),
        Some(i64::from(current_issue_calendar_word_2))
    );
    assert_eq!(
        runtime_company_market_metric_value(
            &state,
            7,
            RuntimeCompanyMarketMetric::PriorIssueCalendarWord
        ),
        Some(i64::from(prior_issue_calendar_word))
    );
    assert_eq!(
        runtime_company_market_metric_value(
            &state,
            7,
            RuntimeCompanyMarketMetric::PriorIssueCalendarWord2
        ),
        Some(i64::from(prior_issue_calendar_word_2))
    );
    assert_eq!(
        runtime_company_market_metric_value(
            &state,
            7,
            RuntimeCompanyMarketMetric::ChairmanSalaryCurrent
        ),
        Some(27)
    );
    assert_eq!(
        runtime_company_market_metric_value(
            &state,
            7,
            RuntimeCompanyMarketMetric::ChairmanBonusAmount
        ),
        Some(625)
    );
    assert_eq!(
        runtime_company_market_metric_value(
            &state,
            99,
            RuntimeCompanyMarketMetric::OutstandingShares
        ),
        None
    );
}

#[test]
fn derives_elapsed_company_finance_years_from_calendar_and_saved_market_state() {
    let state = RuntimeState {
        calendar: CalendarPoint {
            year: 1844,
            month_slot: 0,
            phase_slot: 0,
            tick_slot: 0,
        },
        world_flags: BTreeMap::new(),
        save_profile: RuntimeSaveProfileState::default(),
        world_restore: RuntimeWorldRestoreState::default(),
        metadata: BTreeMap::new(),
        companies: vec![RuntimeCompany {
            company_id: 3,
            current_cash: 0,
            debt: 0,
            credit_rating_score: None,
            prime_rate: None,
            active: true,
            available_track_laying_capacity: None,
            controller_kind: RuntimeCompanyControllerKind::Unknown,
            linked_chairman_profile_id: None,
            book_value_per_share: 0,
            investor_confidence: 0,
            management_attitude: 0,
            takeover_cooldown_year: None,
            merger_cooldown_year: None,
            track_piece_counts: RuntimeTrackPieceCounts::default(),
        }],
        selected_company_id: None,
        players: Vec::new(),
        selected_player_id: None,
        chairman_profiles: Vec::new(),
        selected_chairman_profile_id: None,
        trains: Vec::new(),
        locomotive_catalog: Vec::new(),
        cargo_catalog: Vec::new(),
        territories: Vec::new(),
        company_territory_track_piece_counts: Vec::new(),
        company_territory_access: Vec::new(),
        packed_event_collection: None,
        event_runtime_records: Vec::new(),
        candidate_availability: BTreeMap::new(),
        named_locomotive_availability: BTreeMap::new(),
        named_locomotive_cost: BTreeMap::new(),
        all_cargo_price_override: None,
        named_cargo_price_overrides: BTreeMap::new(),
        all_cargo_production_override: None,
        factory_cargo_production_override: None,
        farm_mine_cargo_production_override: None,
        named_cargo_production_overrides: BTreeMap::new(),
        cargo_production_overrides: BTreeMap::new(),
        world_runtime_variables: BTreeMap::new(),
        company_runtime_variables: BTreeMap::new(),
        player_runtime_variables: BTreeMap::new(),
        territory_runtime_variables: BTreeMap::new(),
        world_scalar_overrides: BTreeMap::new(),
        special_conditions: BTreeMap::new(),
        service_state: RuntimeServiceState {
            company_market_state: BTreeMap::from([(
                3,
                RuntimeCompanyMarketState {
                    outstanding_shares: 10_000,
                    founding_year: 1838,
                    last_bankruptcy_year: 1841,
                    last_dividend_year: 1843,
                    ..RuntimeCompanyMarketState::default()
                },
            )]),
            ..RuntimeServiceState::default()
        },
    };

    let finance_state =
        runtime_company_annual_finance_state(&state, 3).expect("finance state should derive");
    assert_eq!(finance_state.years_since_founding, Some(6));
    assert_eq!(finance_state.years_since_last_bankruptcy, Some(3));
    assert_eq!(finance_state.years_since_last_dividend, Some(1));
}
