use super::*;

#[test]
fn evaluates_runtime_variable_conditions_before_effects_run() {
    let mut state = RuntimeState {
        companies: vec![RuntimeCompany {
            company_id: 1,
            controller_kind: RuntimeCompanyControllerKind::Human,
            current_cash: 10,
            debt: 0,
            credit_rating_score: None,
            prime_rate: None,
            track_piece_counts: RuntimeTrackPieceCounts::default(),
            active: true,
            available_track_laying_capacity: None,
            linked_chairman_profile_id: None,
            book_value_per_share: 0,
            investor_confidence: 0,
            management_attitude: 0,
            takeover_cooldown_year: None,
            merger_cooldown_year: None,
        }],
        selected_company_id: Some(1),
        players: vec![RuntimePlayer {
            player_id: 1,
            current_cash: 50,
            active: true,
            controller_kind: RuntimeCompanyControllerKind::Human,
        }],
        selected_player_id: Some(1),
        territories: vec![RuntimeTerritory {
            territory_id: 7,
            name: Some("North".to_string()),
            track_piece_counts: RuntimeTrackPieceCounts::default(),
        }],
        world_runtime_variables: BTreeMap::from([(1, 111)]),
        company_runtime_variables: BTreeMap::from([(1, BTreeMap::from([(2, 222)]))]),
        player_runtime_variables: BTreeMap::from([(1, BTreeMap::from([(3, 333)]))]),
        territory_runtime_variables: BTreeMap::from([(7, BTreeMap::from([(4, 444)]))]),
        event_runtime_records: vec![
            RuntimeEventRecord {
                record_id: 27,
                trigger_kind: 7,
                active: true,
                service_count: 0,
                marks_collection_dirty: false,
                one_shot: false,
                has_fired: false,
                conditions: vec![
                    RuntimeCondition::WorldVariableThreshold {
                        index: 1,
                        comparator: RuntimeConditionComparator::Eq,
                        value: 111,
                    },
                    RuntimeCondition::CompanyVariableThreshold {
                        target: RuntimeCompanyTarget::SelectedCompany,
                        index: 2,
                        comparator: RuntimeConditionComparator::Eq,
                        value: 222,
                    },
                    RuntimeCondition::PlayerVariableThreshold {
                        target: RuntimePlayerTarget::SelectedPlayer,
                        index: 3,
                        comparator: RuntimeConditionComparator::Eq,
                        value: 333,
                    },
                    RuntimeCondition::TerritoryVariableThreshold {
                        target: RuntimeTerritoryTarget::Ids { ids: vec![7] },
                        index: 4,
                        comparator: RuntimeConditionComparator::Eq,
                        value: 444,
                    },
                ],
                effects: vec![RuntimeEffect::SetWorldFlag {
                    key: "runtime_variable_condition_passed".to_string(),
                    value: true,
                }],
            },
            RuntimeEventRecord {
                record_id: 28,
                trigger_kind: 7,
                active: true,
                service_count: 0,
                marks_collection_dirty: false,
                one_shot: false,
                has_fired: false,
                conditions: vec![RuntimeCondition::PlayerVariableThreshold {
                    target: RuntimePlayerTarget::SelectedPlayer,
                    index: 4,
                    comparator: RuntimeConditionComparator::Gt,
                    value: 0,
                }],
                effects: vec![RuntimeEffect::SetWorldFlag {
                    key: "runtime_variable_condition_failed".to_string(),
                    value: true,
                }],
            },
        ],
        ..state()
    };

    let result = execute_step_command(
        &mut state,
        &StepCommand::ServiceTriggerKind { trigger_kind: 7 },
    )
    .expect("runtime-variable conditions should evaluate successfully");

    assert_eq!(result.service_events[0].serviced_record_ids, vec![27]);
    assert_eq!(
        state.world_flags.get("runtime_variable_condition_passed"),
        Some(&true)
    );
    assert_eq!(
        state.world_flags.get("runtime_variable_condition_failed"),
        None
    );
}

