use std::{collections::HashMap, fmt::Display, path};

use serde::Deserialize;

use crate::ASSETS_FOLDER_PATH;

use super::{
	Game, GameProps,
	enemy::{Enemy, EnemyDrop},
};

pub const GENERIC_MATERIAL_ICON: &str = "generic";

#[derive(Debug, Clone, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct MaterialDetails {
	pub category: MaterialCategory,
	pub kind: MaterialKind,
}

#[derive(Debug, Clone, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(rename_all = "lowercase")]
pub enum MaterialCategory {
	// Common
	Adamantite,
	#[serde(alias = "blaze")]
	Blazing,
	#[serde(alias = "soothing")]
	Bright,
	#[serde(alias = "betwixt")]
	Dense,
	#[serde(alias = "writhing")]
	Dark,
	#[serde(alias = "wellspring")]
	Energy,
	Frost,
	#[serde(alias = "thunder")]
	Lightning,
	Lucid,
	Mythril,
	Orichalcum,
	#[serde(alias = "pulsing")]
	Power,
	#[serde(alias = "shiny")]
	Shimmering,
	#[serde(alias = "mystery", alias = "hungry")]
	Serenity,
	Twilight,

	// Kh1(_) Only
	Spirit,
	Stormy,

	// Kh2(_) Only
	Remembrance,
	Tranquility,
	Illusion,

	// KH3 Only
	Sinister,
	Evanescent,
	Illusory,
	Fluorite,
	Damascus,
	Electrum,

	// Days Only
	Gust,
	Shining,
	GearComponent,
	ComboTech,
	ShieldTech,
	RuneTech,
	PowerTech,
	AerialTech,
	RangeTech,
	LuckTech,
	Ankharite,
	Iron,
	Bronze,
	DarkIngot,
	Silver,
	Gold,
	Moonstone,
	Diamond,
	PremiumOrb,

	// DDD Only
	Fleeting,
	Lofty,
	Rampant,
	Dulcet,
	Intrepid,
	Noble,
	Grim,
	Vibrant,
	Troubling,
	Wondrous,
	Malleable,
	Prickly,
	Wild,
	Epic,
	Charming,
	Brilliant,
	Savage,
}

impl MaterialCategory {
	pub fn get_texture_group(&self) -> String {
		match self {
			MaterialCategory::Adamantite => "adamantite".to_string(),
			MaterialCategory::Blazing => "blazing".to_string(),
			MaterialCategory::Bright => "bright".to_string(),
			MaterialCategory::Dense => "dense".to_string(),
			MaterialCategory::Dark => "dark".to_string(),
			MaterialCategory::Energy => "energy".to_string(),
			MaterialCategory::Frost => "frost".to_string(),
			MaterialCategory::Lightning => "lightning".to_string(),
			MaterialCategory::Lucid => "lucid".to_string(),
			MaterialCategory::Mythril => "mythril".to_string(),
			MaterialCategory::Orichalcum => "orichalcum".to_string(),
			MaterialCategory::Power => "power".to_string(),
			MaterialCategory::Shimmering => "shimmering".to_string(),
			MaterialCategory::Serenity => "serenity".to_string(),
			MaterialCategory::Twilight => "twilight".to_string(),

			MaterialCategory::Spirit => "spirit".to_string(),
			MaterialCategory::Stormy => "stormy".to_string(),

			MaterialCategory::Remembrance => "remembrance".to_string(),
			MaterialCategory::Tranquility => "tranquility".to_string(),
			MaterialCategory::Illusion => "illusion".to_string(),

			MaterialCategory::Sinister => "sinister".to_string(),
			MaterialCategory::Evanescent => "evanescent".to_string(),
			MaterialCategory::Illusory => "illusory".to_string(),
			MaterialCategory::Fluorite => "fluorite".to_string(),
			MaterialCategory::Damascus => "damascus".to_string(),
			MaterialCategory::Electrum => "electrum".to_string(),

			MaterialCategory::Gust => "gust".to_string(),
			MaterialCategory::Shining => "shining".to_string(),
			MaterialCategory::GearComponent => "gear_component".to_string(),
			MaterialCategory::ComboTech => "combo_tech".to_string(),
			MaterialCategory::ShieldTech => "shield_tech".to_string(),
			MaterialCategory::RuneTech => "rune_tech".to_string(),
			MaterialCategory::PowerTech => "power_tech".to_string(),
			MaterialCategory::AerialTech => "aerial_tech".to_string(),
			MaterialCategory::RangeTech => "range_tech".to_string(),
			MaterialCategory::LuckTech => "luck_tech".to_string(),
			MaterialCategory::Ankharite => "ankharite".to_string(),
			MaterialCategory::Iron => "iron".to_string(),
			MaterialCategory::Bronze => "bronze".to_string(),
			MaterialCategory::DarkIngot => "dark_ingot".to_string(),
			MaterialCategory::Silver => "silver".to_string(),
			MaterialCategory::Gold => "gold".to_string(),
			MaterialCategory::Moonstone => "moonstone".to_string(),
			MaterialCategory::Diamond => "diamond".to_string(),
			MaterialCategory::PremiumOrb => "premium_orb".to_string(),

			MaterialCategory::Fleeting => "fleeting".to_string(),
			MaterialCategory::Lofty => "lofty".to_string(),
			MaterialCategory::Rampant => "rampant".to_string(),
			MaterialCategory::Dulcet => "dulcet".to_string(),
			MaterialCategory::Intrepid => "intrepid".to_string(),
			MaterialCategory::Noble => "noble".to_string(),
			MaterialCategory::Grim => "grim".to_string(),
			MaterialCategory::Vibrant => "vibrant".to_string(),
			MaterialCategory::Troubling => "troubling".to_string(),
			MaterialCategory::Wondrous => "wondrous".to_string(),
			MaterialCategory::Malleable => "malleable".to_string(),
			MaterialCategory::Prickly => "prickly".to_string(),
			MaterialCategory::Wild => "wild".to_string(),
			MaterialCategory::Epic => "epic".to_string(),
			MaterialCategory::Charming => "charming".to_string(),
			MaterialCategory::Brilliant => "brilliant".to_string(),
			MaterialCategory::Savage => "savage".to_string(),
		}
	}

