use std::collections::BTreeMap;

use serde::{Deserialize, Serialize};

use crate::engine::advance::{advance_to_target_calendar_point, step_count};
use crate::engine::service::{service_periodic_boundary, service_trigger_kind};
use crate::event::news::{RuntimeAnnualFinanceNewsEvent, RuntimeNearCityAcquisitionNewsEvent};
use crate::state::RuntimeState;
use crate::summary::RuntimeSummary;

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum StepCommand {
    AdvanceTo { calendar: crate::CalendarPoint },
    StepCount { steps: u32 },
    ServiceTriggerKind { trigger_kind: u8 },
    ServicePeriodicBoundary,
}

impl StepCommand {
    pub fn validate(&self) -> Result<(), String> {
        match self {
            Self::AdvanceTo { calendar } => calendar.validate(),
            Self::StepCount { steps } => {
                if *steps == 0 {
                    return Err("step_count command requires steps > 0".to_string());
                }
                Ok(())
            }
            Self::ServiceTriggerKind { .. } | Self::ServicePeriodicBoundary => Ok(()),
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BoundaryEvent {
    pub kind: String,
    pub calendar: crate::CalendarPoint,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ServiceEvent {
    pub kind: String,
    pub trigger_kind: Option<u8>,
    pub serviced_record_ids: Vec<u32>,
    pub applied_effect_count: u32,
    pub mutated_company_ids: Vec<u32>,
    pub mutated_player_ids: Vec<u32>,
    pub appended_record_ids: Vec<u32>,
    pub activated_record_ids: Vec<u32>,
    pub deactivated_record_ids: Vec<u32>,
    pub removed_record_ids: Vec<u32>,
    #[serde(default)]
    pub finance_news_family_candidates: BTreeMap<u32, String>,
    #[serde(default)]
    pub annual_finance_news_events: Vec<RuntimeAnnualFinanceNewsEvent>,
    #[serde(default)]
    pub near_city_acquisition_news_family_candidates: BTreeMap<u32, String>,
    #[serde(default)]
    pub near_city_acquisition_news_events: Vec<RuntimeNearCityAcquisitionNewsEvent>,
    pub dirty_rerun: bool,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StepResult {
    pub initial_summary: RuntimeSummary,
    pub final_summary: RuntimeSummary,
    pub steps_executed: u64,
    pub boundary_events: Vec<BoundaryEvent>,
    pub service_events: Vec<ServiceEvent>,
}
pub fn execute_step_command(
    state: &mut RuntimeState,
    command: &StepCommand,
) -> Result<StepResult, String> {
    state.validate()?;
    command.validate()?;

    let initial_summary = RuntimeSummary::from_state(state);
    let mut boundary_events = Vec::new();
    let mut service_events = Vec::new();
    let steps_executed = match command {
        StepCommand::AdvanceTo { calendar } => advance_to_target_calendar_point(
            state,
            *calendar,
            &mut boundary_events,
            &mut service_events,
        )?,
        StepCommand::StepCount { steps } => {
            step_count(state, *steps, &mut boundary_events, &mut service_events)?
        }
        StepCommand::ServiceTriggerKind { trigger_kind } => {
            service_trigger_kind(state, *trigger_kind, &mut service_events)?;
            0
        }
        StepCommand::ServicePeriodicBoundary => {
            service_periodic_boundary(state, &mut service_events)?;
            0
        }
    };
    state.refresh_derived_world_state();
    state.refresh_derived_market_state();
    let final_summary = RuntimeSummary::from_state(state);

    Ok(StepResult {
        initial_summary,
        final_summary,
        steps_executed,
        boundary_events,
        service_events,
    })
}
