// 导入依赖项
use crate::error::{LsPlusError, Result}; // 错误处理类型
use serde::{Deserialize, Serialize}; // 序列化和反序列化支持
use std::collections::HashMap; // 哈希映射容器
use std::fs; // 文件系统操作
use std::path::{Path, PathBuf}; // 路径处理

/// 目录描述信息结构体
///
/// 存储单个目录的描述信息，包括路径、描述内容和时间戳。
/// 用于为项目中的目录提供自定义的说明信息，帮助用户理解目录的用途。
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DirectoryDescription {
    /// 目录的完整路径
    pub path: PathBuf,
    /// 目录的描述文本
    pub description: String,
    /// 描述创建时间
    pub created_at: chrono::DateTime<chrono::Local>,
    /// 描述最后更新时间
    pub updated_at: chrono::DateTime<chrono::Local>,
}

/// 描述数据库结构体
///
/// 管理所有目录描述的数据库，使用哈希映射来存储路径到描述的映射关系。
/// 支持序列化到 JSON 文件进行持久化存储。
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct DescriptionDatabase {
    /// 存储路径字符串到目录描述的映射
    /// Key: 目录的规范化路径字符串
    /// Value: 对应的目录描述信息
    pub descriptions: HashMap<String, DirectoryDescription>,
}

impl DescriptionDatabase {
    /// 创建新的空描述数据库
    ///
    /// # 返回值
    /// * `Self` - 新的空数据库实例
    pub fn new() -> Self {
        Self {
            descriptions: HashMap::new(), // 初始化为空的哈希映射
        }
    }

    /// 获取描述数据库文件路径
    ///
    /// 返回存储目录描述数据的 JSON 文件路径。
    /// 文件位于应用程序配置目录下的 descriptions.json。
    ///
    /// # 返回值
    /// * `Result<PathBuf>` - 数据库文件路径
    ///
    /// # 错误
    /// * 无法确定配置目录时返回错误
    pub fn database_file() -> Result<PathBuf> {
        let config_dir = crate::config::Config::config_dir()?; // 获取配置目录
        Ok(config_dir.join("descriptions.json")) // 拼接数据库文件名
    }

    /// 从文件加载描述数据库
    ///
    /// 从 JSON 文件中加载已保存的描述数据库。如果文件不存在，
    /// 则返回一个新的空数据库。
    ///
    /// # 返回值
    /// * `Result<Self>` - 成功时返回加载的数据库，失败时返回错误
    ///
    /// # 错误
    /// * 文件读取失败
    /// * JSON 格式解析失败
    pub fn load() -> Result<Self> {
        let db_file = Self::database_file()?;

        // 如果数据库文件不存在，返回空数据库
        if !db_file.exists() {
            return Ok(Self::new());
        }

        // 读取 JSON 文件内容
        let content = fs::read_to_string(&db_file)
            .map_err(|e| LsPlusError::system_error(format!("无法读取描述数据库: {e}")))?;

        // 解析 JSON 内容为数据库结构
        let database: Self = serde_json::from_str(&content)
            .map_err(|e| LsPlusError::format_error(format!("描述数据库格式错误: {e}")))?;

        Ok(database)
    }

    /// 保存描述数据库到文件
    ///
    /// 将当前数据库内容序列化为格式化的 JSON 并保存到文件。
    /// 如果配置目录不存在，会自动创建。
    ///
    /// # 返回值
    /// * `Result<()>` - 成功时返回空结果，失败时返回错误
    ///
    /// # 错误
    /// * 配置目录创建失败
    /// * JSON 序列化失败
    /// * 文件写入失败
    pub fn save(&self) -> Result<()> {
        let config_dir = crate::config::Config::config_dir()?;
        let db_file = Self::database_file()?;

        // 确保配置目录存在，如果不存在则创建
        if !config_dir.exists() {
            fs::create_dir_all(&config_dir)
                .map_err(|e| LsPlusError::system_error(format!("无法创建配置目录: {e}")))?;
        }

        // 将数据库序列化为格式化的 JSON 字符串
        let content = serde_json::to_string_pretty(self)
            .map_err(|e| LsPlusError::format_error(format!("无法序列化描述数据库: {e}")))?;

        // 将 JSON 内容写入文件
        fs::write(&db_file, content)
            .map_err(|e| LsPlusError::system_error(format!("无法保存描述数据库: {e}")))?;

        Ok(())
    }

