use std::{collections::HashMap, fs::{self}, hash::{DefaultHasher, Hash, Hasher}, path::PathBuf, str::FromStr, sync::{Arc, RwLock}};

use log::info;
use serde::{Deserialize, Serialize};
use tower_lsp::lsp_types::Url;

use crate::utils::{file_size_in_kb, get_last_modified_time, k_deserialize, k_serialize, to_escape_path};

use super::hdlparam::FastHdlparam;

pub enum CacheResult<T> {
    Ok(T),
    NotNeedCache,
    CacheNotFound
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct CacheItem {
    /// 文件名字
    pub file_name: String,
    /// 文件大小，单位 KB
    pub size: u64,
    /// 版本，根据特定算法得出
    pub version: String,
    /// 缓存文件的名字
    pub cache_name: String
}

impl Default for CacheItem {
    fn default() -> Self {
        CacheItem {
            file_name: "".to_string(),
            size: 0,
            version: "".to_string(),
            cache_name: "".to_string()
        }
    }
}


pub struct CacheInfo {
    // version
    pub version: Option<String>,
    /// 解析类型文件缓存的根目录
    pub parser_cache: Option<PathBuf>,
    /// 第三方 linter 文件缓存的根目录
    pub linter_cache: Option<PathBuf>,
}

/// 用于进行高效 IR 缓存的模块
pub struct CacheManager {
    /// 缓存文件夹根目录
    pub root_dir: PathBuf,
    pub cache_info: Arc<RwLock<CacheInfo>>,
    /// meta 文件内容
    pub meta_name: String,
    /// meta 内容
    pub meta: Arc<RwLock<HashMap<String, CacheItem>>>
}


impl CacheManager {
    pub fn new(root_dir: PathBuf) -> Self {
        let meta_name = "index.cache";
        let cache_info = Arc::new(RwLock::new(CacheInfo {
            version: None,
            parser_cache: None,
            linter_cache: None,
        }));

        CacheManager {
            root_dir,
            cache_info,
            meta_name: meta_name.to_string(),
            meta: Arc::new(RwLock::new(HashMap::<String, CacheItem>::new()))
        }
    }

    pub fn start(&self, version: &str) {
        let root_dir = &self.root_dir;
        let mut cache_info = self.cache_info.write().unwrap();
        cache_info.version = Some(version.to_string());

        let linter_cache = root_dir.join(version).join("lc");
        let parser_cache = root_dir.join(version).join("pc");

        // 如果不存在指定的缓存文件夹，则创建
        // ~/digital-ide/{版本号}/
        // 📁 lc： linter的cache
        // 📁 pc： parser的cache
        CacheManager::check_dir(&linter_cache);
        CacheManager::check_dir(&parser_cache);

        // 读入 meta 文件
        let meta_path = parser_cache.join(&self.meta_name);
        let meta = get_or_init_meta(&meta_path);
        let mut cache_meta = self.meta.write().unwrap();
        for (key, value) in meta.into_iter() {
            cache_meta.insert(key, value);
        }

        info!("缓存系统初始化完成，pc: {:?}, lc: {:?}", parser_cache, linter_cache);
        cache_info.parser_cache = Some(parser_cache);
        cache_info.linter_cache = Some(linter_cache);
    }

    fn check_dir(dir: &PathBuf) {
        if !dir.exists() {
            let _ = fs::create_dir_all(dir);
        }
    }

    pub fn is_big_file(path: &PathBuf) -> bool {
        if let Ok(size) = file_size_in_kb(path.to_str().unwrap()) {
            return size >= 1024;
        }

        false
    }

    #[allow(unused)]
    pub fn uri_is_big_file(uri: &Url) -> bool {
        let path = PathBuf::from_str(uri.path()).unwrap();
        let path = to_escape_path(&path);
        if let Ok(size) = file_size_in_kb(path.to_str().unwrap()) {
            return size >= 1024;
        }

        false
    }

    fn get_version(&self, path: &PathBuf) -> String {
        let path_str = path.to_str().unwrap();
        if let Ok(modify_ts) = get_last_modified_time(path_str) {
            return modify_ts.to_string();
        }
        "".to_string()
    }

    /// ## 说明
    /// 尝试获取当前的路径文件的 fast 的缓存
    /// 
    /// ## None
    /// 
    /// * 如果本来就没有缓存
    /// * 缓冲的 version 对不上
    pub fn try_get_fast_cache(&self, path: &PathBuf) -> CacheResult<FastHdlparam> {
        if !CacheManager::is_big_file(path) {
            return CacheResult::NotNeedCache
        }
        let path_string = path.to_str().unwrap();
        let meta_handle = self.meta.read().unwrap();
        if let Some(cache_item) = meta_handle.get(path_string) {
            let current_version = self.get_version(path);
            let cache_path = self.root_dir.join(&cache_item.cache_name);
            if current_version == cache_item.version && cache_path.exists() {
                // hit cache
                if let Ok(fast) = k_deserialize::<FastHdlparam>(&cache_path) {
                    return CacheResult::Ok(fast);
                }
            }
        }
        CacheResult::CacheNotFound
    }

    pub fn get_uuid_cache_name(&self, path_string: String) -> String {
        // 采用 sip 哈希算法计算哈希
        let mut hasher = DefaultHasher::new();
        path_string.hash(&mut hasher);
        let hash_value = hasher.finish();
        let hash_string = format!("{:x}", hash_value);
        let mut hash_string: String = hash_string.chars().take(32).collect();
        hash_string.push_str(".cache");
        hash_string
    }

    /// 更新缓存，并写入磁盘
    pub fn update_cache(&self, path: &PathBuf, fast: FastHdlparam) {
        let path_string = path.to_str().unwrap();
        let version = self.get_version(path);
        let size = file_size_in_kb(path_string).unwrap();
        let file_name = path.file_name().unwrap().to_str().unwrap().to_string();
        let cache_name = self.get_uuid_cache_name(path_string.to_string());
        let cache_item = CacheItem {
            file_name,
            version,
            size,
            cache_name: cache_name.clone()
        };
        
        let mut meta_handle = self.meta.write().unwrap();
        meta_handle.insert(path_string.to_string(), cache_item);

        let cache_info = self.cache_info.read().unwrap();
        
        // 准备必要的独立数据塞入线程进行调度
        if let Some(parser_cache) = &cache_info.parser_cache {
            let meta = (&*meta_handle).clone();
            let meta_save_path = parser_cache.join(&self.meta_name);
            let cache_save_path = parser_cache.join(cache_name);
            
            std::thread::spawn(move || {
                info!("save meta to {meta_save_path:?}");
                let _ = k_serialize(&meta_save_path, meta);
    
                info!("save index to {cache_save_path:?}");
                let _ = k_serialize(&cache_save_path, fast);
            });
        }
    }

}



fn get_or_init_meta(meta_path: &PathBuf) -> HashMap<String, CacheItem> {
    if meta_path.exists() {
        // 读取文件
        if let Ok(meta_map) = k_deserialize::<HashMap<String, CacheItem>>(meta_path) {
            return meta_map;
        }
    }

    // 重新创建新的
    HashMap::<String, CacheItem>::new()
}