#[test]
fn one_shot_record_only_fires_once() {
    let mut state = RuntimeState {
        event_runtime_records: vec![RuntimeEventRecord {
            record_id: 20,
            trigger_kind: 2,
            active: true,
            service_count: 0,
            marks_collection_dirty: false,
            one_shot: true,
            has_fired: false,
            conditions: Vec::new(),
            effects: vec![RuntimeEffect::SetWorldFlag {
                key: "one_shot".to_string(),
                value: true,
            }],
        }],
        ..state()
    };

    execute_step_command(
        &mut state,
        &StepCommand::ServiceTriggerKind { trigger_kind: 2 },
    )
    .expect("first one-shot service should succeed");
    let second = execute_step_command(
        &mut state,
        &StepCommand::ServiceTriggerKind { trigger_kind: 2 },
    )
    .expect("second one-shot service should succeed");

    assert_eq!(state.event_runtime_records[0].service_count, 1);
    assert!(state.event_runtime_records[0].has_fired);
    assert_eq!(
        second.service_events[0].serviced_record_ids,
        Vec::<u32>::new()
    );
    assert_eq!(second.service_events[0].applied_effect_count, 0);
}

#[test]
fn rejects_debt_underflow() {
    let mut state = RuntimeState {
        companies: vec![RuntimeCompany {
            company_id: 1,
            controller_kind: RuntimeCompanyControllerKind::Unknown,
            current_cash: 10,
            debt: 2,
            credit_rating_score: None,
            prime_rate: None,
            track_piece_counts: RuntimeTrackPieceCounts::default(),
            active: true,
            available_track_laying_capacity: None,
            linked_chairman_profile_id: None,
            book_value_per_share: 0,
            investor_confidence: 0,
            management_attitude: 0,
            takeover_cooldown_year: None,
            merger_cooldown_year: None,
        }],
        event_runtime_records: vec![RuntimeEventRecord {
            record_id: 30,
            trigger_kind: 3,
            active: true,
            service_count: 0,
            marks_collection_dirty: false,
            one_shot: false,
            has_fired: false,
            conditions: Vec::new(),
            effects: vec![RuntimeEffect::AdjustCompanyDebt {
                target: RuntimeCompanyTarget::AllActive,
                delta: -3,
            }],
        }],
        ..state()
    };

    let result = execute_step_command(
        &mut state,
        &StepCommand::ServiceTriggerKind { trigger_kind: 3 },
    );

    assert!(result.is_err());
}

#[test]
fn appended_record_waits_until_later_pass_without_dirty_rerun() {
    let mut state = RuntimeState {
        event_runtime_records: vec![RuntimeEventRecord {
            record_id: 40,
            trigger_kind: 5,
            active: true,
            service_count: 0,
            marks_collection_dirty: false,
            one_shot: true,
            has_fired: false,
            conditions: Vec::new(),
            effects: vec![RuntimeEffect::AppendEventRecord {
                record: Box::new(RuntimeEventRecordTemplate {
                    record_id: 41,
                    trigger_kind: 5,
                    active: true,
                    marks_collection_dirty: false,
                    one_shot: false,
                    conditions: Vec::new(),
                    effects: vec![RuntimeEffect::SetWorldFlag {
                        key: "follow_on_later_pass".to_string(),
                        value: true,
                    }],
                }),
            }],
        }],
        ..state()
    };

    let first = execute_step_command(
        &mut state,
        &StepCommand::ServiceTriggerKind { trigger_kind: 5 },
    )
    .expect("first pass should succeed");

    assert_eq!(first.service_events.len(), 1);
    assert_eq!(first.service_events[0].serviced_record_ids, vec![40]);
    assert_eq!(first.service_events[0].appended_record_ids, vec![41]);
    assert_eq!(state.world_flags.get("follow_on_later_pass"), None);
    assert_eq!(state.event_runtime_records.len(), 2);
    assert_eq!(state.event_runtime_records[1].service_count, 0);

    let second = execute_step_command(
        &mut state,
        &StepCommand::ServiceTriggerKind { trigger_kind: 5 },
    )
    .expect("second pass should succeed");

    assert_eq!(second.service_events[0].serviced_record_ids, vec![41]);
    assert_eq!(state.world_flags.get("follow_on_later_pass"), Some(&true));
    assert!(state.event_runtime_records[0].has_fired);
    assert_eq!(state.event_runtime_records[1].service_count, 1);
}