    /// 为目录添加或更新描述
    ///
    /// 为指定路径的目录设置描述信息。如果该目录已有描述，则更新现有描述；
    /// 如果没有，则创建新的描述条目。
    ///
    /// # 参数
    /// * `path` - 目录路径，可以是相对路径或绝对路径
    /// * `description` - 要设置的描述文本
    ///
    /// # 返回值
    /// * `Result<()>` - 成功时返回空结果，失败时返回错误
    ///
    /// # 错误
    /// * 路径不存在或无法访问
    /// * 路径规范化失败
    ///
    /// # 功能说明
    /// - 使用规范化路径作为唯一标识符，避免相对路径和绝对路径的冲突
    /// - 自动更新时间戳，记录创建和最后修改时间
    pub fn set_description<P: AsRef<Path>>(&mut self, path: P, description: String) -> Result<()> {
        let path = path.as_ref();
        // 将路径规范化为绝对路径，解析符号链接
        let canonical_path = path
            .canonicalize()
            .map_err(|_e| LsPlusError::path_not_found(path.to_path_buf()))?;

        // 使用规范化路径的字符串形式作为哈希映射的键
        let path_key = canonical_path.to_string_lossy().to_string();
        let now = chrono::Local::now();

        if let Some(existing) = self.descriptions.get_mut(&path_key) {
            // 更新现有描述
            existing.description = description;
            existing.updated_at = now; // 更新修改时间
        } else {
            // 创建新的描述条目
            let desc = DirectoryDescription {
                path: canonical_path,
                description,
                created_at: now, // 记录创建时间
                updated_at: now, // 记录修改时间
            };
            self.descriptions.insert(path_key, desc);
        }

        Ok(())
    }

    /// 获取目录描述
    ///
    /// 根据给定路径查找对应的目录描述信息。
    ///
    /// # 参数
    /// * `path` - 要查找描述的目录路径
    ///
    /// # 返回值
    /// * `Option<&DirectoryDescription>` - 找到时返回描述的引用，否则返回 None
    ///
    /// # 查找逻辑
    /// - 首先将输入路径规范化为绝对路径
    /// - 如果路径规范化失败（如路径不存在），返回 None
    /// - 在描述数据库中查找匹配的条目
    pub fn get_description<P: AsRef<Path>>(&self, path: P) -> Option<&DirectoryDescription> {
        let path = path.as_ref();
        // 尝试规范化路径，如果失败则返回 None
        if let Ok(canonical_path) = path.canonicalize() {
            let path_key = canonical_path.to_string_lossy().to_string();
            self.descriptions.get(&path_key) // 在哈希映射中查找
        } else {
            None // 路径无效或不存在
        }
    }

    /// 删除目录描述
    ///
    /// 从数据库中删除指定路径的目录描述。
    ///
    /// # 参数
    /// * `path` - 要删除描述的目录路径
    ///
    /// # 返回值
    /// * `Result<bool>` - 成功时返回是否实际删除了条目，失败时返回错误
    ///   - `true`: 成功找到并删除了描述
    ///   - `false`: 指定路径没有对应的描述
    ///
    /// # 错误
    /// * 路径不存在或无法访问
    /// * 路径规范化失败
    pub fn remove_description<P: AsRef<Path>>(&mut self, path: P) -> Result<bool> {
        let path = path.as_ref();
        // 规范化路径以确保键的一致性
        let canonical_path = path
            .canonicalize()
            .map_err(|_e| LsPlusError::path_not_found(path.to_path_buf()))?;

        let path_key = canonical_path.to_string_lossy().to_string();
        // 尝试从哈希映射中删除条目，返回是否成功删除
        Ok(self.descriptions.remove(&path_key).is_some())
    }

    /// 列出所有描述
    ///
    /// 获取数据库中所有目录描述的列表，按路径排序。
    ///
    /// # 返回值
    /// * `Vec<&DirectoryDescription>` - 按路径排序的所有描述列表
    ///
    /// # 排序规则
    /// - 按目录路径的字典序排序
    /// - 确保输出的一致性和可预测性
    pub fn list_all(&self) -> Vec<&DirectoryDescription> {
        // 收集所有描述的引用
        let mut descriptions: Vec<&DirectoryDescription> = self.descriptions.values().collect();
        // 按路径排序，提供一致的显示顺序
        descriptions.sort_by(|a, b| a.path.cmp(&b.path));
        descriptions
    }

