// 导入依赖项
use crate::cli::{Cli, FileTypeFilter}; // CLI 类型和文件类型过滤器
use crate::error::{LsPlusError, Result}; // 错误处理类型
use chrono::{DateTime, Local}; // 时间处理
use std::fs; // 标准库文件系统操作
use std::path::{Path, PathBuf}; // 路径处理
use walkdir::WalkDir; // 递归目录遍历

/// 文件条目信息结构体
///
/// 表示文件系统中一个文件或目录的完整信息，包含了显示文件列表
/// 所需的所有元数据。这个结构体是整个应用程序中传递文件信息的
/// 核心数据结构。
#[derive(Debug, Clone)]
pub struct FileEntry {
    /// 文件或目录的完整路径
    pub path: PathBuf,
    /// 文件名（不包含路径）
    pub name: String,
    /// 文件大小（字节数）
    pub size: u64,
    /// 最后修改时间
    pub modified: DateTime<Local>,
    /// 文件权限的字符串表示（如 "rwxr-xr-x"）
    pub permissions: String,
    /// 文件类型（文件、目录、符号链接等）
    pub file_type: EntryType,
    /// 是否为隐藏文件（文件名以 . 开头）
    pub is_hidden: bool,
    /// 是否为符号链接
    pub is_symlink: bool,
    /// 如果是符号链接，指向的目标路径
    pub symlink_target: Option<PathBuf>,
}

/// 文件类型枚举
///
/// 定义了文件系统中不同类型的条目。
/// 这个枚举用于区分不同类型的文件系统对象，
/// 以便在显示时采用不同的颜色和图标。
#[derive(Debug, Clone, PartialEq)]
pub enum EntryType {
    /// 普通文件
    File,
    /// 目录文件夹
    Directory,
    /// 符号链接（软链接）
    Symlink,
    /// 其他类型（如设备文件、管道等）
    Other,
}

impl FileEntry {
    /// 从文件路径创建 FileEntry 实例
    ///
    /// 读取指定路径的文件或目录信息，并创建对应的 FileEntry。
    /// 这是创建文件条目的主要方法。
    ///
    /// # 参数
    /// * `path` - 文件或目录的路径
    /// * `follow_links` - 是否跟踪符号链接（true 表示读取链接指向的文件信息）
    ///
    /// # 返回值
    /// * `Result<Self>` - 成功时返回 FileEntry 实例，失败时返回错误
    ///
    /// # 错误
    /// * 文件不存在或无法访问
    /// * 无法读取文件元数据
    /// * 无法读取修改时间
    pub fn from_path(path: &Path, follow_links: bool) -> Result<Self> {
        // 根据 follow_links 参数决定如何读取元数据
        // - 如果跟踪链接，使用 metadata() 读取链接指向的文件信息
        // - 否则使用 symlink_metadata() 读取链接本身的信息
        let metadata = if follow_links {
            fs::metadata(path)
        } else {
            fs::symlink_metadata(path)
        }
        .map_err(|e| {
            LsPlusError::system_error(format!("Failed to read metadata for {path:?}: {e}"))
        })?;

        // 提取文件名（不包含路径部分）
        let name = path
            .file_name()
            .and_then(|n| n.to_str()) // 将 OsStr 转换为 &str
            .unwrap_or("") // 如果转换失败，使用空字符串
            .to_string();

        // 判断是否为隐藏文件（文件名以 . 开头）
        let is_hidden = name.starts_with('.');
        // 判断是否为符号链接
        let is_symlink = metadata.file_type().is_symlink();

        // 根据元数据确定文件类型
        let file_type = if metadata.is_dir() {
            EntryType::Directory // 目录
        } else if is_symlink {
            EntryType::Symlink // 符号链接
        } else if metadata.is_file() {
            EntryType::File // 普通文件
        } else {
            EntryType::Other // 其他类型（设备文件、管道等）
        };

        // 如果是符号链接，尝试读取链接目标
        let symlink_target = if is_symlink {
            fs::read_link(path).ok() // 读取失败时返回 None
        } else {
            None
        };

        // 获取文件的最后修改时间
        let modified = metadata
            .modified()
            .map_err(|e| {
                LsPlusError::system_error(format!("Failed to read modification time: {e}"))
            })?
            .into(); // 转换为 DateTime<Local>

        // 格式化文件权限字符串
        let permissions = format_permissions(&metadata);

        Ok(FileEntry {
            path: path.to_path_buf(),
            name,
            size: metadata.len(),
            modified,
            permissions,
            file_type,
            is_hidden,
            is_symlink,
            symlink_target,
        })
    }

