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

use bevy::prelude::*;
use parser::{DefinitionParser, SliceParseError, TagParseError};
use serde::{Deserialize, Serialize};

use self::parser::{FilePosition, ParseError, SliceParse};
use super::util;

pub mod creature;
pub mod parser;
pub mod site;

pub use creature::*;
pub use site::*;

pub type DefinitionId = String;

#[derive(Debug, Default, Resource, Serialize, Deserialize, Reflect)]
#[reflect(Resource)]
pub struct Definitions {
    pub sites: DefinitionMap<SiteDef>,
    pub creatures: DefinitionMap<CreatureDef>,
}

impl Definitions {
    pub fn from_default_directory() -> Option<Self> {
        match DefinitionParser::parse_directory(&util::definitions_path()) {
            Ok(defs) => Some(defs),
            Err(err) => {
                match err {
                    parser::DirectoryParseError::Io(err) => {
                        error!("Failed to read definitions: {err:?}");
                    }
                    parser::DirectoryParseError::Files {
                        parser_errors,
                        duplicates,
                    } => {
                        if !parser_errors.is_empty() {
                            error!("{} parse error(s):", parser_errors.len());
                            for parser_error in parser_errors.iter() {
                                error!("- {:?}: {}", parser_error.file_path, parser_error.error);
                            }
                        }
                        if !duplicates.is_empty() {
                            error!("{} duplicate definition(s)", duplicates.len());
                            for duplicate in duplicates.iter() {
                                error!(
                                    "- {:?}: {} ({})",
                                    duplicate.file_path, duplicate.id, duplicate.category
                                );
                            }
                        }
                    }
                }
                None
            }
        }
    }
}

#[derive(Debug, Default, Deref, DerefMut, Serialize, Deserialize, Reflect)]
pub struct DefinitionMap<T>(pub HashMap<DefinitionId, T>);

#[derive(Debug)]
pub enum TopLevelTag {
    Site,
    Creature,
}

impl Display for TopLevelTag {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}",
            match self {
                TopLevelTag::Site => "SITE",
                TopLevelTag::Creature => "CREATURE",
            }
        )
    }
}

#[derive(Default, Debug, Clone)]
pub struct Tag {
    pub name: String,
    pub values: Vec<String>,
    pub file_position: FilePosition,
}

impl Tag {
    fn parse_value<T: SliceParse>(&self) -> Result<T, ParseError> {
        T::parse(&self.values).map_err(|err| {
            ParseError::TagParse(match err {
                SliceParseError::ParseError => TagParseError::ParseError(self.clone()),
                SliceParseError::MissingValue => TagParseError::MissingValue(self.clone()),
            })
        })
    }

    pub fn as_def_string(&self) -> String {
        format!(
            "[{name}{values}]",
            name = self.name,
            values = self
                .values
                .iter()
                .map(|value| format!(":{value}"))
                .collect::<String>()
        )
    }
}
