use super::*;

#[test]
fn derives_company_unassigned_share_pool_from_market_state_and_holdings() {
    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::default(),
        metadata: BTreeMap::new(),
        companies: vec![RuntimeCompany {
            company_id: 4,
            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(4),
                company_holdings: BTreeMap::from([(4, 8_000)]),
                holdings_value_total: 0,
                net_worth_total: 0,
                purchasing_power_total: 0,
            },
            RuntimeChairmanProfile {
                profile_id: 2,
                name: "Chairman Two".to_string(),
                active: true,
                current_cash: 0,
                linked_company_id: None,
                company_holdings: BTreeMap::from([(4, 7_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([(
                4,
                RuntimeCompanyMarketState {
                    outstanding_shares: 20_000,
                    ..RuntimeCompanyMarketState::default()
                },
            )]),
            ..RuntimeServiceState::default()
        },
    };

    assert_eq!(
        runtime_company_unassigned_share_pool(&state, 4),
        Some(4_500)
    );
    assert_eq!(runtime_company_unassigned_share_pool(&state, 99), None);
}

#[test]
fn derives_company_annual_finance_state_from_owned_runtime_market_state() {
    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::default(),
        metadata: BTreeMap::new(),
        companies: vec![RuntimeCompany {
            company_id: 4,
            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(4),
                company_holdings: BTreeMap::from([(4, 8_000)]),
                holdings_value_total: 0,
                net_worth_total: 0,
                purchasing_power_total: 0,
            },
            RuntimeChairmanProfile {
                profile_id: 2,
                name: "Chairman Two".to_string(),
                active: true,
                current_cash: 0,
                linked_company_id: None,
                company_holdings: BTreeMap::from([(4, 7_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([(
                4,
                RuntimeCompanyMarketState {
                    outstanding_shares: 20_000,
                    bond_count: 3,
                    largest_live_bond_principal: Some(650_000),
                    highest_coupon_live_bond_principal: Some(500_000),
                    cached_share_price_raw_u32: 0x42200000,
                    chairman_salary_baseline: 24,
                    chairman_salary_current: 30,
                    chairman_bonus_year: 1842,
                    chairman_bonus_amount: 750,
                    founding_year: 1831,
                    last_bankruptcy_year: 0,
                    last_dividend_year: 1841,
                    current_issue_calendar_word: 5,
                    current_issue_calendar_word_2: 6,
                    prior_issue_calendar_word: 4,
                    prior_issue_calendar_word_2: 5,
                    city_connection_latch: true,
                    linked_transit_latch: false,
                    ..RuntimeCompanyMarketState::default()
                },
            )]),
            ..RuntimeServiceState::default()
        },
    };

    assert_eq!(runtime_company_assigned_share_pool(&state, 4), Some(15_500));
    assert_eq!(
        runtime_company_annual_finance_state(&state, 4),
        Some(RuntimeCompanyAnnualFinanceState {
            company_id: 4,
            outstanding_shares: 20_000,
            bond_count: 3,
            largest_live_bond_principal: Some(650_000),
            highest_coupon_live_bond_principal: Some(500_000),
            live_bond_coupon_burden_total: Some(0),
            assigned_share_pool: 15_500,
            unassigned_share_pool: 4_500,
            cached_share_price: Some(40),
            chairman_salary_baseline: 24,
            chairman_salary_current: 30,
            chairman_bonus_year: 1842,
            chairman_bonus_amount: 750,
            founding_year: 1831,
            last_bankruptcy_year: 0,
            last_dividend_year: 1841,
            years_since_founding: None,
            years_since_last_bankruptcy: None,
            years_since_last_dividend: None,
            current_partial_year_weight_numerator: None,
            trailing_full_year_year_words: Vec::new(),
            trailing_full_year_net_profits: Vec::new(),
            trailing_full_year_revenues: Vec::new(),
            trailing_full_year_fuel_costs: Vec::new(),
            current_issue_absolute_counter: None,
            prior_issue_absolute_counter: None,
            current_issue_age_absolute_counter_delta: None,
            current_issue_calendar_word: 5,
            current_issue_calendar_word_2: 6,
            prior_issue_calendar_word: 4,
            prior_issue_calendar_word_2: 5,
            preferred_locomotive_engine_type_raw_u8: None,
            city_connection_latch: true,
            linked_transit_latch: false,
        })
    );
    assert_eq!(runtime_company_assigned_share_pool(&state, 99), None);
    assert_eq!(runtime_company_annual_finance_state(&state, 99), None);
}

#[test]
fn derives_company_periodic_service_state_from_side_latches_and_world_route_preference() {
    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 {
            auto_show_grade_during_track_lay_raw_u8: Some(1),
            ..RuntimeWorldRestoreState::default()
        },
        metadata: BTreeMap::new(),
        companies: vec![RuntimeCompany {
            company_id: 4,
            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(4),
        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([(
                4,
                RuntimeCompanyMarketState {
                    city_connection_latch: true,
                    linked_transit_latch: false,
                    ..RuntimeCompanyMarketState::default()
                },
            )]),
            company_periodic_side_latch_state: BTreeMap::from([(
                4,
                RuntimeCompanyPeriodicSideLatchState {
                    preferred_locomotive_engine_type_raw_u8: Some(2),
                    city_connection_latch: false,
                    linked_transit_latch: true,
                },
            )]),
            ..RuntimeServiceState::default()
        },
    };

    assert_eq!(
        runtime_company_periodic_service_state(&state, 4),
        Some(RuntimeCompanyPeriodicServiceState {
            company_id: 4,
            preferred_locomotive_engine_type_raw_u8: Some(2),
            city_connection_latch: false,
            linked_transit_latch: true,
            base_route_preference_raw_u8: Some(1),
            effective_route_preference_raw_u8: Some(2),
            electric_route_preference_override_active: true,
            effective_route_quality_multiplier_basis_points: 180,
        })
    );
    assert_eq!(runtime_company_periodic_service_state(&state, 99), None);
}

#[test]
fn periodic_service_state_falls_back_to_market_latches_and_world_base_route_preference() {
    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 {
            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_company_periodic_service_state(&state, 8),
        Some(RuntimeCompanyPeriodicServiceState {
            company_id: 8,
            preferred_locomotive_engine_type_raw_u8: None,
            city_connection_latch: true,
            linked_transit_latch: false,
            base_route_preference_raw_u8: Some(3),
            effective_route_preference_raw_u8: Some(3),
            electric_route_preference_override_active: false,
            effective_route_quality_multiplier_basis_points: 140,
        })
    );
}

#[test]
fn applies_and_restores_company_periodic_route_preference_override() {
    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(1),
            ..RuntimeWorldRestoreState::default()
        },
        metadata: BTreeMap::new(),
        companies: vec![RuntimeCompany {
            company_id: 4,
            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(4),
        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([(
                4,
                RuntimeCompanyMarketState {
                    city_connection_latch: true,
                    linked_transit_latch: false,
                    ..RuntimeCompanyMarketState::default()
                },
            )]),
            company_periodic_side_latch_state: BTreeMap::from([(
                4,
                RuntimeCompanyPeriodicSideLatchState {
                    preferred_locomotive_engine_type_raw_u8: Some(2),
                    city_connection_latch: true,
                    linked_transit_latch: false,
                },
            )]),
            ..RuntimeServiceState::default()
        },
    };

    let applied = runtime_apply_company_periodic_route_preference_override(&mut state, 4)
        .expect("electric override should apply");
    assert_eq!(
        applied,
        RuntimeWorldRoutePreferenceOverrideState {
            company_id: 4,
            base_route_preference_raw_u8: Some(1),
            effective_route_preference_raw_u8: Some(2),
            electric_route_preference_override_active: true,
        }
    );
    assert_eq!(
        state.world_restore.auto_show_grade_during_track_lay_raw_u8,
        Some(2)
    );
    assert_eq!(
        state
            .service_state
            .active_periodic_route_preference_override,
        Some(applied.clone())
    );
    assert_eq!(
        state.service_state.last_periodic_route_preference_override,
        Some(applied.clone())
    );
    assert_eq!(
        state
            .service_state
            .periodic_route_preference_override_apply_count,
        1
    );
    assert_eq!(
        state
            .service_state
            .periodic_route_preference_override_restore_count,
        0
    );

    let restored = runtime_restore_company_periodic_route_preference_override(&mut state)
        .expect("override should restore");
    assert_eq!(restored, applied);
    assert_eq!(
        state.world_restore.auto_show_grade_during_track_lay_raw_u8,
        Some(1)
    );
    assert_eq!(
        state
            .service_state
            .active_periodic_route_preference_override,
        None
    );
    assert_eq!(
        state.service_state.last_periodic_route_preference_override,
        Some(restored)
    );
    assert_eq!(
        state
            .service_state
            .periodic_route_preference_override_apply_count,
        1
    );
    assert_eq!(
        state
            .service_state
            .periodic_route_preference_override_restore_count,
        1
    );
}
