pub mod crystallized;
pub mod dormant;
pub mod immediate;
pub mod phantom;
pub mod resonant;

use crate::fact::{MemoFact, MemoryTier};
use crate::pruning::{archival, consolidation, decay, eviction};
use crate::retrieval::VaultQuery;
use anyhow::Result;

/// Top-level Vault that owns all five memory tiers.
#[derive(Default)]
pub struct Vault {
    pub immediate: immediate::ImmediateTrace,
    pub resonant: resonant::ResonantMemory,
    pub crystallized: crystallized::CrystallizedKnowledge,
    pub dormant: dormant::DormantArchive,
    pub phantom: phantom::PhantomCache,
}

impl Vault {
    pub fn new() -> Self {
        Self::default()
    }

    /// Load persisted tiers from `~/.bctx/vault/`.
    pub fn load_from_disk(&mut self) {
        let base = vault_base_dir();
        let crystallized_path = base.join("crystallized.json");
        let resonant_path = base.join("resonant.json");

        if let Ok(data) = std::fs::read_to_string(&crystallized_path) {
            if let Ok(facts) = serde_json::from_str::<Vec<MemoFact>>(&data) {
                for fact in facts {
                    self.crystallized.insert(fact);
                }
            }
        }
        if let Ok(data) = std::fs::read_to_string(&resonant_path) {
            if let Ok(facts) = serde_json::from_str::<Vec<MemoFact>>(&data) {
                for fact in facts {
                    self.resonant.insert(fact);
                }
            }
        }
    }

    /// Run all pruning passes: decay → consolidate → archive low-confidence → evict overflow.
    pub fn prune(&mut self) {
        // 1. Decay confidence on resonant and crystallized facts
        decay::apply_to_facts(self.resonant.facts_mut());
        decay::apply_to_facts(self.crystallized.facts_mut());

        // 2. Merge near-duplicate facts within resonant tier (Jaccard ≥ 0.85)
        consolidation::merge_duplicates(self.resonant.facts_mut(), 0.85);

        // 3. Archive resonant facts that fell below confidence threshold
        let resonant_facts = self.resonant.facts_mut();
        let dormant_facts = self.dormant.facts_mut();
        archival::archive_low_confidence(resonant_facts, dormant_facts);

        // 4. Evict dormant overflow (keep max 500)
        eviction::evict_to_limit(self.dormant.facts_mut(), 500);
    }

    /// Persist durable tiers to `~/.bctx/vault/`.
    pub fn flush_to_disk(&mut self) -> Result<()> {
        self.prune();
        let base = vault_base_dir();
        std::fs::create_dir_all(&base)?;
        let crystallized = serde_json::to_string_pretty(self.crystallized.all())?;
        std::fs::write(base.join("crystallized.json"), crystallized)?;
        let resonant_facts: Vec<&MemoFact> = self.resonant.all();
        let resonant = serde_json::to_string_pretty(&resonant_facts)?;
        std::fs::write(base.join("resonant.json"), resonant)?;
        Ok(())
    }

    pub fn insert(&mut self, fact: MemoFact) {
        match fact.tier {
            MemoryTier::Immediate => self.immediate.push(fact),
            MemoryTier::Resonant => self.resonant.insert(fact),
            MemoryTier::Crystallized => self.crystallized.insert(fact),
            MemoryTier::Dormant => self.dormant.insert(fact),
            MemoryTier::Phantom => self.phantom.insert(fact),
        }
    }

    pub fn query(&self, q: &VaultQuery) -> Vec<MemoFact> {
        let mut results = Vec::new();

        if q.tiers.is_empty() || q.tiers.contains(&MemoryTier::Immediate) {
            results.extend(self.immediate.search(&q.keyword));
        }
        if q.tiers.is_empty() || q.tiers.contains(&MemoryTier::Resonant) {
            results.extend(self.resonant.search(&q.keyword));
        }
        if q.tiers.is_empty() || q.tiers.contains(&MemoryTier::Crystallized) {
            results.extend(self.crystallized.search(&q.keyword));
        }
        if q.tiers.is_empty() || q.tiers.contains(&MemoryTier::Dormant) {
            results.extend(self.dormant.search(&q.keyword));
        }

        // Apply confidence floor
        if let Some(min) = q.min_confidence {
            results.retain(|f| f.confidence >= min);
        }

        results.truncate(q.top_k);
        results
    }
}

fn vault_base_dir() -> std::path::PathBuf {
    let home = std::env::var("HOME").unwrap_or_else(|_| ".".into());
    std::path::PathBuf::from(home).join(".bctx").join("vault")
}
