//! 跨会话学习：元数据追踪
//!
//! 追踪记忆使用频率，自动识别高价值记忆，优先展示重要记忆。
//! Phase 2 扩展：基于元数据的自动过期清理。

use crate::config::{ensure_data_dir, metadata_file};
use std::collections::HashMap;
use std::fs;

/// 单条记忆的元数据
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct MemoryMetadata {
    /// 内容指纹（MD5 哈希）
    pub fingerprint: String,

    /// 访问次数
    pub access_count: usize,

    /// 最后访问时间（ISO 8601 格式）
    pub last_accessed: String,

    /// 首次创建时间（ISO 8601 格式）
    pub first_created: String,
}

impl MemoryMetadata {
    /// 创建新的元数据
    pub fn new(fingerprint: String) -> Self {
        let now = chrono::Local::now().to_rfc3339();
        Self {
            fingerprint,
            access_count: 1,
            last_accessed: now.clone(),
            first_created: now,
        }
    }

    /// 记录一次访问
    pub fn track_access(&mut self) {
        self.access_count += 1;
        self.last_accessed = chrono::Local::now().to_rfc3339();
    }

    /// 判断是否为高价值记忆（访问次数 >= 5）
    pub fn is_high_value(&self) -> bool {
        self.access_count >= 5
    }
}

/// 元数据存储
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct MetadataStore {
    /// 指纹到元数据的映射
    memories: HashMap<String, MemoryMetadata>,
}

impl Default for MetadataStore {
    fn default() -> Self {
        Self::new()
    }
}

impl MetadataStore {
    /// 创建新的空存储
    pub fn new() -> Self {
        Self {
            memories: HashMap::new(),
        }
    }

    /// 从文件加载元数据
    ///
    /// 如果文件不存在或解析失败，返回空存储（降级处理）
    pub fn load() -> Self {
        let path = metadata_file();
        if !path.exists() {
            return Self::new();
        }

        let content = match fs::read_to_string(&path) {
            Ok(c) => c,
            Err(e) => {
                eprintln!("⚠️  读取元数据文件失败: {}，创建新存储（降级）", e);
                return Self::new();
            }
        };

        match serde_json::from_str(&content) {
            Ok(store) => store,
            Err(e) => {
                eprintln!("⚠️  解析元数据文件失败: {}，创建新存储（降级）", e);
                Self::new()
            }
        }
    }

    /// 保存元数据到文件
    pub fn save(&self) -> Result<(), std::io::Error> {
        let path = metadata_file();

        // 确保数据目录存在
        ensure_data_dir()?;

        let json = serde_json::to_string_pretty(self)
            .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;

        fs::write(&path, json)?;
        Ok(())
    }

    /// 追踪记忆访问
    ///
    /// 如果指纹不存在，会创建新的元数据（access_count = 1）
    pub fn track_access(&mut self, fingerprint: &str) {
        self.memories
            .entry(fingerprint.to_string())
            .and_modify(|m| m.track_access())
            .or_insert_with(|| MemoryMetadata::new(fingerprint.to_string()));
    }

    /// 获取高价值记忆列表（access_count >= 5）
    pub fn high_value_memories(&self) -> Vec<&MemoryMetadata> {
        self.memories
            .values()
            .filter(|m| m.is_high_value())
            .collect()
    }

    /// 获取记忆元数据
    pub fn get(&self, fingerprint: &str) -> Option<&MemoryMetadata> {
        self.memories.get(fingerprint)
    }

    /// 获取所有元数据
    pub fn all(&self) -> &HashMap<String, MemoryMetadata> {
        &self.memories
    }

    /// Phase 2 扩展：判断记忆是否应该保留（基于元数据的过期判断）
    ///
    /// 当前实现：总是返回 true（不过期）
    #[allow(dead_code)]
    pub fn should_keep_memory(&self, _fingerprint: &str) -> bool {
        true
    }
}

/// 计算内容指纹（使用 MD5 哈希）
///
/// 对内容进行归一化处理（去除首尾空格）后计算哈希
pub fn content_fingerprint(content: &str) -> String {
    use md5::{Digest, Md5};
    let normalized = content.trim();
    let mut hasher = Md5::new();
    hasher.update(normalized);
    format!("{:x}", hasher.finalize())
}

// ============ 单元测试 ============

#[cfg(test)]
mod tests {
    use super::*;
    use serial_test::serial;

    fn setup_test_dir(test_name: &str) -> std::path::PathBuf {
        let thread_id = format!("{:?}", std::thread::current().id());
        let temp_dir =
            std::env::temp_dir().join(format!("ai_memory_test_{}_{}", test_name, thread_id));
        std::fs::create_dir_all(&temp_dir).ok();
        crate::init(temp_dir.clone());
        temp_dir
    }