#[test]
fn appended_record_runs_in_dirty_rerun_after_commit() {
    let mut state = RuntimeState {
        event_runtime_records: vec![RuntimeEventRecord {
            record_id: 50,
            trigger_kind: 1,
            active: true,
            service_count: 0,
            marks_collection_dirty: true,
            one_shot: false,
            has_fired: false,
            conditions: Vec::new(),
            effects: vec![RuntimeEffect::AppendEventRecord {
                record: Box::new(RuntimeEventRecordTemplate {
                    record_id: 51,
                    trigger_kind: 0x0a,
                    active: true,
                    marks_collection_dirty: false,
                    one_shot: false,
                    conditions: Vec::new(),
                    effects: vec![RuntimeEffect::SetWorldFlag {
                        key: "dirty_rerun_follow_on".to_string(),
                        value: true,
                    }],
                }),
            }],
        }],
        ..state()
    };

    let result = execute_step_command(
        &mut state,
        &StepCommand::ServiceTriggerKind { trigger_kind: 1 },
    )
    .expect("dirty rerun with follow-on should succeed");

    assert_eq!(result.service_events.len(), 2);
    assert_eq!(result.service_events[0].serviced_record_ids, vec![50]);
    assert_eq!(result.service_events[0].appended_record_ids, vec![51]);
    assert_eq!(result.service_events[1].trigger_kind, Some(0x0a));
    assert_eq!(result.service_events[1].serviced_record_ids, vec![51]);
    assert_eq!(state.service_state.dirty_rerun_count, 1);
    assert_eq!(state.event_runtime_records.len(), 2);
    assert_eq!(state.event_runtime_records[1].service_count, 1);
    assert_eq!(state.world_flags.get("dirty_rerun_follow_on"), Some(&true));
}

