use std::{
	collections::HashMap,
	fmt::{self, Display},
	str::FromStr,
	string::ParseError,
};

use ability::Abilities;
use askama::{FastWritable, Template, filters::HtmlSafe};
use command::Command;
use finisher::Finisher;
use itertools::Itertools;
use serde::Deserialize;

use crate::{RuntimeModule, create_file};

mod ability;
mod command;
mod finisher;
mod melding;

pub const ABILITIES_PATH: &str = "./input/bbs/abilities.toml";
const FINISHERS_PATH: &str = "./input/bbs/finishers.toml";
pub const COMMANDS_PATH: &str = "./input/bbs/commands";

#[derive(Debug, Deserialize, PartialEq, Eq)]
enum Character {
	#[serde(alias = "A")]
	Aqua,
	#[serde(alias = "V")]
	Ventus,
	#[serde(alias = "T")]
	Terra,
}

impl fmt::Display for Character {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		match self {
			Character::Aqua => f.write_str("Aqua"),
			Character::Ventus => f.write_str("Ventus"),
			Character::Terra => f.write_str("Terra"),
		}
	}
}

impl FromStr for Character {
	type Err = ParseError;

	fn from_str(s: &str) -> Result<Self, Self::Err> {
		Ok(match s {
			"Aqua" => Character::Aqua,
			"Ventus" => Character::Ventus,
			"Terra" => Character::Terra,
			_ => Character::Ventus,
		})
	}
}

impl Character {
	pub fn values() -> Vec<Character> {
		vec![Character::Aqua, Character::Ventus, Character::Terra]
	}
}

#[derive(Template)]
#[template(path = "pages/bbs/melding.html")]
struct CommandsTemplate {
	pub commands: Vec<Command>,
	pub crystals: Vec<String>,
}

#[derive(Template)]
#[template(path = "pages/bbs/finishers.html")]
struct FinishersTemplate {
	pub finishers: HashMap<String, Finisher>,
}

impl FinishersTemplate {
	pub fn get_at(&self, char: &Character, level: u8, pos: u8) -> Option<&Finisher> {
		// let char = Character::from_str(char).unwrap();
		let mut col = self
			.finishers
			.values()
			.filter(|f| f.char.contains(char) && f.level == level);

		col.find(|f| f.row == pos)
	}
}

pub struct Module;

impl RuntimeModule for Module {
	fn start_module() {
		tracing::info!("Loading BBS abilities data from {}", ABILITIES_PATH);
		let abilities = Abilities::new();

		tracing::info!("Loading BBS finishers data from {}", ABILITIES_PATH);
		let finishers_str = std::fs::read_to_string(FINISHERS_PATH).unwrap();
		let finishers = toml::from_str::<HashMap<String, Finisher>>(&finishers_str).unwrap();

		tracing::info!("Loading BBS commands data from {}", ABILITIES_PATH);
		let mut commands = Command::import();
		commands.sort_by(|a, b| a.order.cmp(&b.order));

		// Create a vec with all the crystal variants found in abilities
		let crystals = abilities
			.iter()
			.map(|x| x.from.clone())
			.unique()
			.sorted()
			.collect();

		// Create a vec of crystals and what ability they give for each recipe
		for cmd in commands.iter_mut() {
			for recipe in cmd.recipes.iter_mut() {
				recipe.set_abilities(&abilities);
			}
		}

		tracing::info!("Generating BBS melding table template");
		let melding_template = CommandsTemplate { commands, crystals };

		create_file("./out/bbs", "melding", melding_template).unwrap();

		tracing::info!("Generating BBS finishers table template");
		let finishers_template = FinishersTemplate { finishers };

		create_file("./out/bbs", "finishers", finishers_template).unwrap();
	}
}
