use std::collections::BTreeSet;

use super::context::ValidationContext;
use crate::state::RuntimeState;

pub(super) fn validate_entities(
    state: &RuntimeState,
    context: &mut ValidationContext,
) -> Result<(), String> {
    for company in &state.companies {
        if !context.seen_company_ids.insert(company.company_id) {
            return Err(format!("duplicate company_id {}", company.company_id));
        }
        if company.active {
            context.active_company_ids.insert(company.company_id);
        }
    }
    if let Some(selected_company_id) = state.selected_company_id {
        if !context.seen_company_ids.contains(&selected_company_id) {
            return Err(format!(
                "selected_company_id {} does not reference a live company",
                selected_company_id
            ));
        }
        if !context.active_company_ids.contains(&selected_company_id) {
            return Err(format!(
                "selected_company_id {} must reference an active company",
                selected_company_id
            ));
        }
    }

    let mut seen_chairman_names = BTreeSet::new();
    for chairman in &state.chairman_profiles {
        if !context
            .seen_chairman_profile_ids
            .insert(chairman.profile_id)
        {
            return Err(format!(
                "duplicate chairman_profile.profile_id {}",
                chairman.profile_id
            ));
        }
        if chairman.name.trim().is_empty() {
            return Err(format!(
                "chairman_profile {} has an empty name",
                chairman.profile_id
            ));
        }
        if !seen_chairman_names.insert(chairman.name.clone()) {
            return Err(format!(
                "duplicate chairman_profile.name {:?}",
                chairman.name
            ));
        }
        if chairman.active {
            context
                .active_chairman_profile_ids
                .insert(chairman.profile_id);
        }
        if let Some(linked_company_id) = chairman.linked_company_id {
            if !context.seen_company_ids.contains(&linked_company_id) {
                return Err(format!(
                    "chairman_profile {} references unknown linked_company_id {}",
                    chairman.profile_id, linked_company_id
                ));
            }
        }
        for company_id in chairman.company_holdings.keys() {
            if !context.seen_company_ids.contains(company_id) {
                return Err(format!(
                    "chairman_profile {} references unknown holdings company_id {}",
                    chairman.profile_id, company_id
                ));
            }
        }
    }
    if let Some(selected_chairman_profile_id) = state.selected_chairman_profile_id {
        if !context
            .seen_chairman_profile_ids
            .contains(&selected_chairman_profile_id)
        {
            return Err(format!(
                "selected_chairman_profile_id {} does not reference a live chairman profile",
                selected_chairman_profile_id
            ));
        }
        if !context
            .active_chairman_profile_ids
            .contains(&selected_chairman_profile_id)
        {
            return Err(format!(
                "selected_chairman_profile_id {} must reference an active chairman profile",
                selected_chairman_profile_id
            ));
        }
    }

    for player in &state.players {
        if !context.seen_player_ids.insert(player.player_id) {
            return Err(format!("duplicate player_id {}", player.player_id));
        }
        if player.active {
            context.active_player_ids.insert(player.player_id);
        }
    }
    if let Some(selected_player_id) = state.selected_player_id {
        if !context.seen_player_ids.contains(&selected_player_id) {
            return Err(format!(
                "selected_player_id {} does not reference a live player",
                selected_player_id
            ));
        }
        if !context.active_player_ids.contains(&selected_player_id) {
            return Err(format!(
                "selected_player_id {} must reference an active player",
                selected_player_id
            ));
        }
    }

    for company in &state.companies {
        if let Some(linked_chairman_profile_id) = company.linked_chairman_profile_id {
            let linked_profile = state
                .chairman_profiles
                .iter()
                .find(|profile| profile.profile_id == linked_chairman_profile_id)
                .ok_or_else(|| {
                    format!(
                        "company {} references unknown linked_chairman_profile_id {}",
                        company.company_id, linked_chairman_profile_id
                    )
                })?;
            if linked_profile.linked_company_id != Some(company.company_id) {
                return Err(format!(
                    "company {} linked_chairman_profile_id {} must point back through linked_company_id",
                    company.company_id, linked_chairman_profile_id
                ));
            }
        }
    }
    for chairman in &state.chairman_profiles {
        if let Some(linked_company_id) = chairman.linked_company_id {
            let linked_company = state
                .companies
                .iter()
                .find(|company| company.company_id == linked_company_id)
                .ok_or_else(|| {
                    format!(
                        "chairman_profile {} references unknown linked_company_id {}",
                        chairman.profile_id, linked_company_id
                    )
                })?;
            if linked_company.linked_chairman_profile_id != Some(chairman.profile_id) {
                return Err(format!(
                    "chairman_profile {} linked_company_id {} must point back through linked_chairman_profile_id",
                    chairman.profile_id, linked_company_id
                ));
            }
        }
    }

    for region in &state.service_state.near_city_acquisition_regions {
        if !context.seen_near_city_region_ids.insert(region.region_id) {
            return Err(format!(
                "duplicate near_city_acquisition_region.region_id {}",
                region.region_id
            ));
        }
        if region.name.trim().is_empty() {
            return Err(format!(
                "near_city_acquisition_region {} has an empty name",
                region.region_id
            ));
        }
    }

    for site in &state.service_state.near_city_acquisition_sites {
        if !context.seen_near_city_site_ids.insert(site.site_id) {
            return Err(format!(
                "duplicate near_city_acquisition_site.site_id {}",
                site.site_id
            ));
        }
        if site.primary_name.trim().is_empty() {
            return Err(format!(
                "near_city_acquisition_site {} has an empty primary_name",
                site.site_id
            ));
        }
        if let Some(region_id) = site.region_id {
            if !context.seen_near_city_region_ids.contains(&region_id) {
                return Err(format!(
                    "near_city_acquisition_site {} references unknown region_id {}",
                    site.site_id, region_id
                ));
            }
        }
        if let Some(owner_company_id) = site.owner_company_id {
            if !context.seen_company_ids.contains(&owner_company_id) {
                return Err(format!(
                    "near_city_acquisition_site {} references unknown owner_company_id {}",
                    site.site_id, owner_company_id
                ));
            }
        }
        if let Some(preferred_company_id) = site.preferred_company_id {
            if !context.seen_company_ids.contains(&preferred_company_id) {
                return Err(format!(
                    "near_city_acquisition_site {} references unknown preferred_company_id {}",
                    site.site_id, preferred_company_id
                ));
            }
        }
    }

    Ok(())
}