    /// 搜索描述（按描述内容和路径）
    ///
    /// 在所有目录描述中搜索包含指定查询字符串的条目。
    /// 搜索范围包括描述内容和目录路径。
    ///
    /// # 参数
    /// * `query` - 搜索查询字符串
    ///
    /// # 返回值
    /// * `Vec<&DirectoryDescription>` - 匹配的描述列表
    ///
    /// # 搜索规则
    /// - 不区分大小写的模糊匹配
    /// - 同时搜索描述内容和目录路径
    /// - 只要任一字段包含查询字符串就算匹配
    pub fn search_descriptions(&self, query: &str) -> Vec<&DirectoryDescription> {
        let query_lower = query.to_lowercase(); // 转为小写以实现不区分大小写搜索
        self.descriptions
            .values()
            .filter(|desc| {
                // 在描述内容中搜索
                desc.description.to_lowercase().contains(&query_lower) ||
                // 在路径中搜索
                desc.path.to_string_lossy().to_lowercase().contains(&query_lower)
            })
            .collect()
    }

    /// 清理不存在的目录描述
    ///
    /// 扫描数据库中的所有描述，删除那些指向不存在目录的条目。
    /// 这有助于保持数据库的整洁，避免无效的描述信息累积。
    ///
    /// # 返回值
    /// * `usize` - 被删除的无效描述条目数量
    ///
    /// # 清理逻辑
    /// - 遍历所有描述条目
    /// - 检查每个条目对应的目录路径是否仍然存在
    /// - 删除指向不存在目录的描述
    /// - 返回删除的条目数量
    pub fn cleanup_missing_directories(&mut self) -> usize {
        let mut to_remove = Vec::new();

        // 扫描所有描述，标记需要删除的条目
        for (key, desc) in &self.descriptions {
            if !desc.path.exists() {
                // 检查目录是否仍然存在
                to_remove.push(key.clone()); // 记录需要删除的键
            }
        }

        let removed_count = to_remove.len();
        // 删除所有标记的条目
        for key in to_remove {
            self.descriptions.remove(&key);
        }

        removed_count // 返回删除的条目数量
    }
}

/// 管理目录描述的主要接口
///
/// 这是应用程序与描述数据库交互的主要接口，提供了高级的描述管理功能。
/// 它封装了底层的数据库操作，并添加了额外的验证和便利功能。
///
/// # 功能特性
/// - 自动加载和保存数据库
/// - 路径和类型验证
/// - 事务性操作（操作后自动保存）
/// - 用户友好的错误处理
pub struct DescriptionManager {
    /// 底层的描述数据库实例
    database: DescriptionDatabase,
}

impl DescriptionManager {
    /// 创建新的描述管理器实例
    ///
    /// 自动从文件加载现有的描述数据库，如果数据库文件不存在，
    /// 则创建一个空的数据库。
    ///
    /// # 返回值
    /// * `Result<Self>` - 成功时返回管理器实例，失败时返回错误
    ///
    /// # 错误
    /// * 数据库文件读取失败
    /// * JSON 格式解析失败
    /// * 配置目录访问失败
    pub fn new() -> Result<Self> {
        let database = DescriptionDatabase::load()?; // 加载现有数据库或创建新的
        Ok(Self { database })
    }

    /// 为目录设置描述
    ///
    /// 为指定目录设置描述信息，包含完整的验证和自动保存功能。
    ///
    /// # 参数
    /// * `path` - 目录路径
    /// * `description` - 描述文本
    ///
    /// # 返回值
    /// * `Result<()>` - 成功时返回空结果，失败时返回错误
    ///
    /// # 错误
    /// * 路径不存在
    /// * 路径不是目录
    /// * 数据库保存失败
    ///
    /// # 验证规则
    /// - 路径必须存在
    /// - 路径必须是目录（不能是文件）
    /// - 设置成功后自动保存到文件
    pub fn set_description<P: AsRef<Path>>(&mut self, path: P, description: String) -> Result<()> {
        let path = path.as_ref();

        // 验证路径存在
        if !path.exists() {
            return Err(LsPlusError::path_not_found(path.to_path_buf()));
        }

        // 验证路径是目录而不是文件
        if !path.is_dir() {
            return Err(LsPlusError::format_error("只能为目录添加描述"));
        }

        // 在数据库中设置描述
        self.database.set_description(path, description)?;
        // 自动保存数据库到文件
        self.database.save()?;

        Ok(())
    }