	pub fn get_category(&self, game: &Game) -> String {
		match self {
			MaterialCategory::Blazing => match game {
				Game::Kh1(_) => "blaze".to_string(),
				_ => "blazing".to_string(),
			},
			MaterialCategory::Bright => match game {
				Game::Kh1(_) | Game::Kh2(_) => "bright".to_string(),
				_ => "soothing".to_string(),
			},
			MaterialCategory::Dense => match game {
				Game::Kh2(_) => "dense".to_string(),
				_ => "betwixt".to_string(),
			},
			MaterialCategory::Dark => match game {
				Game::Kh2(_) => "dark".to_string(),
				_ => "writhing".to_string(),
			},
			MaterialCategory::Energy => match game {
				Game::Kh1(_) | Game::Kh2(_) => "energy".to_string(),
				_ => "wellspring".to_string(),
			},
			MaterialCategory::Lightning => match game {
				Game::Kh1(_) => "thunder".to_string(),
				_ => "lightning".to_string(),
			},
			MaterialCategory::Power => match game {
				Game::Kh1(_) | Game::Kh2(_) => "power".to_string(),
				_ => "pulsing".to_string(),
			},
			MaterialCategory::Shimmering => match game {
				Game::Kh1(_) => "shiny".to_string(),
				_ => "shimmering".to_string(),
			},
			MaterialCategory::Serenity => match game {
				Game::Kh1(_) => "mystery".to_string(),
				Game::Kh2(_) => "serenity".to_string(),
				_ => "hungry".to_string(),
			},
			_ => self.get_texture_group(),
		}
	}
}

#[derive(Debug, Clone, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(rename_all = "lowercase")]
pub enum MaterialKind {
	Shard,
	Stone,
	Gem,
	Crystal,

	Figment,
	Fancy,
	Fantasy,
}

impl Display for MaterialKind {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		match self {
			MaterialKind::Shard => f.write_str("shard"),
			MaterialKind::Stone => f.write_str("stone"),
			MaterialKind::Gem => f.write_str("gem"),
			MaterialKind::Crystal => f.write_str("crystal"),

			MaterialKind::Figment => f.write_str("figment"),
			MaterialKind::Fancy => f.write_str("fancy"),
			MaterialKind::Fantasy => f.write_str("fantasy"),
		}
	}
}

#[derive(Debug, PartialEq, Eq)]
pub struct MaterialDrops {
	pub name: String,
	pub icon: String,
	pub category: MaterialCategory,
	pub kind: MaterialKind,
	pub drops: Vec<EnemyDrop>,
}

impl PartialOrd for MaterialDrops {
	fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
		if self.category.cmp(&other.category) == std::cmp::Ordering::Equal {
			return Some(self.kind.cmp(&other.kind));
		}

		Some(self.category.cmp(&other.category))
	}
}

impl MaterialDrops {
	pub fn new(enemies: Vec<Enemy>) -> Vec<MaterialDrops> {
		let mut mat_map = HashMap::<(MaterialCategory, MaterialKind), MaterialDrops>::new();

		for enemy in enemies {
			for drop in &enemy.drops {
				let Some(material) = &drop.material else {
					continue;
				};

				let key = (material.category.clone(), material.kind.clone());

				mat_map
					.entry(key)
					.and_modify(|d| d.drops.push(drop.clone()))
					.or_insert(MaterialDrops {
						name: drop.name.to_string(),
						icon: "".to_string(),
						category: material.category.clone(),
						kind: material.kind.clone(),
						drops: vec![drop.clone()],
					});
			}
		}

		let mut values: Vec<MaterialDrops> = mat_map.into_values().collect();

		values.sort_by(|a, b| a.partial_cmp(b).unwrap());

		values
	}

	pub fn drops(&self, kind: &str) -> Vec<&EnemyDrop> {
		match kind {
			"shard" => self.get_drop_kind(MaterialKind::Shard),
			"stone" => self.get_drop_kind(MaterialKind::Stone),
			"gem" => self.get_drop_kind(MaterialKind::Gem),
			"crystal" => self.get_drop_kind(MaterialKind::Crystal),
			_ => vec![],
		}
	}

	fn get_drop_kind(&self, kind: MaterialKind) -> Vec<&EnemyDrop> {
		self.drops
			.iter()
			.filter(|d| d.material.as_ref().map(|m| m.kind == kind).unwrap_or(false))
			.collect::<Vec<&EnemyDrop>>()
	}

	pub fn texture(&self, game: &Game) -> String {
		if let Game::Kh1(_) = *game {
			return GENERIC_MATERIAL_ICON.to_string();
		};

		let category = self.category.get_texture_group();
		let kind = self.kind.to_string();
		let path = format!(
			"{}/materials/{}/{}.webp",
			ASSETS_FOLDER_PATH, category, kind
		);
		let path = path::Path::new(&path);
		if path.exists() {
			format!("{}/{}", self.category.get_texture_group(), self.kind)
		} else {
			GENERIC_MATERIAL_ICON.to_string()
		}
	}
}