#[test]
fn lifecycle_mutations_commit_between_passes() {
    let mut state = RuntimeState {
        event_runtime_records: vec![
            RuntimeEventRecord {
                record_id: 60,
                trigger_kind: 7,
                active: true,
                service_count: 0,
                marks_collection_dirty: false,
                one_shot: true,
                has_fired: false,
                conditions: Vec::new(),
                effects: vec![
                    RuntimeEffect::AppendEventRecord {
                        record: Box::new(RuntimeEventRecordTemplate {
                            record_id: 64,
                            trigger_kind: 7,
                            active: true,
                            marks_collection_dirty: false,
                            one_shot: false,
                            conditions: Vec::new(),
                            effects: vec![RuntimeEffect::SetCandidateAvailability {
                                name: "Appended Industry".to_string(),
                                value: 1,
                            }],
                        }),
                    },
                    RuntimeEffect::DeactivateEventRecord { record_id: 61 },
                    RuntimeEffect::ActivateEventRecord { record_id: 62 },
                    RuntimeEffect::RemoveEventRecord { record_id: 63 },
                ],
            },
            RuntimeEventRecord {
                record_id: 61,
                trigger_kind: 7,
                active: true,
                service_count: 0,
                marks_collection_dirty: false,
                one_shot: false,
                has_fired: false,
                conditions: Vec::new(),
                effects: vec![RuntimeEffect::SetWorldFlag {
                    key: "deactivated_after_first_pass".to_string(),
                    value: true,
                }],
            },
            RuntimeEventRecord {
                record_id: 62,
                trigger_kind: 7,
                active: false,
                service_count: 0,
                marks_collection_dirty: false,
                one_shot: false,
                has_fired: false,
                conditions: Vec::new(),
                effects: vec![RuntimeEffect::SetSpecialCondition {
                    label: "Activated On Second Pass".to_string(),
                    value: 1,
                }],
            },
            RuntimeEventRecord {
                record_id: 63,
                trigger_kind: 7,
                active: true,
                service_count: 0,
                marks_collection_dirty: false,
                one_shot: false,
                has_fired: false,
                conditions: Vec::new(),
                effects: vec![RuntimeEffect::SetWorldFlag {
                    key: "removed_after_first_pass".to_string(),
                    value: true,
                }],
            },
        ],
        ..state()
    };

    let first = execute_step_command(
        &mut state,
        &StepCommand::ServiceTriggerKind { trigger_kind: 7 },
    )
    .expect("first lifecycle pass should succeed");

    assert_eq!(
        first.service_events[0].serviced_record_ids,
        vec![60, 61, 63]
    );
    assert_eq!(first.service_events[0].appended_record_ids, vec![64]);
    assert_eq!(first.service_events[0].activated_record_ids, vec![62]);
    assert_eq!(first.service_events[0].deactivated_record_ids, vec![61]);
    assert_eq!(first.service_events[0].removed_record_ids, vec![63]);
    assert_eq!(
        state
            .event_runtime_records
            .iter()
            .map(|record| (record.record_id, record.active))
            .collect::<Vec<_>>(),
        vec![(60, true), (61, false), (62, true), (64, true)]
    );

    let second = execute_step_command(
        &mut state,
        &StepCommand::ServiceTriggerKind { trigger_kind: 7 },
    )
    .expect("second lifecycle pass should succeed");

    assert_eq!(second.service_events[0].serviced_record_ids, vec![62, 64]);
    assert_eq!(
        state.special_conditions.get("Activated On Second Pass"),
        Some(&1)
    );
    assert_eq!(
        state.candidate_availability.get("Appended Industry"),
        Some(&1)
    );
    assert_eq!(
        state.world_flags.get("deactivated_after_first_pass"),
        Some(&true)
    );
    assert_eq!(
        state.world_flags.get("removed_after_first_pass"),
        Some(&true)
    );
}

#[test]
fn rejects_duplicate_appended_record_id() {
    let mut state = RuntimeState {
        event_runtime_records: vec![
            RuntimeEventRecord {
                record_id: 70,
                trigger_kind: 4,
                active: true,
                service_count: 0,
                marks_collection_dirty: false,
                one_shot: false,
                has_fired: false,
                conditions: Vec::new(),
                effects: vec![RuntimeEffect::AppendEventRecord {
                    record: Box::new(RuntimeEventRecordTemplate {
                        record_id: 71,
                        trigger_kind: 4,
                        active: true,
                        marks_collection_dirty: false,
                        one_shot: false,
                        conditions: Vec::new(),
                        effects: Vec::new(),
                    }),
                }],
            },
            RuntimeEventRecord {
                record_id: 71,
                trigger_kind: 4,
                active: true,
                service_count: 0,
                marks_collection_dirty: false,
                one_shot: false,
                has_fired: false,
                conditions: Vec::new(),
                effects: Vec::new(),
            },
        ],
        ..state()
    };

    let result = execute_step_command(
        &mut state,
        &StepCommand::ServiceTriggerKind { trigger_kind: 4 },
    );

    assert!(result.is_err());
}

#[test]
fn rejects_missing_lifecycle_mutation_target() {
    let mut state = RuntimeState {
        event_runtime_records: vec![RuntimeEventRecord {
            record_id: 80,
            trigger_kind: 6,
            active: true,
            service_count: 0,
            marks_collection_dirty: false,
            one_shot: false,
            has_fired: false,
            conditions: Vec::new(),
            effects: vec![RuntimeEffect::ActivateEventRecord { record_id: 999 }],
        }],
        ..state()
    };

    let result = execute_step_command(
        &mut state,
        &StepCommand::ServiceTriggerKind { trigger_kind: 6 },
    );

    assert!(result.is_err());
}