    /// 获取目录描述
    ///
    /// 查找指定目录的描述信息。
    ///
    /// # 参数
    /// * `path` - 目录路径
    ///
    /// # 返回值
    /// * `Option<&DirectoryDescription>` - 找到时返回描述引用，否则返回 None
    pub fn get_description<P: AsRef<Path>>(&self, path: P) -> Option<&DirectoryDescription> {
        self.database.get_description(path)
    }

    /// 删除目录描述
    ///
    /// 删除指定目录的描述信息，并在成功删除时自动保存数据库。
    ///
    /// # 参数
    /// * `path` - 目录路径
    ///
    /// # 返回值
    /// * `Result<bool>` - 成功时返回是否实际删除了描述
    ///   - `true`: 找到并删除了描述
    ///   - `false`: 指定目录没有描述
    ///
    /// # 错误
    /// * 路径处理失败
    /// * 数据库保存失败
    pub fn remove_description<P: AsRef<Path>>(&mut self, path: P) -> Result<bool> {
        let removed = self.database.remove_description(path)?;
        // 只有在实际删除了描述时才保存数据库
        if removed {
            self.database.save()?;
        }
        Ok(removed)
    }

    /// 列出所有描述
    ///
    /// 获取数据库中所有目录描述的列表，按路径排序。
    ///
    /// # 返回值
    /// * `Vec<&DirectoryDescription>` - 所有描述的排序列表
    pub fn list_all(&self) -> Vec<&DirectoryDescription> {
        self.database.list_all()
    }

    /// 搜索描述
    ///
    /// 在所有描述中搜索包含指定查询字符串的条目。
    ///
    /// # 参数
    /// * `query` - 搜索查询字符串
    ///
    /// # 返回值
    /// * `Vec<&DirectoryDescription>` - 匹配的描述列表
    pub fn search(&self, query: &str) -> Vec<&DirectoryDescription> {
        self.database.search_descriptions(query)
    }

    /// 清理不存在的目录
    ///
    /// 删除所有指向不存在目录的描述条目，并在有删除时自动保存数据库。
    ///
    /// # 返回值
    /// * `Result<usize>` - 成功时返回删除的条目数量，失败时返回错误
    ///
    /// # 错误
    /// * 数据库保存失败
    pub fn cleanup(&mut self) -> Result<usize> {
        let removed_count = self.database.cleanup_missing_directories();
        // 只有在实际删除了条目时才保存数据库
        if removed_count > 0 {
            self.database.save()?;
        }
        Ok(removed_count)
    }
}

