use std::collections::{BTreeMap, BTreeSet};

use crate::engine::command::ServiceEvent;
use crate::engine::conditions::evaluate_record_conditions;
use crate::engine::effects::apply_runtime_effects;
use crate::engine::mutations::commit_staged_event_graph_mutations;
use crate::state::RuntimeState;

pub(crate) fn service_trigger_kind(
    state: &mut RuntimeState,
    trigger_kind: u8,
    service_events: &mut Vec<ServiceEvent>,
) -> Result<(), String> {
    let eligible_indices = state
        .event_runtime_records
        .iter()
        .enumerate()
        .filter(|(_, record)| {
            record.active
                && record.trigger_kind == trigger_kind
                && !(record.one_shot && record.has_fired)
        })
        .map(|(index, _)| index)
        .collect::<Vec<_>>();

    let mut serviced_record_ids = Vec::new();
    let mut applied_effect_count = 0_u32;
    let mut mutated_company_ids = BTreeSet::new();
    let mut mutated_player_ids = BTreeSet::new();
    let mut appended_record_ids = Vec::new();
    let mut activated_record_ids = Vec::new();
    let mut deactivated_record_ids = Vec::new();
    let mut removed_record_ids = Vec::new();
    let mut staged_event_graph_mutations = Vec::new();
    let mut dirty_rerun = false;

    *state
        .service_state
        .trigger_dispatch_counts
        .entry(trigger_kind)
        .or_insert(0) += 1;

    for index in eligible_indices {
        let (
            record_id,
            record_conditions,
            record_effects,
            record_marks_collection_dirty,
            record_one_shot,
        ) = {
            let record = &state.event_runtime_records[index];
            (
                record.record_id,
                record.conditions.clone(),
                record.effects.clone(),
                record.marks_collection_dirty,
                record.one_shot,
            )
        };

        let Some(condition_context) = evaluate_record_conditions(state, &record_conditions)? else {
            continue;
        };

        let effect_summary = apply_runtime_effects(
            state,
            &record_effects,
            &condition_context,
            &mut mutated_company_ids,
            &mut mutated_player_ids,
            &mut staged_event_graph_mutations,
        )?;
        applied_effect_count += effect_summary.applied_effect_count;
        appended_record_ids.extend(effect_summary.appended_record_ids);
        activated_record_ids.extend(effect_summary.activated_record_ids);
        deactivated_record_ids.extend(effect_summary.deactivated_record_ids);
        removed_record_ids.extend(effect_summary.removed_record_ids);

        {
            let record = &mut state.event_runtime_records[index];
            record.service_count += 1;
            if record_one_shot {
                record.has_fired = true;
            }
        }

        serviced_record_ids.push(record_id);
        state.service_state.total_event_record_services += 1;
        if trigger_kind != 0x0a && record_marks_collection_dirty {
            dirty_rerun = true;
        }
    }

    commit_staged_event_graph_mutations(state, &staged_event_graph_mutations)?;

    service_events.push(ServiceEvent {
        kind: "trigger_dispatch".to_string(),
        trigger_kind: Some(trigger_kind),
        serviced_record_ids,
        applied_effect_count,
        mutated_company_ids: mutated_company_ids.into_iter().collect(),
        mutated_player_ids: mutated_player_ids.into_iter().collect(),
        appended_record_ids,
        activated_record_ids,
        deactivated_record_ids,
        removed_record_ids,
        finance_news_family_candidates: BTreeMap::new(),
        annual_finance_news_events: Vec::new(),
        near_city_acquisition_news_family_candidates: BTreeMap::new(),
        near_city_acquisition_news_events: Vec::new(),
        dirty_rerun,
    });

    if dirty_rerun {
        state.service_state.dirty_rerun_count += 1;
        service_trigger_kind(state, 0x0a, service_events)?;
    }

    Ok(())
}
