use super::*;

#[test]
fn reads_grounded_world_issue_state_from_runtime_restore_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 {
            issue_37_value: Some(3),
            issue_38_value: Some(1),
            issue_39_value: Some(2),
            issue_3a_value: Some(4),
            issue_37_multiplier_raw_u32: Some(0x3d75c28f),
            issue_37_multiplier_value_f32_text: Some("0.060000".to_string()),
            ..RuntimeWorldRestoreState::default()
        },
        metadata: BTreeMap::new(),
        companies: Vec::new(),
        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::default(),
    };

    let issue = runtime_world_issue_state(&state, RUNTIME_WORLD_ISSUE_INVESTOR_CONFIDENCE)
        .expect("grounded issue 0x37 state");
    assert_eq!(issue.issue_id, RUNTIME_WORLD_ISSUE_INVESTOR_CONFIDENCE);
    assert_eq!(issue.raw_value_u32, 3);
    assert_eq!(issue.multiplier_raw_u32, Some(0x3d75c28f));
    assert_eq!(issue.multiplier_value_f32_text.as_deref(), Some("0.060000"));
    assert_eq!(
        runtime_world_issue_state(&state, RUNTIME_WORLD_ISSUE_CREDIT_MARKET)
            .expect("grounded issue 0x38 state")
            .raw_value_u32,
        1
    );
    assert_eq!(
        runtime_world_issue_state(&state, RUNTIME_WORLD_ISSUE_PRIME_RATE)
            .expect("grounded issue 0x39 state")
            .raw_value_u32,
        2
    );
    assert_eq!(
        runtime_world_issue_state(&state, RUNTIME_WORLD_ISSUE_MANAGEMENT_ATTITUDE)
            .expect("grounded issue 0x3a state")
            .raw_value_u32,
        4
    );
    assert_eq!(runtime_world_issue_state(&state, 0x40), None);
    assert_eq!(runtime_world_absolute_counter(&state), None);
}

#[test]
fn sums_save_native_issue_opinion_terms_with_linked_company_fallback() {
    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: 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: 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: "Chairman".to_string(),
            active: true,
            current_cash: 0,
            linked_company_id: Some(7),
            company_holdings: BTreeMap::new(),
            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: (0..0x3b).map(|value| value as i32).collect(),
            company_market_state: BTreeMap::from([(
                7,
                RuntimeCompanyMarketState {
                    issue_opinion_terms_raw_i32: (0..0x3b)
                        .map(|value| (value as i32) * 2)
                        .collect(),
                    ..RuntimeCompanyMarketState::default()
                },
            )]),
            chairman_issue_opinion_terms_raw_i32: BTreeMap::from([(
                3,
                (0..0x3b).map(|value| (value as i32) * 3).collect(),
            )]),
            ..RuntimeServiceState::default()
        },
    };

    assert_eq!(
        runtime_world_issue_opinion_term_sum_raw(&state, 0x39, Some(3), None, None),
        Some(57 + 114 + 171)
    );
    assert_eq!(
        runtime_world_issue_opinion_term_sum_raw(&state, 0x39, None, Some(7), None),
        Some(57 + 114)
    );
}

#[test]
fn computes_save_native_issue_opinion_multiplier_with_floor() {
    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: 5,
            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: 9,
            name: "Chairman".to_string(),
            active: true,
            current_cash: 0,
            linked_company_id: Some(5),
            company_holdings: BTreeMap::new(),
            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: {
                let mut values = vec![0; 0x3b];
                values[0x38] = -150;
                values
            },
            company_market_state: BTreeMap::from([(
                5,
                RuntimeCompanyMarketState {
                    issue_opinion_terms_raw_i32: {
                        let mut values = vec![0; 0x3b];
                        values[0x38] = -60;
                        values
                    },
                    ..RuntimeCompanyMarketState::default()
                },
            )]),
            chairman_issue_opinion_terms_raw_i32: BTreeMap::from([(9, {
                let mut values = vec![0; 0x3b];
                values[0x38] = 50;
                values
            })]),
            ..RuntimeServiceState::default()
        },
    };

    let multiplier = runtime_world_issue_opinion_multiplier(&state, 0x38, Some(9), None, None)
        .expect("issue multiplier");
    assert!((multiplier - 0.01).abs() < f64::EPSILON);
}

#[test]
fn reads_grounded_world_absolute_counter_from_runtime_restore_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 {
            absolute_counter_raw_u32: Some(5),
            absolute_counter_mirror_raw_u32: Some(5),
            packed_year_word_raw_u16: Some(0x0210),
            partial_year_progress_raw_u8: Some(8),
            current_calendar_tuple_word_raw_u32: Some(0x0108_0210),
            current_calendar_tuple_word_2_raw_u32: Some(0x35e6_3160),
            ..RuntimeWorldRestoreState::default()
        },
        metadata: BTreeMap::new(),
        companies: Vec::new(),
        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::default(),
    };

    assert_eq!(runtime_world_absolute_counter(&state), Some(5));
    assert_eq!(
        runtime_world_partial_year_weight_numerator(&state),
        Some(35)
    );
}

#[test]
fn derives_prime_rate_baseline_from_saved_world_raw_word() {
    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 {
            issue_37_value: Some(5.0f32.to_bits()),
            ..RuntimeWorldRestoreState::default()
        },
        metadata: BTreeMap::new(),
        companies: Vec::new(),
        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::default(),
    };

    assert_eq!(runtime_world_prime_rate_baseline(&state), Some(5.0));
}

#[test]
fn derives_company_prime_rate_from_issue39_owner_state() {
    let mut company_terms = vec![0; 0x3b];
    company_terms[RUNTIME_WORLD_ISSUE_PRIME_RATE as usize] = 50;
    let mut chairman_terms = vec![0; 0x3b];
    chairman_terms[RUNTIME_WORLD_ISSUE_PRIME_RATE as usize] = 25;
    let mut world_terms = vec![0; 0x3b];
    world_terms[RUNTIME_WORLD_ISSUE_PRIME_RATE as usize] = 100;

    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 {
            issue_37_value: Some(5.0f32.to_bits()),
            ..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: 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: "Chairman".to_string(),
            active: true,
            current_cash: 0,
            linked_company_id: Some(7),
            company_holdings: BTreeMap::new(),
            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: world_terms,
            company_market_state: BTreeMap::from([(
                7,
                RuntimeCompanyMarketState {
                    issue_opinion_terms_raw_i32: company_terms,
                    ..RuntimeCompanyMarketState::default()
                },
            )]),
            chairman_issue_opinion_terms_raw_i32: BTreeMap::from([(3, chairman_terms)]),
            ..RuntimeServiceState::default()
        },
    };

    assert_eq!(runtime_company_prime_rate(&state, 7), Some(7));
}
