use crate::{Index, competitor::Competitor, drift::Drift, time::Time};

/// Dense Vec-backed store for competitor state in History.
///
/// Indexed directly by Index.0, eliminating HashMap hashing in the
/// forward/backward sweep. Uses `Vec<Option<Competitor<T, D>>>` so slots can be
/// absent without an explicit present mask.
#[derive(Debug)]
pub struct CompetitorStore<T: Time = i64, D: Drift<T> = crate::drift::ConstantDrift> {
    competitors: Vec<Option<Competitor<T, D>>>,
    n_present: usize,
}

impl<T: Time, D: Drift<T>> Default for CompetitorStore<T, D> {
    fn default() -> Self {
        Self {
            competitors: Vec::new(),
            n_present: 0,
        }
    }
}

impl<T: Time, D: Drift<T>> CompetitorStore<T, D> {
    pub fn new() -> Self {
        Self::default()
    }

    fn ensure_capacity(&mut self, idx: usize) {
        if idx >= self.competitors.len() {
            self.competitors.resize_with(idx + 1, || None);
        }
    }

    pub fn insert(&mut self, idx: Index, competitor: Competitor<T, D>) {
        self.ensure_capacity(idx.0);
        if self.competitors[idx.0].is_none() {
            self.n_present += 1;
        }
        self.competitors[idx.0] = Some(competitor);
    }

    pub fn get(&self, idx: Index) -> Option<&Competitor<T, D>> {
        self.competitors.get(idx.0).and_then(|slot| slot.as_ref())
    }

    pub fn get_mut(&mut self, idx: Index) -> Option<&mut Competitor<T, D>> {
        self.competitors
            .get_mut(idx.0)
            .and_then(|slot| slot.as_mut())
    }

    pub fn contains(&self, idx: Index) -> bool {
        self.get(idx).is_some()
    }

    pub fn len(&self) -> usize {
        self.n_present
    }

    pub fn is_empty(&self) -> bool {
        self.n_present == 0
    }

    pub fn iter(&self) -> impl Iterator<Item = (Index, &Competitor<T, D>)> {
        self.competitors
            .iter()
            .enumerate()
            .filter_map(|(i, slot)| slot.as_ref().map(|a| (Index(i), a)))
    }

    pub fn iter_mut(&mut self) -> impl Iterator<Item = (Index, &mut Competitor<T, D>)> {
        self.competitors
            .iter_mut()
            .enumerate()
            .filter_map(|(i, slot)| slot.as_mut().map(|a| (Index(i), a)))
    }

    pub fn values_mut(&mut self) -> impl Iterator<Item = &mut Competitor<T, D>> {
        self.competitors.iter_mut().filter_map(|s| s.as_mut())
    }
}

impl<T: Time, D: Drift<T>> std::ops::Index<Index> for CompetitorStore<T, D> {
    type Output = Competitor<T, D>;
    fn index(&self, idx: Index) -> &Competitor<T, D> {
        self.get(idx).expect("competitor not found at index")
    }
}

impl<T: Time, D: Drift<T>> std::ops::IndexMut<Index> for CompetitorStore<T, D> {
    fn index_mut(&mut self, idx: Index) -> &mut Competitor<T, D> {
        self.get_mut(idx).expect("competitor not found at index")
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{competitor::Competitor, drift::ConstantDrift};

    #[test]
    fn insert_then_get() {
        let mut store: CompetitorStore<i64, ConstantDrift> = CompetitorStore::new();
        let idx = Index(7);
        store.insert(idx, Competitor::default());
        assert!(store.contains(idx));
        assert_eq!(store.len(), 1);
        assert!(store.get(idx).is_some());
    }

    #[test]
    fn iter_in_index_order() {
        let mut store: CompetitorStore<i64, ConstantDrift> = CompetitorStore::new();
        store.insert(Index(2), Competitor::default());
        store.insert(Index(0), Competitor::default());
        store.insert(Index(5), Competitor::default());
        let keys: Vec<Index> = store.iter().map(|(i, _)| i).collect();
        assert_eq!(keys, vec![Index(0), Index(2), Index(5)]);
    }

    #[test]
    fn index_operator_works() {
        let mut store: CompetitorStore<i64, ConstantDrift> = CompetitorStore::new();
        store.insert(Index(3), Competitor::default());
        let _ = &store[Index(3)];
    }
}
