use std::collections::BTreeSet;

use crate::engine::conditions::ResolvedConditionContext;
use crate::event::targets::RuntimePlayerTarget;
use crate::state::{RuntimeCompanyControllerKind, RuntimeState};

use super::shared;

pub(in crate::engine) fn resolve_player_target_ids(
    state: &RuntimeState,
    target: &RuntimePlayerTarget,
    condition_context: &ResolvedConditionContext,
) -> Result<Vec<u32>, String> {
    match target {
        RuntimePlayerTarget::AllActive => Ok(state
            .players
            .iter()
            .filter(|player| player.active)
            .map(|player| player.player_id)
            .collect()),
        RuntimePlayerTarget::Ids { ids } => {
            let known_ids = state
                .players
                .iter()
                .map(|player| player.player_id)
                .collect::<BTreeSet<_>>();
            shared::validate_known_ids(ids, &known_ids, |player_id| {
                format!("target references unknown player_id {player_id}")
            })?;
            Ok(ids.clone())
        }
        RuntimePlayerTarget::HumanPlayers => {
            shared::ensure_no_unknown_player_controller_kinds(
                &state.players,
                "target requires player role context but at least one player has unknown controller_kind",
            )?;
            Ok(state
                .players
                .iter()
                .filter(|player| {
                    player.active && player.controller_kind == RuntimeCompanyControllerKind::Human
                })
                .map(|player| player.player_id)
                .collect())
        }
        RuntimePlayerTarget::AiPlayers => {
            shared::ensure_no_unknown_player_controller_kinds(
                &state.players,
                "target requires player role context but at least one player has unknown controller_kind",
            )?;
            Ok(state
                .players
                .iter()
                .filter(|player| {
                    player.active && player.controller_kind == RuntimeCompanyControllerKind::Ai
                })
                .map(|player| player.player_id)
                .collect())
        }
        RuntimePlayerTarget::SelectedPlayer => {
            let selected_player_id = state
                .selected_player_id
                .ok_or_else(|| "target requires selected_player_id context".to_string())?;
            if state
                .players
                .iter()
                .any(|player| player.player_id == selected_player_id && player.active)
            {
                Ok(vec![selected_player_id])
            } else {
                Err("target requires selected_player_id to reference an active player".to_string())
            }
        }
        RuntimePlayerTarget::ConditionTruePlayer => {
            if condition_context.matching_player_ids.is_empty() {
                Err("target requires player condition-evaluation context".to_string())
            } else {
                Ok(condition_context
                    .matching_player_ids
                    .iter()
                    .copied()
                    .collect())
            }
        }
    }
}