    /// 检查文件是否匹配指定的文件类型过滤器
    ///
    /// 根据传入的过滤器判断当前文件条目是否符合过滤条件。
    /// 这个方法用于实现按文件类型过滤的功能。
    ///
    /// # 参数
    /// * `filter` - 文件类型过滤器
    ///
    /// # 返回值
    /// * `bool` - 如果文件匹配过滤器返回 true，否则返回 false
    pub fn matches_filter(&self, filter: &FileTypeFilter) -> bool {
        match filter {
            // 基本文件类型过滤
            FileTypeFilter::File => self.file_type == EntryType::File, // 普通文件
            FileTypeFilter::Dir => self.file_type == EntryType::Directory, // 目录
            FileTypeFilter::Link => self.file_type == EntryType::Symlink, // 符号链接

            // 特殊文件类型过滤（需要同时是文件并满足特定条件）
            FileTypeFilter::Exec => {
                self.file_type == EntryType::File && self.is_executable() // 可执行文件
            }
            FileTypeFilter::Image => {
                self.file_type == EntryType::File && self.is_image() // 图像文件
            }
            FileTypeFilter::Audio => {
                self.file_type == EntryType::File && self.is_audio() // 音频文件
            }
            FileTypeFilter::Video => {
                self.file_type == EntryType::File && self.is_video() // 视频文件
            }
            FileTypeFilter::Archive => {
                self.file_type == EntryType::File && self.is_archive() // 压缩档案文件
            }
            FileTypeFilter::Document => {
                self.file_type == EntryType::File && self.is_document() // 文档文件
            }
        }
    }

    /// 检查文件是否为可执行文件
    ///
    /// 这是一个简化的实现，主要基于文件扩展名判断。
    /// 在真实的实现中，应该检查文件的执行权限。
    ///
    /// # 返回值
    /// * `bool` - 如果文件可能是可执行的返回 true
    fn is_executable(&self) -> bool {
        // 简化的检查 - 在真实实现中应该检查文件权限
        self.name.ends_with(".exe") ||   // Windows 可执行文件
        self.name.ends_with(".sh") ||    // Shell 脚本
        self.name.ends_with(".bat") ||   // Windows 批处理文件
        !self.name.contains('.') // 无扩展名的文件（可能是 Unix 可执行文件）
    }

    /// 检查文件是否为图像文件
    ///
    /// 基于文件扩展名判断是否为常见的图像格式。
    ///
    /// # 返回值
    /// * `bool` - 如果是图像文件返回 true
    fn is_image(&self) -> bool {
        let extensions = ["jpg", "jpeg", "png", "gif", "bmp", "svg", "webp", "ico"];
        self.has_extension(&extensions)
    }

    /// 检查文件是否为音频文件
    ///
    /// 基于文件扩展名判断是否为常见的音频格式。
    ///
    /// # 返回值
    /// * `bool` - 如果是音频文件返回 true
    fn is_audio(&self) -> bool {
        let extensions = ["mp3", "wav", "flac", "ogg", "m4a", "aac", "wma"];
        self.has_extension(&extensions)
    }

    /// 检查文件是否为视频文件
    ///
    /// 基于文件扩展名判断是否为常见的视频格式。
    ///
    /// # 返回值
    /// * `bool` - 如果是视频文件返回 true
    fn is_video(&self) -> bool {
        let extensions = ["mp4", "avi", "mov", "mkv", "wmv", "flv", "webm", "m4v"];
        self.has_extension(&extensions)
    }

    /// 检查文件是否为压缩档案文件
    ///
    /// 基于文件扩展名判断是否为常见的压缩档案格式。
    ///
    /// # 返回值
    /// * `bool` - 如果是压缩档案文件返回 true
    fn is_archive(&self) -> bool {
        let extensions = ["zip", "tar", "gz", "bz2", "xz", "7z", "rar", "deb", "rpm"];
        self.has_extension(&extensions)
    }

    /// 检查文件是否为文档文件
    ///
    /// 基于文件扩展名判断是否为常见的文档格式。
    ///
    /// # 返回值
    /// * `bool` - 如果是文档文件返回 true
    fn is_document(&self) -> bool {
        let extensions = ["pdf", "doc", "docx", "txt", "md", "rst", "odt", "rtf"];
        self.has_extension(&extensions)
    }