/// 处理描述相关的 CLI 命令
///
/// 这是描述功能的命令行接口处理函数，根据用户的操作类型执行相应的功能。
/// 支持设置、获取、删除、列出、搜索和清理描述等操作。
///
/// # 参数
/// * `action` - 用户请求的描述操作类型
///
/// # 返回值
/// * `crate::Result<()>` - 成功时返回空结果，失败时返回错误
///
/// # 支持的操作
/// - `Set`: 为目录设置描述
/// - `Get`: 获取目录的描述信息
/// - `Remove`: 删除目录的描述
/// - `List`: 列出所有描述
/// - `Search`: 搜索包含指定内容的描述
/// - `Cleanup`: 清理无效的描述条目
pub async fn handle_describe_command(action: crate::cli::DescribeAction) -> crate::Result<()> {
    use crate::cli::DescribeAction;
    use console::style;

    match action {
        // 设置目录描述
        DescribeAction::Set { path, description } => {
            let mut manager = DescriptionManager::new()?;
            manager.set_description(&path, description.clone())?;

            // 显示成功消息，使用颜色突出显示路径和描述
            println!(
                "✅ 已为目录 {} 设置描述: {}",
                style(path.display()).cyan(), // 目录路径用青色显示
                style(&description).green()   // 描述内容用绿色显示
            );
        }

        // 获取目录描述
        DescribeAction::Get { path } => {
            let manager = DescriptionManager::new()?;

            if let Some(desc) = manager.get_description(&path) {
                // 显示详细的描述信息，包括路径、内容和时间戳
                println!("📁 {}", style(&desc.path.display()).cyan().bold()); // 目录路径（加粗青色）
                println!("📝 {}", desc.description); // 描述内容
                println!(
                    "🕐 创建时间: {}",
                    desc.created_at.format("%Y-%m-%d %H:%M:%S")
                ); // 创建时间
                println!(
                    "🕑 更新时间: {}",
                    desc.updated_at.format("%Y-%m-%d %H:%M:%S")
                ); // 更新时间
            } else {
                // 没有找到描述时的提示
                println!("❌ 目录 {} 没有描述", style(path.display()).yellow());
            }
        }

        // 删除目录描述
        DescribeAction::Remove { path } => {
            let mut manager = DescriptionManager::new()?;

            if manager.remove_description(&path)? {
                // 成功删除描述
                println!("✅ 已删除目录 {} 的描述", style(path.display()).cyan());
            } else {
                // 没有找到可删除的描述
                println!("❌ 目录 {} 没有描述可删除", style(path.display()).yellow());
            }
        }

        // 列出所有描述
        DescribeAction::List => {
            let manager = DescriptionManager::new()?;
            let descriptions = manager.list_all();

            if descriptions.is_empty() {
                // 没有任何描述时的提示
                println!("📭 暂无目录描述");
            } else {
                // 显示所有描述的标题和数量
                println!("📋 所有目录描述 ({} 个):", descriptions.len());
                println!();

                // 遍历并显示每个描述
                for desc in descriptions {
                    println!("📁 {}", style(&desc.path.display()).cyan().bold()); // 目录路径
                    println!("   📝 {}", desc.description); // 描述内容（缩进显示）
                    println!("   🕐 {}", desc.updated_at.format("%Y-%m-%d %H:%M:%S")); // 更新时间
                    println!(); // 空行分隔
                }
            }
        }

        // 搜索描述
        DescribeAction::Search { query } => {
            let manager = DescriptionManager::new()?;
            let results = manager.search(&query);

            if results.is_empty() {
                // 没有找到匹配结果时的提示
                println!("🔍 未找到包含 \"{}\" 的描述", style(&query).yellow());
            } else {
                // 显示搜索结果的标题和数量
                println!("🔍 搜索结果 ({} 个):", results.len());
                println!();

                // 遍历并显示每个匹配的描述
                for desc in results {
                    println!("📁 {}", style(&desc.path.display()).cyan().bold()); // 目录路径

                    // 高亮显示匹配的文本（在描述中突出显示搜索词）
                    let highlighted_desc = highlight_search_term(&desc.description, &query);
                    println!("   📝 {highlighted_desc}"); // 高亮的描述内容
                    println!("   🕐 {}", desc.updated_at.format("%Y-%m-%d %H:%M:%S")); // 更新时间
                    println!(); // 空行分隔
                }
            }
        }

        // 清理无效描述
        DescribeAction::Cleanup => {
            let mut manager = DescriptionManager::new()?;
            let removed_count = manager.cleanup()?;

            if removed_count > 0 {
                // 有删除无效描述时的提示
                println!("✅ 已清理 {removed_count} 个不存在目录的描述");
            } else {
                // 没有需要清理的描述时的提示
                println!("✨ 没有需要清理的描述");
            }
        }
    }

    Ok(())
}

/// 在搜索结果中高亮显示匹配的文本
///
/// 在给定文本中查找搜索词并用颜色高亮显示，提供更好的视觉反馈。
/// 搜索是不区分大小写的，但会保持原文本的大小写格式。
///
/// # 参数
/// * `text` - 要进行高亮处理的原始文本
/// * `query` - 要高亮的搜索词
///
/// # 返回值
/// * `String` - 包含高亮标记的文本字符串
///
/// # 高亮规则
/// - 使用黄色粗体显示匹配的文本
/// - 保持原文本的大小写格式
/// - 如果没有找到匹配，返回原始文本
///
/// # 实现细节
/// - 使用不区分大小写的搜索来查找匹配位置
/// - 在找到的每个匹配位置应用终端颜色样式
/// - 保持非匹配部分的原始格式
fn highlight_search_term(text: &str, query: &str) -> String {
    let query_lower = query.to_lowercase(); // 转为小写用于搜索
    let mut result = String::new();
    let mut last_end = 0;

    // 查找所有匹配的位置
    for (start, part) in text.match_indices(&query_lower) {
        // 添加匹配前的普通文本
        result.push_str(&text[last_end..start]);

        // 添加高亮的匹配文本（黄色粗体）
        result.push_str(
            &console::style(&text[start..start + part.len()])
                .yellow()
                .bold()
                .to_string(),
        );

        last_end = start + part.len();
    }

    // 添加剩余的普通文本
    result.push_str(&text[last_end..]);

    // 如果没有找到任何匹配（可能因为大小写问题），返回原文本
    if result.is_empty() {
        text.to_string()
    } else {
        result
    }
}
