//! Generative distillation replay buffer (L5).
//!
//! Named after the Nordic concept of the replay of past events, this buffer
//! stores compressed latent representations of memories for sleep-time distillation.

use serde::{Deserialize, Serialize};
use mimirs_core::DensityMemory;

/// Ring buffer of latent replay samples for generative distillation.
/// Stores DensityMemory states for manifold sharpening and synthetic replay.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GenerativeDistillationBuffer {
    /// Maximum buffer size.
    pub capacity: usize,
    /// Current number of samples.
    pub len: usize,
    /// Density memories representing latent states.
    pub samples: Vec<DensityMemory>,
}

impl GenerativeDistillationBuffer {
    /// Creates a new generative distillation buffer with the specified capacity.
    pub fn new(capacity: usize) -> Self {
        Self {
            capacity,
            len: 0,
            samples: Vec::with_capacity(capacity),
        }
    }

    /// Pushes a new sample into the buffer, replacing the oldest if at capacity.
    pub fn push(&mut self, sample: DensityMemory) {
        if self.samples.len() < self.capacity {
            self.samples.push(sample);
        } else {
            // Ring buffer behavior: replace oldest sample
            let idx = self.len % self.capacity;
            self.samples[idx] = sample;
        }
        self.len += 1;
    }

    /// Samples a batch of latent representations from the buffer using reservoir sampling logic.
    pub fn sample_batch(&self, n: usize) -> Vec<DensityMemory> {
        if self.samples.is_empty() {
            return Vec::new();
        }
        
        let count = n.min(self.samples.len());
        let mut results = Vec::with_capacity(count);
        
        // Use a deterministic stride for sampling to ensure diversity without full randomness
        let stride = (self.samples.len() / count).max(1);
        for i in 0..count {
            let idx = (i * stride + (self.len % stride)) % self.samples.len();
            results.push(self.samples[idx].clone());
        }
        
        results
    }

    /// Returns the average purity of the buffer.
    pub fn average_purity(&self) -> f32 {
        if self.samples.is_empty() {
            return 0.0;
        }
        self.samples.iter().map(|s| s.purity).sum::<f32>() / self.samples.len() as f32
    }
}
