use std::{fmt::Display, time::Duration};

use bevy::prelude::Reflect;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

use super::prelude::*;

#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, Reflect)]
pub struct TravelGroupId(Uuid);

impl TravelGroupId {
    pub fn generate() -> Self {
        Self(Uuid::new_v4())
    }
}

impl Display for TravelGroupId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "trav-{}", self.0)
    }
}

#[derive(Clone, Copy, Eq, PartialEq, Debug, Serialize, Deserialize, Reflect)]
pub enum WorldPoint {
    Coords(Coords),
    Site(SiteId),
}

impl Display for WorldPoint {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match *self {
            Self::Site(site_id) => write!(f, "[{site_id}]"),
            Self::Coords(coords) => write!(f, "{coords}"),
        }
    }
}

#[derive(PartialEq, Debug, Serialize, Deserialize, Reflect)]
pub struct TravelGroup {
    id: TravelGroupId,
    accumulated_movement: Kilometer,
    pub creatures: Vec<Creature>,
    pub position: Coords,
    pub origin: Option<WorldPoint>,
    pub destination: Option<WorldPoint>,
}

impl TravelGroup {
    pub fn new(
        creatures: Vec<Creature>,
        position: Coords,
        origin: Option<WorldPoint>,
        destination: Option<WorldPoint>,
    ) -> Self {
        Self {
            id: TravelGroupId::generate(),
            accumulated_movement: 0.,
            creatures,
            position,
            origin,
            destination,
        }
    }

    pub fn id(&self) -> TravelGroupId {
        self.id
    }

    /// The travel speed of the group in km/h
    pub fn speed(&self) -> Kilometer {
        // TODO: hard-coded, should depend on the creatures in the group
        5.0
    }

    pub fn advance_time(&mut self, time: Duration) {
        self.accumulated_movement += time.as_secs_f32() / 3600. * self.speed();
        // while self.accumulated_movement >= Self::minimum_simulated_distance() {
        //     self.inner_tick();
        //     self.accumulated_movement -= Self::minimum_simulated_distance();
        // }
    }

    pub fn insert(&mut self, creature: Creature) {
        self.creatures.push(creature)
    }

    pub fn remove(&mut self, id: CreatureId) {
        self.creatures.retain(|creature| creature.id() != id);
    }

    // fn inner_tick(&mut self) {

    // }
}

impl Display for TravelGroup {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "travel group from {origin} to {destination} [{id}] [",
            origin = self
                .origin
                .map_or("nowhere".to_string(), |point| format!("{point}")),
            destination = self
                .destination
                .map_or("nowhere".to_string(), |point| format!("{point}")),
            id = self.id
        )?;
        for creature in self.creatures.iter() {
            write!(f, "\n\t{creature}")?;
        }
        if !self.creatures.is_empty() {
            writeln!(f)?;
        }
        write!(f, "]")
    }
}