    #[test]
    fn test_metadata_file() {
        crate::config::reset();
        let file = metadata_file();
        assert!(file.to_str().unwrap().contains(".ai-memory/memories.meta.json"));
        assert!(file.is_absolute());
    }

    #[test]
    fn test_memory_metadata_new() {
        let metadata = MemoryMetadata::new("test-fingerprint".to_string());
        assert_eq!(metadata.fingerprint, "test-fingerprint");
        assert_eq!(metadata.access_count, 1);
        assert!(!metadata.last_accessed.is_empty());
        assert!(!metadata.first_created.is_empty());
    }

    #[test]
    fn test_memory_metadata_track_access() {
        let mut metadata = MemoryMetadata::new("test-fingerprint".to_string());
        assert_eq!(metadata.access_count, 1);

        metadata.track_access();
        assert_eq!(metadata.access_count, 2);

        metadata.track_access();
        metadata.track_access();
        metadata.track_access();
        assert_eq!(metadata.access_count, 5);
    }

    #[test]
    fn test_memory_metadata_is_high_value() {
        let mut metadata = MemoryMetadata::new("test-fingerprint".to_string());
        assert!(!metadata.is_high_value(), "初始访问次数为 1，不是高价值");

        for _ in 0..4 {
            metadata.track_access();
        }
        assert!(metadata.is_high_value(), "访问次数达到 5，是高价值");
    }

    #[test]
    fn test_metadata_store_new() {
        let store = MetadataStore::new();
        assert_eq!(store.all().len(), 0);
    }

    #[test]
    fn test_metadata_store_track_access() {
        let mut store = MetadataStore::new();

        store.track_access("fingerprint1");
        assert_eq!(store.all().len(), 1);
        assert_eq!(store.get("fingerprint1").unwrap().access_count, 1);

        store.track_access("fingerprint1");
        assert_eq!(store.get("fingerprint1").unwrap().access_count, 2);

        store.track_access("fingerprint2");
        assert_eq!(store.all().len(), 2);
    }

    #[test]
    fn test_metadata_store_high_value_memories() {
        let mut store = MetadataStore::new();

        for _ in 0..3 {
            store.track_access("low_value");
        }
        for _ in 0..5 {
            store.track_access("high_value");
        }
        for _ in 0..10 {
            store.track_access("very_high_value");
        }

        let high_value = store.high_value_memories();
        assert_eq!(high_value.len(), 2);
        assert!(high_value.iter().any(|m| m.fingerprint == "high_value"));
        assert!(high_value
            .iter()
            .any(|m| m.fingerprint == "very_high_value"));
        assert!(!high_value.iter().any(|m| m.fingerprint == "low_value"));
    }

    #[test]
    fn test_metadata_store_load_file_not_exist() {
        let temp_dir = setup_test_dir("meta_load");

        let store = MetadataStore::load();
        assert_eq!(store.all().len(), 0, "文件不存在时应返回空存储");

        std::fs::remove_dir_all(temp_dir).ok();
    }

    #[test]
    #[serial]
    fn test_metadata_store_save_and_load() {
        let temp_dir = setup_test_dir("meta_save_load");

        let mut store = MetadataStore::new();
        store.track_access("test1");
        store.track_access("test2");
        store.track_access("test1");
        assert!(store.save().is_ok());

        let loaded = MetadataStore::load();
        assert_eq!(loaded.all().len(), 2);
        assert_eq!(loaded.get("test1").unwrap().access_count, 2);
        assert_eq!(loaded.get("test2").unwrap().access_count, 1);

        std::fs::remove_dir_all(temp_dir).ok();
    }

    #[test]
    fn test_content_fingerprint() {
        let content1 = "  Test Content  ";
        let content2 = "Test Content";
        let content3 = "test content";

        let fp1 = content_fingerprint(content1);
        let fp2 = content_fingerprint(content2);
        let fp3 = content_fingerprint(content3);

        assert_eq!(fp1, fp2, "去除空格后应相同");
        assert_ne!(fp2, fp3, "大小写不同应产生不同指纹");
        assert_eq!(fp1.len(), 32);
    }

    #[test]
    fn test_metadata_store_should_keep_always_true() {
        let mut store = MetadataStore::new();
        store.track_access("test");
        assert!(store.should_keep_memory("test"), "Phase 1 所有记忆都应保留");
    }

    #[test]
    fn test_metadata_store_serialization() {
        let mut store = MetadataStore::new();
        store.track_access("test1");
        store.track_access("test2");

        let json = serde_json::to_string(&store).unwrap();
        assert!(json.contains("test1"));
        assert!(json.contains("test2"));

        let deserialized: MetadataStore = serde_json::from_str(&json).unwrap();
        assert_eq!(deserialized.all().len(), 2);
    }
}
