use std::{collections::HashMap, time::Duration};

use bevy::prelude::*;
use serde::{Deserialize, Serialize};

use super::prelude::*;

#[derive(Debug, Resource, Serialize, Deserialize, Reflect)]
#[reflect(Resource)]
pub struct GameWorld {
    pub area: CoordRect,
    pub sites: HashMap<SiteId, Site>,
    pub travel_groups: HashMap<TravelGroupId, TravelGroup>,
}

impl Default for GameWorld {
    fn default() -> Self {
        Self {
            area: CoordRect::from_size(Coords::new(0, 0), Coords::new(32, 32)),
            sites: HashMap::new(),
            travel_groups: HashMap::new(),
        }
    }
}

impl GameWorld {
    pub fn generate_mock(defs: &Definitions) -> Self {
        let cave_def = defs.sites.get("CAVE").unwrap();

        let bandit_def = defs.creatures.get("BANDIT").unwrap();
        let spider_def = defs.creatures.get("SPIDER").unwrap();
        let cave = Site::new(
            cave_def.clone(),
            "Gorbo's cave",
            Coords::new(0, 0),
            vec![
                SiteArea::from_creatures(&vec![
                    Creature::generate_from_def(defs.creatures.get("DRAGON").unwrap().clone()),
                    Creature::generate_from_def(
                        defs.creatures.get("INDESTRUCTIBLE").unwrap().clone(),
                    ),
                ]),
                SiteArea::from_creatures(&vec![
                    Creature::generate_from_def(bandit_def.clone()),
                    Creature::generate_from_def(spider_def.clone()),
                ]),
                SiteArea::from_creatures(&vec![
                    Creature::generate_from_def(bandit_def.clone()),
                    Creature::generate_from_def(bandit_def.clone()),
                    Creature::generate_from_def(bandit_def.clone()),
                ]),
                SiteArea::from_creatures(&vec![
                    Creature::generate_from_def(spider_def.clone()),
                    Creature::generate_from_def(spider_def.clone()),
                ]),
            ],
        );

        let group = TravelGroup::new(
            vec![
                Creature::generate_from_def(bandit_def.clone()),
                Creature::generate_from_def(bandit_def.clone()),
                Creature::generate_from_def(bandit_def.clone()),
            ],
            Coords { x: 0, y: 0 },
            Some(WorldPoint::Coords(Coords { x: 0, y: 0 })),
            Some(WorldPoint::Site(cave.id())),
        );
        Self {
            sites: HashMap::from([(cave.id(), cave)]),
            travel_groups: HashMap::from([(group.id(), group)]),
            ..default()
        }
    }

    pub fn generate_random(defs: &Definitions) -> Self {
        let mut world = Self::default();

        const SITE_COUNT: usize = 30;
        let site_locations = world.area.pick_random_coords(SITE_COUNT);
        let site_locations = site_locations.chunks(SITE_COUNT / 3);

        for (chunk, locations) in site_locations.enumerate() {
            for (i, coords) in locations.iter().enumerate() {
                let mut site = match chunk {
                    0 => Site::generate_from_def(
                        defs.sites.get("DUNGEON").unwrap(),
                        format!("Dungeon #{}", i + 1),
                        *coords,
                    ),
                    1 => Site::generate_from_def(
                        defs.sites.get("CAVE").unwrap(),
                        format!("Cave #{}", i + 1),
                        *coords,
                    ),
                    _ => Site::generate_from_def(
                        defs.sites.get("BANDIT_CAMP").unwrap(),
                        format!("Bandit camp #{}", i + 1),
                        *coords,
                    ),
                };
                site.populate_randomly(&defs.creatures.values().collect::<Vec<&CreatureDef>>());
                world.insert_site(site);
            }
        }

        world
    }

    pub fn advance_time(&mut self, time: Duration) {
        for site in self.sites.values_mut() {
            site.advance_time(time);
        }
        for travel_group in self.travel_groups.values_mut() {
            travel_group.advance_time(time);
        }
    }

    pub fn insert_site(&mut self, site: Site) {
        self.sites.insert(site.id(), site);
    }
}