    /// 检查文件是否具有指定的扩展名
    ///
    /// 辅助方法，用于检查文件扩展名是否在指定的列表中。
    /// 检查时不区分大小写。
    ///
    /// # 参数
    /// * `extensions` - 扩展名列表（不包含点号）
    ///
    /// # 返回值
    /// * `bool` - 如果文件扩展名在列表中返回 true
    fn has_extension(&self, extensions: &[&str]) -> bool {
        if let Some(ext) = self.path.extension().and_then(|e| e.to_str()) {
            // 将扩展名转为小写后在列表中查找
            extensions.contains(&ext.to_lowercase().as_str())
        } else {
            false // 文件没有扩展名
        }
    }
}

/// 列出指定目录中的文件和子目录
///
/// 这是获取目录内容的主要函数，支持各种过滤和排序选项。
/// 如果指定的路径是文件而非目录，则返回包含该文件信息的单元素向量。
///
/// # 参数
/// * `path` - 要列出的目录路径
/// * `cli` - CLI 配置，包含排序、过滤、隐藏文件等选项
///
/// # 返回值
/// * `Result<Vec<FileEntry>>` - 成功时返回文件条目列表，失败时返回错误
///
/// # 错误
/// * 路径不存在
/// * 权限不足，无法读取目录
/// * 文件系统错误
///
/// # 功能特性
/// - 根据 CLI 配置应用文件类型过滤
/// - 可选择显示或隐藏以 . 开头的隐藏文件
/// - 支持多种排序方式（文件名、修改时间）
/// - 支持正向或反向排序
/// - 可选择目录优先显示
pub async fn list_directory(path: &Path, cli: &Cli) -> Result<Vec<FileEntry>> {
    // 检查路径是否存在
    if !path.exists() {
        return Err(LsPlusError::path_not_found(path.to_path_buf()));
    }

    // 如果指定的路径是文件而非目录，直接返回该文件的信息
    if !path.is_dir() {
        let entry = FileEntry::from_path(path, cli.follow_links)?;
        return Ok(vec![entry]);
    }

    let mut entries = Vec::new();

    // 读取目录内容，处理可能的权限错误
    let dir_entries = fs::read_dir(path).map_err(|e| match e.kind() {
        std::io::ErrorKind::PermissionDenied => LsPlusError::permission_denied(path.to_path_buf()),
        _ => LsPlusError::system_error(format!("Failed to read directory {path:?}: {e}")),
    })?;

    // 遍历目录中的每个条目
    for entry in dir_entries {
        let entry = entry.map_err(|e| {
            LsPlusError::system_error(format!("Failed to read directory entry: {e}"))
        })?;
        let path = entry.path();

        // 尝试创建 FileEntry，如果失败则跳过该条目
        if let Ok(file_entry) = FileEntry::from_path(&path, cli.follow_links) {
            // 根据 CLI 配置决定是否跳过隐藏文件
            if !cli.all && file_entry.is_hidden {
                continue;
            }

            // 应用文件类型过滤器（如果指定了的话）
            if let Some(ref filter) = cli.file_type {
                if !file_entry.matches_filter(filter) {
                    continue;
                }
            }

            entries.push(file_entry);
        }
    }

    // 根据 CLI 配置对条目进行排序
    sort_entries(&mut entries, cli);

    Ok(entries)
}

/// 递归列出目录内容
///
/// 递归遍历指定目录及其所有子目录，返回所有找到的文件和目录。
/// 主要用于树形视图功能。
///
/// # 参数
/// * `path` - 要递归遍历的目录路径
/// * `cli` - CLI 配置
/// * `max_depth` - 最大递归深度，None 表示无限制
///
/// # 返回值
/// * `Result<Vec<FileEntry>>` - 成功时返回所有文件条目列表
///
/// # 错误
/// * 路径不存在
/// * 遍历过程中发生文件系统错误
pub async fn list_directory_recursive(
    path: &Path,
    cli: &Cli,
    max_depth: Option<usize>,
) -> Result<Vec<FileEntry>> {
    // 检查路径是否存在
    if !path.exists() {
        return Err(LsPlusError::path_not_found(path.to_path_buf()));
    }

    let mut entries = Vec::new();

    // 根据是否指定了深度限制来配置 WalkDir
    let walker = if let Some(depth) = max_depth {
        WalkDir::new(path).max_depth(depth) // 有深度限制
    } else {
        WalkDir::new(path) // 无限制递归
    };

    // 遍历所有找到的条目
    for entry in walker {
        let entry = entry.map_err(|e| LsPlusError::system_error(format!("Walk error: {e}")))?;
        let path = entry.path();

        // 尝试创建 FileEntry
        if let Ok(file_entry) = FileEntry::from_path(path, cli.follow_links) {
            // 根据 CLI 配置决定是否跳过隐藏文件
            if !cli.all && file_entry.is_hidden {
                continue;
            }

            // 应用文件类型过滤器
            if let Some(ref filter) = cli.file_type {
                if !file_entry.matches_filter(filter) {
                    continue;
                }
            }

            entries.push(file_entry);
        }
    }

    // 对条目进行排序
    sort_entries(&mut entries, cli);

    Ok(entries)
}

