// Copyright (C) Call of Nil contributors
// SPDX-License-Identifier: AGPL-3.0-only

#[cfg(test)]
mod tests;

use crate::city::City;
use crate::error::{Error, Result};
use crate::military::Military;
use crate::military::maneuver::ManeuverDirection;
use crate::player::{Player, PlayerId, PlayerManager, PlayerStatus};
use crate::report::ReportId;
use crate::resources::maintenance::Maintenance;
use crate::ruler::Ruler;
use crate::world::World;

impl World {
  #[inline]
  pub fn player_manager(&self) -> &PlayerManager {
    &self.player_manager
  }

  #[inline]
  pub fn player(&self, id: &PlayerId) -> Result<&Player> {
    self.player_manager.player(id)
  }

  #[inline]
  pub(crate) fn player_mut(&mut self, id: &PlayerId) -> Result<&mut Player> {
    self.player_manager.player_mut(id)
  }

  pub fn players(&self) -> impl Iterator<Item = &Player> {
    self.player_manager.players()
  }

  #[inline]
  pub fn get_player_maintenance(&self, player: &PlayerId) -> Result<Maintenance> {
    self.get_maintenance(player)
  }

  pub fn get_player_military(&self, player: &PlayerId) -> Result<Military> {
    let ruler = Ruler::from(player);
    let coords = self.continent.coords_of(player);
    let mut military = self.military.intersection(coords)?;

    military.retain_maneuvers(|maneuver| {
      // Should not include foreign armies returning home.
      if let ManeuverDirection::Returning = maneuver.direction() {
        self
          .military
          .army(maneuver.army())
          .is_ok_and(|army| army.is_owned_by(&ruler))
      } else {
        true
      }
    });

    Ok(military)
  }

  pub fn get_player_reports(&self, player: &PlayerId) -> Vec<ReportId> {
    self
      .report_manager
      .reports_of(player)
      .collect()
  }

  #[inline]
  pub fn has_player(&self, id: &PlayerId) -> bool {
    self.player_manager.has(id)
  }

  pub fn has_any_active_player(&self) -> bool {
    self
      .player_manager
      .players()
      .any(Player::is_active)
  }

  pub fn set_player_status(&mut self, id: &PlayerId, status: PlayerStatus) -> Result<()> {
    *self
      .player_manager
      .player_mut(id)?
      .status_mut() = status;

    self.emit_player_updated(id.clone())?;

    Ok(())
  }

  pub fn spawn_player(&mut self, mut player: Player) -> Result<()> {
    let id = player.id();
    if self.has_player(&id) {
      Err(Error::PlayerAlreadySpawned(id))
    } else {
      let (coord, field) = self.find_spawn_point()?;
      *field = City::builder(coord)
        .name(&*id)
        .owner(&id)
        .build()
        .into();

      *player.status_mut() = PlayerStatus::Active;
      self.player_manager.manage(player)?;

      self.emit_public_city_updated(coord)?;

      Ok(())
    }
  }
}
