use crate::fs::atomic_write;
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;

#[cfg(not(test))]
fn profiles_dir() -> PathBuf {
    dirs::home_dir()
        .unwrap_or_else(|| PathBuf::from("."))
        .join(".patheditor")
        .join("profiles")
}

#[cfg(test)]
fn profiles_dir() -> PathBuf {
    std::env::temp_dir().join("patheditor_test_profiles")
}

fn validate_profile_name(name: &str) -> Result<(), String> {
    if name.is_empty() {
        return Err("配置名称不能为空".into());
    }
    if name.len() > 255 {
        return Err("配置名称过长（最大 255 字符）".into());
    }
    if name.contains('\0') || name.chars().any(|c| c.is_control()) {
        return Err("配置名称包含非法字符".into());
    }
    if name.contains('/') || name.contains('\\') || name.contains("..") {
        return Err("配置名称包含非法字符".into());
    }
    for ch in name.chars() {
        if "<>:\"|?*".contains(ch) {
            return Err("配置名称包含非法字符".into());
        }
    }
    Ok(())
}

fn profile_path(name: &str) -> PathBuf {
    profiles_dir().join(format!("{}.json", name))
}

/// 内部用的 PathEntry（与前端 PathEntry 字段一致）
#[derive(Serialize, Deserialize, Clone)]
pub struct ProfilePathEntry {
    pub path: String,
    pub enabled: bool,
}

#[derive(Serialize, Deserialize)]
pub struct ProfileMeta {
    pub name: String,
    pub created: String,
    pub modified: String,
}

#[derive(Serialize, Deserialize)]
pub struct ProfileData {
    pub name: String,
    pub sys: Vec<ProfilePathEntry>,
    pub user: Vec<ProfilePathEntry>,
    pub created: String,
    pub modified: String,
}

/// 列出所有配置文件的元数据
pub fn list_profiles() -> Result<Vec<ProfileMeta>, String> {
    let dir = profiles_dir();
    if !dir.exists() {
        return Ok(vec![]);
    }

    let mut profiles: Vec<ProfileMeta> = Vec::new();
    let entries = fs::read_dir(&dir).map_err(|e| format!("无法读取配置目录: {}", e))?;

    for entry in entries.flatten() {
        let path = entry.path();
        if path.extension().is_none_or(|e| e != "json") {
            continue;
        }
        let content = match fs::read_to_string(&path) {
            Ok(c) => c,
            Err(_) => continue,
        };
        if let Ok(data) = serde_json::from_str::<ProfileData>(&content) {
            profiles.push(ProfileMeta {
                name: data.name,
                created: data.created,
                modified: data.modified,
            });
        }
    }

    profiles.sort_by(|a, b| a.name.cmp(&b.name));
    Ok(profiles)
}

/// 保存当前 PATH 为配置文件
pub fn save_profile(
    name: &str,
    sys: Vec<ProfilePathEntry>,
    user: Vec<ProfilePathEntry>,
) -> Result<(), String> {
    validate_profile_name(name)?;
    let dir = profiles_dir();
    fs::create_dir_all(&dir).map_err(|e| format!("无法创建配置目录: {}", e))?;

    let path = profile_path(name);
    let now = chrono::Local::now().format("%Y-%m-%dT%H:%M:%S").to_string();

    // 覆盖已有配置时保留原始创建时间
    let created = if path.exists() {
        fs::read_to_string(&path)
            .ok()
            .and_then(|c| serde_json::from_str::<ProfileData>(&c).ok())
            .map(|d| d.created)
            .unwrap_or_else(|| now.clone())
    } else {
        now.clone()
    };

    let data = ProfileData {
        name: name.to_string(),
        sys,
        user,
        created,
        modified: now,
    };

    let json =
        serde_json::to_string_pretty(&data).map_err(|e| format!("JSON 序列化失败: {}", e))?;
    atomic_write(&path, &json).map_err(|e| format!("无法写入配置文件: {}", e))?;

    log::info!("已保存配置: {}", path.display());
    Ok(())
}

/// 加载配置文件
pub fn load_profile(name: &str) -> Result<ProfileData, String> {
    validate_profile_name(name)?;
    let path = profile_path(name);
    if !path.exists() {
        return Err(format!("配置文件不存在: {}", name));
    }
    let content = fs::read_to_string(&path).map_err(|e| format!("无法读取配置文件: {}", e))?;
    serde_json::from_str(&content).map_err(|e| format!("JSON 解析失败: {}", e))
}

