use anyhow::Result;
use chrono::Utc;

use crate::store::Store;

pub struct DecayConfig {
    pub rate_per_day: f32,
    pub min_confidence: f32,
    pub access_protection_factor: f32,
}

impl Default for DecayConfig {
    fn default() -> Self {
        Self {
            rate_per_day: 0.01,
            min_confidence: 0.05,
            access_protection_factor: 0.1,
        }
    }
}

pub fn apply_decay<S: Store>(store: &S, config: &DecayConfig) -> Result<u32> {
    let memories = store.memories_needing_decay(1)?;
    let mut updated = 0;

    for mut memory in memories {
        let days_since_access = Utc::now()
            .signed_duration_since(memory.last_accessed)
            .num_hours() as f32
            / 24.0;

        let protection = (memory.access_count as f32 * config.access_protection_factor).min(0.5);
        let effective_rate = config.rate_per_day * (1.0 - protection);
        let decay_amount = effective_rate * days_since_access;

        memory.confidence = (memory.confidence - decay_amount).max(config.min_confidence);
        // invariant: confidence decay is per-machine and time-driven, so it must
        // go through record_access and not enter the sync log.
        store.record_access(&memory)?;
        updated += 1;
    }

    Ok(updated)
}