/// 搜索文件功能
///
/// 目前这是一个简化的实现，直接返回目录列表。
/// 在完整的实现中，这里应该实现模式匹配和文件名搜索功能。
///
/// # 参数
/// * `cli` - CLI 配置，包含搜索路径和过滤条件
///
/// # 返回值
/// * `Result<Vec<FileEntry>>` - 搜索结果列表
///
/// # TODO
/// - 实现模式匹配功能
/// - 支持正则表达式
/// - 支持大小写敏感性控制
pub async fn find_files(cli: &Cli) -> Result<Vec<FileEntry>> {
    // 目前直接返回目录列表
    // 在完整实现中，这里应该实现模式匹配功能
    list_directory(&cli.path, cli).await
}

/// 对文件条目进行排序
///
/// 根据 CLI 配置对文件列表进行排序。支持目录优先、
/// 按时间排序和反向排序。
///
/// # 参数
/// * `entries` - 要排序的文件条目列表（可变引用）
/// * `cli` - CLI 配置，包含排序选项
fn sort_entries(entries: &mut [FileEntry], cli: &Cli) {
    if cli.directories_first {
        // 目录优先排序：先按文件类型分组，然后在组内排序
        entries.sort_by(|a, b| {
            match (&a.file_type, &b.file_type) {
                // 两个都是目录，按正常规则排序
                (EntryType::Directory, EntryType::Directory) => compare_entries(a, b, cli),
                // 目录排在非目录之前
                (EntryType::Directory, _) => std::cmp::Ordering::Less,
                // 非目录排在目录之后
                (_, EntryType::Directory) => std::cmp::Ordering::Greater,
                // 两个都不是目录，按正常规则排序
                _ => compare_entries(a, b, cli),
            }
        });
    } else {
        // 正常排序，不区分文件类型
        entries.sort_by(|a, b| compare_entries(a, b, cli));
    }

    // 如果设置了反向排序，将整个列表反转
    if cli.reverse {
        entries.reverse();
    }
}

/// 比较两个文件条目的排序顺序
///
/// 根据 CLI 配置决定排序规则：按文件名或修改时间排序。
///
/// # 参数
/// * `a` - 第一个文件条目
/// * `b` - 第二个文件条目
/// * `cli` - CLI 配置，包含排序选项
///
/// # 返回值
/// * `std::cmp::Ordering` - 比较结果（Less、Equal、Greater）
fn compare_entries(a: &FileEntry, b: &FileEntry, cli: &Cli) -> std::cmp::Ordering {
    if cli.time {
        // 按修改时间排序
        a.modified.cmp(&b.modified)
    } else {
        // 按文件名排序（不区分大小写）
        a.name.to_lowercase().cmp(&b.name.to_lowercase())
    }
}

/// 格式化文件权限为字符串
///
/// 这是一个简化的实现，只根据文件类型返回固定的权限字符串。
/// 在完整的实现中，应该正确解析并格式化 Unix 文件权限。
///
/// # 参数
/// * `metadata` - 文件元数据
///
/// # 返回值
/// * `String` - 权限的字符串表示（如 "rwxr-xr-x"）
///
/// # TODO
/// - 实现真实的 Unix 权限解析
/// - 支持 Windows 文件属性
/// - 显示特殊权限位（setuid、setgid、sticky）
fn format_permissions(metadata: &fs::Metadata) -> String {
    // 简化的权限格式化
    // 在真实实现中应该正确格式化 Unix 权限
    if metadata.is_dir() {
        "drwxr-xr-x".to_string() // 目录的默认权限
    } else if metadata.is_file() {
        "rw-r--r--".to_string() // 文件的默认权限
    } else {
        "lrwxrwxrwx".to_string() // 符号链接的默认权限
    }
}
