// Copyright Marcus Del Favero 2025
// Licensed under the GNU AGPLv3 with an exception, see `README.md` for details
use serde::Deserialize;

use super::{TeamZoneColour, super::scoring::condition::AnyCondition};

use crate::world::team::TeamId;
use crate::protocol::message::scores::ScoreOrdering;
use crate::utils::maths::RectCorners;

#[derive(Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ScoringConfig {
	#[serde(default)] pub controller: ScoringControllerConfig,
	#[serde(default)] pub win: AnyCondition,
	#[serde(default)] pub elimination: AnyCondition,
	#[serde(default)] pub player_ordering: ScoreOrdering,
	#[serde(default)] pub team_ordering: ScoreOrdering,
	#[serde(default = "can_join_midgame_default")] pub can_join_midgame: bool,
}

impl Default for ScoringConfig {
	fn default() -> ScoringConfig {
		ScoringConfig {
			controller: ScoringControllerConfig::default(),
			win: AnyCondition::default(),
			elimination: AnyCondition::default(),
			player_ordering: ScoreOrdering::default(),
			team_ordering: ScoreOrdering::default(),
			can_join_midgame: can_join_midgame_default(),
		}
	}
}

#[derive(Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ScoringControllerConfig {
	pub events: Vec<ScoringEventHandler>,
	#[serde(default)] pub player: ScoringControllerSubconfig,
	#[serde(default)] pub team: ScoringControllerSubconfig,
	#[serde(default)] pub remove_empty_teams: bool,
}

impl Default for ScoringControllerConfig {
	fn default() -> Self {
		ScoringControllerConfig {
			events: vec![ScoringEventHandler { listener: ScoringEventListener::Kill, score: 1, output: ScoringOutput::Both }],
			player: ScoringControllerSubconfig::default(),
			team: ScoringControllerSubconfig::default(),
			remove_empty_teams: false,
		}
	}
}

#[derive(Default, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ScoringControllerSubconfig {
	#[serde(default)] pub init: i32,
	pub min: Option<i32>,
	pub max: Option<i32>,
}

impl ScoringControllerSubconfig {
	pub(super) fn validate(&self) -> Result<(), String> {
		if let Some(min) = self.min && let Some(max) = self.max && min > max {
			return Err(format!("min score = {min} > max score = {max}"));
		}

		if let Some(min) = self.min && self.init < min {
			return Err(format!("init score = {} < min = {min}", self.init));
		}

		if let Some(max) = self.max && self.init > max {
			return Err(format!("init score = {} > max = {max}", self.init));
		}

		Ok(())
	}
}

#[derive(Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ScoringEventHandler {
	pub listener: ScoringEventListener,
	pub score: i32,
	#[serde(default)] pub output: ScoringOutput,
}

#[derive(Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub enum ScoringEventListener {
	Kill,
	Death,
	Time(f32), // Time period the score is added
	Occupation(OccupationZone, #[serde(default = "super::team_zone_colour_default")] Option<TeamZoneColour>),
	Flag(Option<usize>),
}

#[derive(Default, Clone, Copy, Deserialize)]
#[serde(deny_unknown_fields)]
pub enum ScoringOutput {
	Player,
	Team,
	#[default] Both,
}

impl ScoringOutput {
	pub fn player(self) -> bool {
		matches!(self, ScoringOutput::Player | ScoringOutput::Both)
	}

	pub fn team(self) -> bool {
		matches!(self, ScoringOutput::Team | ScoringOutput::Both)
	}
}

#[derive(Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct OccupationZone {
	pub owner: Option<TeamId>,
	pub rect: RectCorners,
	#[serde(default)] pub capture_time: f32,
	pub period: f32,
}

fn can_join_midgame_default() -> bool { true }