/// 删除配置文件
pub fn delete_profile(name: &str) -> Result<(), String> {
    validate_profile_name(name)?;
    let path = profile_path(name);
    fs::remove_file(&path).map_err(|e| format!("无法删除配置文件: {}", e))?;
    log::info!("已删除配置: {}", path.display());
    Ok(())
}

/// 重命名配置文件
pub fn rename_profile(old_name: &str, new_name: &str) -> Result<(), String> {
    validate_profile_name(old_name)?;
    validate_profile_name(new_name)?;
    let old_path = profile_path(old_name);
    let new_path = profile_path(new_name);
    if !old_path.exists() {
        return Err(format!("配置文件不存在: {}", old_name));
    }
    if old_path != new_path && new_path.exists() {
        return Err(format!("目标配置名已存在: {}", new_name));
    }

    let mut data: ProfileData = serde_json::from_str(
        &fs::read_to_string(&old_path).map_err(|e| format!("无法读取配置文件: {}", e))?,
    )
    .map_err(|e| format!("JSON 解析失败: {}", e))?;

    data.name = new_name.to_string();
    data.modified = chrono::Local::now().format("%Y-%m-%dT%H:%M:%S").to_string();

    let json =
        serde_json::to_string_pretty(&data).map_err(|e| format!("JSON 序列化失败: {}", e))?;
    atomic_write(&new_path, &json).map_err(|e| format!("无法写入配置文件: {}", e))?;

    if old_path != new_path {
        fs::remove_file(&old_path).map_err(|e| format!("无法删除旧配置文件: {}", e))?;
    }

    log::info!("已重命名配置: {} -> {}", old_name, new_name);
    Ok(())
}

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

    fn test_entry(path: &str) -> ProfilePathEntry {
        ProfilePathEntry {
            path: path.into(),
            enabled: true,
        }
    }

    #[test]
    fn validate_name_rejects_empty() {
        assert!(validate_profile_name("").is_err());
    }

    #[test]
    fn validate_name_rejects_path_traversal() {
        assert!(validate_profile_name("../../evil").is_err());
        assert!(validate_profile_name("foo\\bar").is_err());
    }

    #[test]
    fn validate_name_rejects_reserved_chars() {
        assert!(validate_profile_name("foo:bar").is_err());
        assert!(validate_profile_name("foo<bar").is_err());
    }

    #[test]
    fn validate_name_rejects_null_bytes() {
        assert!(validate_profile_name("foo\0bar").is_err());
    }

    #[test]
    fn validate_name_rejects_control_chars() {
        assert!(validate_profile_name("foo\tbar").is_err());
        assert!(validate_profile_name("foo\nbar").is_err());
    }

    #[test]
    fn validate_name_rejects_too_long() {
        let long_name = "a".repeat(256);
        assert!(validate_profile_name(&long_name).is_err());
    }

    #[test]
    fn validate_name_accepts_255_chars() {
        let name = "a".repeat(255);
        assert!(validate_profile_name(&name).is_ok());
    }

    #[test]
    fn profile_crud() {
        // save -> load -> delete
        let name = "__test_profile_crud";
        let _ = delete_profile(name);
        save_profile(
            name,
            vec![test_entry("C:\\sys")],
            vec![test_entry("D:\\usr")],
        )
        .unwrap();
        let loaded = load_profile(name).unwrap();
        assert_eq!(loaded.sys[0].path, "C:\\sys");
        delete_profile(name).unwrap();
        assert!(load_profile(name).is_err());

        // rename
        let old_name = "__test_rename_old";
        let new_name = "__test_rename_new";
        let _ = delete_profile(old_name);
        let _ = delete_profile(new_name);
        save_profile(old_name, vec![test_entry("C:\\x")], vec![]).unwrap();
        rename_profile(old_name, new_name).unwrap();
        assert!(load_profile(old_name).is_err());
        let renamed = load_profile(new_name).unwrap();
        assert_eq!(renamed.name, new_name);
        delete_profile(new_name).unwrap();

        // list
        let _ = delete_profile("__test_list_a");
        let _ = delete_profile("__test_list_b");
        save_profile("__test_list_a", vec![], vec![]).unwrap();
        save_profile("__test_list_b", vec![], vec![]).unwrap();
        let list = list_profiles().unwrap();
        let names: Vec<&str> = list.iter().map(|m| m.name.as_str()).collect();
        assert!(names.contains(&"__test_list_a"));
        delete_profile("__test_list_a").unwrap();
        delete_profile("__test_list_b").unwrap();
    }
}
