use alloc::collections::BTreeMap;

use crate::ecs::{Entity, World};

pub struct NamedId(pub &'static str);

pub struct IdMap {
    map: BTreeMap<&'static str, Entity>,
}

impl IdMap {
    pub fn new() -> Self {
        Self {
            map: BTreeMap::new(),
        }
    }

    pub fn insert(&mut self, id: &'static str, entity: Entity) {
        self.map.insert(id, entity);
    }

    pub fn get(&self, id: &'static str) -> Option<Entity> {
        self.map.get(id).copied()
    }

    pub fn remove(&mut self, id: &'static str) -> Option<Entity> {
        self.map.remove(id)
    }
}

impl Default for IdMap {
    fn default() -> Self {
        Self::new()
    }
}

impl World {
    pub fn find_by_id(&self, id: &'static str) -> Option<Entity> {
        self.resource::<IdMap>().and_then(|m| m.get(id))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::ecs::World;

    fn fresh() -> (World, Entity, Entity) {
        let mut w = World::new();
        let a = w.spawn();
        let b = w.spawn();
        (w, a, b)
    }

    #[test]
    fn insert_then_get() {
        let (_, a, _) = fresh();
        let mut m = IdMap::new();
        m.insert("hero", a);
        assert_eq!(m.get("hero"), Some(a));
    }

    #[test]
    fn missing_id_returns_none() {
        let m = IdMap::new();
        assert_eq!(m.get("ghost"), None);
    }

    #[test]
    fn second_insert_overwrites() {
        let (_, a, b) = fresh();
        let mut m = IdMap::new();
        m.insert("slot", a);
        m.insert("slot", b);
        assert_eq!(m.get("slot"), Some(b));
    }

    #[test]
    fn remove_clears_entry() {
        let (_, a, _) = fresh();
        let mut m = IdMap::new();
        m.insert("hero", a);
        let removed = m.remove("hero");
        assert_eq!(removed, Some(a));
        assert_eq!(m.get("hero"), None);
    }

    #[test]
    fn remove_missing_returns_none() {
        let mut m = IdMap::new();
        assert_eq!(m.remove("ghost"), None);
    }

    #[test]
    fn world_find_by_id_returns_none_without_map() {
        let w = World::new();
        assert_eq!(w.find_by_id("anything"), None);
    }

    #[test]
    fn world_find_by_id_hits_resource() {
        let mut w = World::new();
        w.insert_resource(IdMap::new());
        let e = w.spawn();
        if let Some(map) = w.resource_mut::<IdMap>() {
            map.insert("hero", e);
        }
        assert_eq!(w.find_by_id("hero"), Some(e));
        assert_eq!(w.find_by_id("ghost"), None);
    }
}
