use std::{
	fmt::Display,
	path::{self, PathBuf},
};

use serde::Deserialize;

use crate::ASSETS_FOLDER_PATH;

use super::{Game, materials::MaterialDetails};

#[derive(Default, Debug, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct Enemy {
	pub name: String,
	pub icon: Option<String>,
	#[serde(default)]
	pub world: Vec<SpawnLocation>,
	#[serde(default)]
	pub drops: Vec<EnemyDrop>,
}

impl Enemy {
	pub fn import(path: &str) -> Vec<Enemy> {
		let mut enemies: Vec<Enemy> = vec![];

		// Loading multiple files into one vector due to the size of each board
		let paths = std::fs::read_dir(path)
			.unwrap()
			.filter_map(|f| f.ok())
			.map(|f| f.path())
			.filter_map(|p| match p.extension().is_some_and(|e| e == "toml") {
				true => Some(p),
				false => None,
			})
			.collect::<Vec<PathBuf>>();

		for path in paths {
			let enemy_str = std::fs::read_to_string(path).unwrap();
			let mut enemy = toml::from_str::<Enemy>(&enemy_str).unwrap();
			enemy.drops.iter_mut().for_each(|d| {
				d.from = enemy.name.clone();
				d.spawns = enemy.world.clone();
			});
			enemies.push(enemy);
		}

		enemies
	}
}

#[derive(Debug, Clone, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct EnemyDrop {
	pub name: String,
	#[serde(skip)]
	pub from: String,
	#[serde(skip)]
	pub spawns: Vec<SpawnLocation>,
	pub chance: EnemyDropChance,
	pub kind: EnemyDropKind,
	pub info: Option<String>,
	pub material: Option<MaterialDetails>,
}

impl EnemyDrop {
	pub fn texture(&self, game: &Game) -> String {
		let filename = self.from.replace(" ", "_").to_lowercase();
		let path = format!("{}/enemies/{}/{}.webp", ASSETS_FOLDER_PATH, game, filename);
		let path = path::Path::new(&path);

		if path.exists() {
			return format!("{game}/{filename}");
		} else {
			return filename;
		}
	}
}

#[derive(Debug, Clone, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[serde(rename_all = "lowercase")]
pub enum EnemyDropKind {
	Item,
	Material,
	Equipment,
}

#[derive(Debug, Clone, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[serde(untagged)]
pub enum EnemyDropChance {
	Fixed(ordered_float::OrderedFloat<f32>),
	Variable(String),
}

impl Display for EnemyDropChance {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		match self {
			EnemyDropChance::Fixed(val) => f.write_str(&format!("{val}%")),
			EnemyDropChance::Variable(val) => f.write_str(val),
		}
	}
}

#[derive(Debug, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct SpawnLocation {
	pub name: String,
	#[serde(default)]
	pub rooms: Vec<String>,
}

impl SpawnLocation {
	pub fn texture(&self, game: &Game) -> String {
		format!(
			"{}/{}",
			game,
			self.name.replace(" ", "-").replace("'", "").to_lowercase()
		)
	}
}
