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

use crate::engine::command::ServiceEvent;
use crate::event::news::RuntimeNearCityAcquisitionNewsEvent;
use crate::state::{
    RuntimeNearCityAcquisitionSite, RuntimeNearCityAcquisitionValueProvenance, RuntimeState,
};

pub(crate) fn service_company_near_city_acquisition(
    state: &mut RuntimeState,
    service_events: &mut Vec<ServiceEvent>,
) -> Result<(), String> {
    state.service_state.near_city_acquisition_service_calls += 1;
    state
        .service_state
        .near_city_acquisition_last_news_family_candidates
        .clear();
    state
        .service_state
        .near_city_acquisition_last_news_events
        .clear();

    let active_company_ids = state
        .companies
        .iter()
        .filter(|company| company.active)
        .map(|company| company.company_id)
        .collect::<BTreeSet<_>>();
    let mut acquired_company_ids = BTreeSet::new();
    let mut mutated_company_ids = BTreeSet::new();
    let mut news_family_candidates = BTreeMap::new();
    let mut news_events = Vec::new();
    let mut applied_effect_count = 0_u32;

    for site_index in 0..state.service_state.near_city_acquisition_sites.len() {
        let site_snapshot = state.service_state.near_city_acquisition_sites[site_index].clone();
        if site_snapshot.owner_company_id.is_some()
            || !is_acquirable_subtype(&site_snapshot.candidate_subtype_label)
        {
            continue;
        }

        let Some(company_id) = select_near_city_acquisition_company(
            state,
            &site_snapshot,
            &active_company_ids,
            &acquired_company_ids,
        ) else {
            continue;
        };
        let site = &mut state.service_state.near_city_acquisition_sites[site_index];

        site.owner_company_id = Some(company_id);
        site.owner_company_id_provenance =
            RuntimeNearCityAcquisitionValueProvenance::BestEffortGuess;
        acquired_company_ids.insert(company_id);
        mutated_company_ids.insert(company_id);
        applied_effect_count += 1;

        let selector_label = near_city_selector_label(site);
        state
            .service_state
            .near_city_acquisition_last_news_family_candidates
            .insert(site.site_id, selector_label.clone());
        news_family_candidates.insert(site.site_id, selector_label.clone());

        let news_event = RuntimeNearCityAcquisitionNewsEvent {
            company_id,
            site_id: site.site_id,
            site_primary_name: site.primary_name.clone(),
            site_secondary_name: site.secondary_name.clone(),
            selector_label,
            outcome_label: "acquired_near_city_industry".to_string(),
            subtype_label: site.candidate_subtype_label.clone(),
            owner_company_id_provenance: site.owner_company_id_provenance,
            cached_tri_lane_provenance: site.cached_tri_lane_provenance,
        };
        state
            .service_state
            .near_city_acquisition_last_news_events
            .push(news_event.clone());
        news_events.push(news_event);
    }

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

    Ok(())
}

fn select_near_city_acquisition_company(
    state: &RuntimeState,
    site: &RuntimeNearCityAcquisitionSite,
    active_company_ids: &BTreeSet<u32>,
    acquired_company_ids: &BTreeSet<u32>,
) -> Option<u32> {
    let mut candidates = state
        .companies
        .iter()
        .filter(|company| active_company_ids.contains(&company.company_id))
        .filter(|company| !acquired_company_ids.contains(&company.company_id))
        .map(|company| {
            let market_state = state
                .service_state
                .company_market_state
                .get(&company.company_id);
            let side_latch_state = state
                .service_state
                .company_periodic_side_latch_state
                .get(&company.company_id);
            let mut score = 0_i32;
            if state.selected_company_id == Some(company.company_id) {
                score += 3;
            }
            if site.preferred_company_id == Some(company.company_id) {
                score += 4;
            }
            if side_latch_state.is_some_and(|latch| latch.linked_transit_latch) {
                score += 4;
            }
            if side_latch_state.is_some_and(|latch| latch.city_connection_latch) {
                score += 2;
            }
            if market_state
                .is_some_and(|market| market.linked_transit_route_anchor_entry_id.is_some())
            {
                score += 1;
            }
            (score, company.company_id)
        })
        .collect::<Vec<_>>();
    candidates.sort_by(|left, right| right.cmp(left));

    candidates
        .into_iter()
        .find(|(score, _)| *score >= 4)
        .map(|(_, company_id)| company_id)
}

fn is_acquirable_subtype(subtype_label: &str) -> bool {
    matches!(
        subtype_label,
        "farm" | "mine" | "industry" | "industry_like"
    )
}

fn near_city_selector_label(site: &RuntimeNearCityAcquisitionSite) -> String {
    match site.candidate_subtype_label.as_str() {
        "farm" => "near_city_farm_acquisition",
        "mine" => "near_city_mine_acquisition",
        "industry" | "industry_like" => "near_city_industry_acquisition",
        _ => "near_city_site_acquisition",
    }
    .to_string()
}
