use itertools::Itertools;
use serde::Deserialize;

use super::{
	ability::{Ability, AbilityType},
	board_position::BoardPosition,
	route::{Interaction, Route, RouteColor},
};

#[derive(Debug, Deserialize, PartialEq, Eq)]
pub struct Board {
	pub order: u32,
	pub spirit: String,
	pub routes: Vec<Route>,
	pub abilities: Vec<Ability>,

	#[serde(skip_deserializing)]
	pub total_lp: u32,
	#[serde(skip_deserializing)]
	pub max_level: u32,

	#[serde(skip_deserializing)]
	pub stats: Vec<(usize, String)>,
}

impl Board {
	pub fn init_ability_help(&mut self) {
		for abl in &mut self.abilities {
			if abl.tip.is_none() && abl.route.is_some() {
				let route_id = abl.route.unwrap();

				let mut route = None;
				for r in &self.routes {
					if r.id == route_id {
						route = Some(r);
						break;
					}
				}

				if route_id < 10 && route.is_some() {
					abl.tip = Some(format!("Unlocks with disposition {}", route.unwrap().name));
				}
			}
		}
	}

	pub fn init_routes(&mut self) {
		self.routes.iter_mut().for_each(|r| {
			r.interaction = match r.color {
				RouteColor::Blue => Interaction::Poke,
				RouteColor::Purple => Interaction::Poke,
				RouteColor::Yellow => Interaction::Rub,
				RouteColor::Green => Interaction::Rub,

				_ => Interaction::None,
			}
		});

		let routes = self
			.routes
			.clone()
			.into_iter()
			.filter(|r| r.id < 100)
			.collect_vec();

		self.routes.iter_mut().filter(|r| r.id < 100).for_each(|r| {
			r.tips.iter_mut().for_each(|t| {
				let route = routes.iter().find(|r| r.name == t.to);
				if let Some(route) = route {
					t.to_color = route.color.clone();
					t.tip = format!("{} {}", route.interaction, t.tip);
				}
			});
		});
	}

	pub fn init_stats(&mut self) {
		let v = self
			.abilities
			.iter()
			.filter(|&ability| ability.r#type == AbilityType::Stat)
			.sorted_by(|a, b| Ord::cmp(&a.name, &b.name))
			.collect_vec();

		let mut stats: Vec<(usize, String)> = vec![];
		for (key, chunk) in &v.into_iter().chunk_by(|k| k.name.clone()) {
			let grouped = chunk.collect_vec();
			stats.push((grouped.len(), key));
		}

		self.stats = stats;
	}

	pub fn init_total_lp(&mut self) {
		self.total_lp = self
			.abilities
			.iter()
			.filter(|&ability| ability.price.contains("LP"))
			.map(|ability| {
				let mut split = ability.price.split_whitespace();

				split.next().unwrap_or("0").parse::<u32>().unwrap_or(0)
			})
			.sum::<u32>();
	}

	pub fn init_max_level(&mut self) {
		self.max_level = self
			.abilities
			.iter()
			.filter(|&ability| ability.price.starts_with("Level"))
			.map(|ability| {
				let mut split = ability.price.split_whitespace();

				split.nth(1).unwrap_or("0").parse::<u32>().unwrap_or(0)
			})
			.sorted()
			.last()
			.unwrap_or(0);
	}

	pub fn get_size(&self) -> BoardPosition {
		let mut x = 1;
		let mut y = 1;
		for ability in &self.abilities {
			if ability.pos.0 > x {
				x = ability.pos.0;
			}

			if ability.pos.1 > y {
				y = ability.pos.1;
			}
		}

		(x, y)
	}

	pub fn get_ability_at(&self, x: &u32, y: &u32) -> Option<&Ability> {
		self.abilities
			.iter()
			.find(|&ability| ability.pos == (*x, *y))
	}

	pub fn get_char(&self, i: &u32) -> char {
		char::from_u32(64 + *i).unwrap_or('0')
	}

	pub fn get_dispositions(&self) -> Vec<&Route> {
		self.routes.iter().filter(|r| r.id < 100).collect_vec()
	}

	pub fn get_supports(&self) -> Vec<&Ability> {
		self.abilities
			.iter()
			.filter(|&ability| ability.r#type == AbilityType::Support)
			.collect_vec()
	}

	pub fn get_commands(&self) -> Vec<&Ability> {
		self.abilities
			.iter()
			.filter(|&ability| {
				ability.r#type == AbilityType::Magic
					|| ability.r#type == AbilityType::Attack
					|| ability.r#type == AbilityType::Reprisal
					|| ability.r#type == AbilityType::Defense
			})
			.collect_vec()
	}

	pub fn get_spirits(&self) -> Vec<&Ability> {
		self.abilities
			.iter()
			.filter(|&ability| ability.r#type == AbilityType::Spirit)
			.collect_vec()
	}
}
