// 导入依赖项
use crate::cli::{Cli, ColorOption, FormatOption}; // CLI 类型和选项
use crate::descriptions::DescriptionManager; // 目录描述管理器
use crate::error::Result; // 错误处理类型
use crate::fs::{EntryType, FileEntry}; // 文件系统条目类型
use chrono::{DateTime, Local}; // 时间处理
use console::{style, Color}; // 终端样式和颜色
use std::io::{self, Write}; // 输入输出和写入操作

/// 主要的文件条目渲染函数
///
/// 根据 CLI 中指定的格式选项，选择对应的渲染方式来显示文件列表。
/// 支持多种显示格式：默认、长格式、树形、网格和 JSON。
///
/// # 参数
/// * `entries` - 要显示的文件条目列表
/// * `cli` - CLI 配置，包含格式和其他显示选项
///
/// # 返回值
/// * `Result<()>` - 成功时返回空结果，失败时返回错误
pub async fn render_entries(entries: &[FileEntry], cli: &Cli) -> Result<()> {
    match cli.format {
        FormatOption::Default => render_default(entries, cli).await, // 默认格式
        FormatOption::Long => render_long(entries, cli).await,       // 长格式（详细信息）
        FormatOption::Tree => render_tree(entries, None).await,      // 树形格式
        FormatOption::Grid => render_grid(entries, cli).await,       // 网格格式
        FormatOption::Json => render_json(entries).await,            // JSON 格式
    }
}

/// 渲染树形格式的文件列表
///
/// 以层次结构显示文件和目录，类似于 tree 命令的输出。
/// 目前是一个简化的实现，未来可以扩展为真正的树形结构。
///
/// # 参数
/// * `entries` - 要显示的文件条目列表
/// * `_max_depth` - 最大深度（目前未使用）
///
/// # 返回值
/// * `Result<()>` - 成功时返回空结果
///
/// # TODO
/// - 实现真正的树形结构显示
/// - 支持深度限制
/// - 添加适当的缩进和连接符
pub async fn render_tree(entries: &[FileEntry], _max_depth: Option<usize>) -> Result<()> {
    // 简化的树形渲染
    for entry in entries {
        let icon = get_file_icon(&entry.file_type); // 获取文件图标
        let color = get_file_color(&entry.file_type, &entry.name); // 获取文件颜色

        // 使用简单的树形连接符和颜色显示
        println!("├── {} {}", icon, style(&entry.name).fg(color));
    }
    Ok(())
}

/// 渲染搜索结果
///
/// 显示文件搜索的结果，在正常的文件列表之前显示匹配数量。
///
/// # 参数
/// * `entries` - 搜索结果文件条目列表
/// * `cli` - CLI 配置，用于决定结果的显示格式
///
/// # 返回值
/// * `Result<()>` - 成功时返回空结果
pub async fn render_search_results(entries: &[FileEntry], cli: &Cli) -> Result<()> {
    // 显示搜索结果的数量
    println!("Found {} matches:", entries.len());
    // 使用正常的渲染方式显示结果
    render_entries(entries, cli).await
}

/// 渲染默认格式的文件列表
///
/// 类似于传统 ls 命令的标准输出格式，每行显示一个文件名。
/// 支持颜色、图标和目录描述显示。
///
/// # 参数
/// * `entries` - 要显示的文件条目列表
/// * `cli` - CLI 配置，包含颜色、图标等显示选项
///
/// # 返回值
/// * `Result<()>` - 成功时返回空结果
///
/// # 功能特性
/// - 支持文件图标显示
/// - 根据文件类型显示不同颜色
/// - 显示符号链接的目标
/// - 为目录显示自定义描述信息
async fn render_default(entries: &[FileEntry], cli: &Cli) -> Result<()> {
    // 检查是否应该使用颜色
    let use_colors = should_use_colors(&cli.color);
    // 尝试创建描述管理器，如果失败则为 None
    let desc_manager = DescriptionManager::new().ok();

    // 遍历每个文件条目并构建输出字符串
    for entry in entries {
        let mut output = String::new();

        // 如果启用了图标，添加文件类型图标
        if cli.icons {
            output.push_str(&get_file_icon(&entry.file_type));
            output.push(' ');
        }

        // 添加文件名，根据配置决定是否使用颜色
        if use_colors {
            let color = get_file_color(&entry.file_type, &entry.name);
            output.push_str(&style(&entry.name).fg(color).to_string());
        } else {
            output.push_str(&entry.name);
        }

        // 如果是符号链接，显示链接目标
        if entry.is_symlink {
            if let Some(ref target) = entry.symlink_target {
                output.push_str(" -> ");
                if use_colors {
                    // 链接目标使用暗淡的颜色显示
                    output.push_str(&style(target.display()).dim().to_string());
                } else {
                    output.push_str(&target.display().to_string());
                }
            }
        }

        // 为目录显示描述信息（如果有的话）
        if entry.file_type == EntryType::Directory {
            if let Some(ref manager) = desc_manager {
                if let Some(desc) = manager.get_description(&entry.path) {
                    if use_colors {
                        // 描述信息使用暗淡的斜体显示
                        output.push_str(&format!(
                            " {}",
                            style(format!("({})", desc.description)).dim().italic()
                        ));
                    } else {
                        output.push_str(&format!(" ({})", desc.description));
                    }
                }
            }
        }

        println!("{output}");
    }

    Ok(())
}

/// 渲染长格式的文件列表
///
/// 类似于 `ls -l` 命令的详细输出格式，显示文件的完整信息。
/// 包括权限、大小、修改时间、文件名等详细信息。
///
/// # 参数
/// * `entries` - 要显示的文件条目列表
/// * `cli` - CLI 配置，包含颜色、图标、人类可读格式等选项
///
/// # 返回值
/// * `Result<()>` - 成功时返回空结果
///
/// # 显示格式
/// ```
/// 权限       大小     时间        [图标] 文件名 [-> 链接目标]
/// rwxr-xr-x  1.2K    Jan 15 10:30  📁 Documents
/// rw-r--r--  156B    Jan 14 14:22  📄 README.md -> /path/to/real/readme
/// ```
///
/// # 功能特性
/// - 显示文件权限
/// - 支持人类可读的文件大小格式
/// - 显示修改时间
/// - 支持文件图标
/// - 显示符号链接目标
/// - 为目录显示描述信息（单独一行）
async fn render_long(entries: &[FileEntry], cli: &Cli) -> Result<()> {
    let use_colors = should_use_colors(&cli.color);
    let desc_manager = DescriptionManager::new().ok();

    for entry in entries {
        let mut output = String::new();

        // Permissions
        output.push_str(&entry.permissions);
        output.push(' ');

        // Size
        let size_str = if cli.human_readable {
            format_human_size(entry.size)
        } else {
            entry.size.to_string()
        };

        if use_colors {
            output.push_str(&style(format!("{size_str:>8}")).cyan().to_string());
        } else {
            output.push_str(&format!("{size_str:>8}"));
        }
        output.push(' ');

        // Modified time
        let time_str = format_time(&entry.modified);
        if use_colors {
            output.push_str(&style(&time_str).yellow().to_string());
        } else {
            output.push_str(&time_str);
        }
        output.push(' ');

        // Icon
        if cli.icons {
            output.push_str(&get_file_icon(&entry.file_type));
            output.push(' ');
        }

        // Name
        if use_colors {
            let color = get_file_color(&entry.file_type, &entry.name);
            output.push_str(&style(&entry.name).fg(color).to_string());
        } else {
            output.push_str(&entry.name);
        }

        // Symlink target
        if entry.is_symlink {
            if let Some(ref target) = entry.symlink_target {
                output.push_str(" -> ");
                if use_colors {
                    output.push_str(&style(target.display()).dim().to_string());
                } else {
                    output.push_str(&target.display().to_string());
                }
            }
        }

        println!("{output}");

        // 在长格式中为目录显示描述信息（独立一行）
        if entry.file_type == EntryType::Directory {
            if let Some(ref manager) = desc_manager {
                if let Some(desc) = manager.get_description(&entry.path) {
                    if use_colors {
                        println!("          📝 {}", style(&desc.description).dim().italic());
                    } else {
                        println!("          📝 {}", desc.description);
                    }
                }
            }
        }
    }

    Ok(())
}

/// 渲染网格格式的文件列表
///
/// 在多列中排列显示文件名，充分利用终端宽度。
/// 类似于传统 ls 命令在宽终端中的多列显示效果。
///
/// # 参数
/// * `entries` - 要显示的文件条目列表
/// * `cli` - CLI 配置，包含颜色和图标选项
///
/// # 返回值
/// * `Result<()>` - 成功时返回空结果
///
/// # 功能特性
/// - 自动计算列数以充分利用终端宽度
/// - 支持文件图标显示
/// - 根据文件类型显示不同颜色
/// - 自动填充空格以对齐列
async fn render_grid(entries: &[FileEntry], cli: &Cli) -> Result<()> {
    let use_colors = should_use_colors(&cli.color);
    let terminal_width = get_terminal_width();
    let max_name_len = entries.iter().map(|e| e.name.len()).max().unwrap_or(10);

    let column_width = max_name_len + 2;
    let columns = (terminal_width / column_width).max(1);

    for (i, entry) in entries.iter().enumerate() {
        let mut output = String::new();

        if cli.icons {
            output.push_str(&get_file_icon(&entry.file_type));
            output.push(' ');
        }

        if use_colors {
            let color = get_file_color(&entry.file_type, &entry.name);
            output.push_str(&style(&entry.name).fg(color).to_string());
        } else {
            output.push_str(&entry.name);
        }

        // Pad to column width
        let padding = column_width.saturating_sub(output.len());
        output.push_str(&" ".repeat(padding));

        print!("{output}");

        if (i + 1) % columns == 0 {
            println!();
        }
    }

    // Final newline if needed
    if entries.len() % columns != 0 {
        println!();
    }

    io::stdout().flush().unwrap();
    Ok(())
}

/// 渲染 JSON 格式的文件列表
///
/// 将文件信息序列化为格式化的 JSON 输出，便于程序化处理。
/// 包含文件的所有元数据信息。
///
/// # 参数
/// * `entries` - 要序列化的文件条目列表
///
/// # 返回值
/// * `Result<()>` - 成功时返回空结果，失败时返回序列化错误
///
/// # JSON 格式
/// ```json
/// [
///   {
///     "name": "文件名",
///     "path": "完整路径",
///     "size": 1024,
///     "modified": "2024-01-15T10:30:00Z",
///     "permissions": "rwxr-xr-x",
///     "type": "file|directory|symlink|other",
///     "is_hidden": false,
///     "is_symlink": false,
///     "symlink_target": null
///   }
/// ]
/// ```
async fn render_json(entries: &[FileEntry]) -> Result<()> {
    // 将每个文件条目转换为 JSON 对象
    let json_entries: Vec<serde_json::Value> = entries
        .iter()
        .map(|entry| {
            serde_json::json!({
                "name": entry.name,                    // 文件名
                "path": entry.path,                    // 完整路径
                "size": entry.size,                    // 文件大小（字节）
                "modified": entry.modified.to_rfc3339(), // ISO 8601 格式的修改时间
                "permissions": entry.permissions,      // 权限字符串
                "type": match entry.file_type {
                    EntryType::File => "file",
                    EntryType::Directory => "directory",
                    EntryType::Symlink => "symlink",
                    EntryType::Other => "other"
                },
                "is_hidden": entry.is_hidden,          // 是否为隐藏文件
                "is_symlink": entry.is_symlink,        // 是否为符号链接
                "symlink_target": entry.symlink_target // 符号链接目标（可为 null）
            })
        })
        .collect();

    // 将 JSON 数据序列化为格式化的字符串
    let output = serde_json::to_string_pretty(&json_entries).map_err(|e| {
        crate::error::LsPlusError::format_error(format!("JSON serialization failed: {e}"))
    })?;

    println!("{output}");
    Ok(())
}

/// 判断是否应该使用颜色输出
///
/// 根据用户的颜色配置决定是否在输出中使用颜色。
///
/// # 参数
/// * `color_option` - 颜色选项配置
///
/// # 返回值
/// * `bool` - 如果应该使用颜色返回 true
///
/// # 逻辑
/// - Always: 始终使用颜色
/// - Never: 从不使用颜色
/// - Auto: 仅在输出到终端（TTY）时使用颜色
fn should_use_colors(color_option: &ColorOption) -> bool {
    match color_option {
        ColorOption::Always => true,                         // 总是使用颜色
        ColorOption::Never => false,                         // 从不使用颜色
        ColorOption::Auto => atty::is(atty::Stream::Stdout), // 自动检测是否为 TTY
    }
}

/// 获取文件类型对应的图标
///
/// 根据文件类型返回相应的 emoji 图标。
///
/// # 参数
/// * `file_type` - 文件类型
///
/// # 返回值
/// * `String` - 对应的 emoji 图标字符串
///
/// # 图标映射
/// - 目录: 📁 (文件夹图标)
/// - 文件: 📄 (文档图标)
/// - 符号链接: 🔗 (链接图标)
/// - 其他: ❓ (问号图标)
fn get_file_icon(file_type: &EntryType) -> String {
    match file_type {
        EntryType::Directory => "📁".to_string(), // 文件夹图标
        EntryType::File => "📄".to_string(),      // 文档图标
        EntryType::Symlink => "🔗".to_string(),   // 链接图标
        EntryType::Other => "❓".to_string(),     // 问号图标（未知类型）
    }
}

/// 获取文件类型对应的颜色
///
/// 根据文件类型和文件名的特征决定显示颜色。
///
/// # 参数
/// * `file_type` - 文件类型
/// * `name` - 文件名，用于判断特殊情况（如隐藏文件、可执行文件）
///
/// # 返回值
/// * `Color` - 对应的颜色
///
/// # 颜色规则
/// - 目录: 蓝色
/// - 符号链接: 青色
/// - 隐藏文件: 黑色（暗淡）
/// - 可执行文件: 绿色
/// - 普通文件: 白色
/// - 其他类型: 紫红色
fn get_file_color(file_type: &EntryType, name: &str) -> Color {
    match file_type {
        EntryType::Directory => Color::Blue, // 目录使用蓝色
        EntryType::Symlink => Color::Cyan,   // 符号链接使用青色
        EntryType::File => {
            if name.starts_with('.') {
                Color::Black // 隐藏文件使用黑色（暗淡）
            } else if is_executable(name) {
                Color::Green // 可执行文件使用绿色
            } else {
                Color::White // 普通文件使用白色
            }
        }
        EntryType::Other => Color::Magenta, // 其他类型使用紫红色
    }
}

/// 检查文件是否可能是可执行的
///
/// 基于文件扩展名的简化检查。在真实实现中应该检查文件权限。
///
/// # 参数
/// * `name` - 文件名
///
/// # 返回值
/// * `bool` - 如果文件可能是可执行的返回 true
///
/// # 检查规则
/// - Windows 可执行文件: .exe, .bat
/// - Shell 脚本: .sh
/// - Unix 可执行文件: 无扩展名且不以点开头
fn is_executable(name: &str) -> bool {
    name.ends_with(".exe") ||   // Windows 可执行文件
    name.ends_with(".sh") ||    // Shell 脚本
    name.ends_with(".bat") ||   // Windows 批处理文件
    (!name.contains('.') && !name.starts_with('.')) // Unix 风格可执行文件
}

/// 将字节数格式化为人类可读的文件大小
///
/// 将原始的字节数转换为带单位的易读格式。
///
/// # 参数
/// * `size` - 文件大小（字节数）
///
/// # 返回值
/// * `String` - 格式化后的文件大小字符串
///
/// # 示例
/// - 1024 字节 → "1.0K"
/// - 1536 字节 → "1.5K"
/// - 1048576 字节 → "1.0M"
/// - 512 字节 → "512B"
///
/// # 单位
/// B (Bytes), K (KB), M (MB), G (GB), T (TB)
fn format_human_size(size: u64) -> String {
    const UNITS: &[&str] = &["B", "K", "M", "G", "T"]; // 单位数组
    let mut size = size as f64;
    let mut unit_index = 0;

    // 循环除以 1024，直到找到合适的单位
    while size >= 1024.0 && unit_index < UNITS.len() - 1 {
        size /= 1024.0;
        unit_index += 1;
    }

    // 如果是字节单位，显示整数；否则显示一位小数
    if unit_index == 0 {
        format!("{}B", size as u64) // 字节显示为整数
    } else {
        format!("{:.1}{}", size, UNITS[unit_index]) // 其他单位显示一位小数
    }
}

/// 格式化时间为用户友好的字符串
///
/// 将 DateTime 对象格式化为类似 ls 命令的时间显示格式。
///
/// # 参数
/// * `time` - 要格式化的时间
///
/// # 返回值
/// * `String` - 格式化后的时间字符串
///
/// # 格式
/// "%b %d %H:%M" - 例如: "Jan 15 14:30"
/// - %b: 简写月份名（Jan, Feb, ...）
/// - %d: 日期（两位数）
/// - %H:%M: 24小时格式的时间
fn format_time(time: &DateTime<Local>) -> String {
    time.format("%b %d %H:%M").to_string()
}

/// 获取终端宽度
///
/// 尝试获取当前终端的宽度，如果获取失败则使用默认值。
/// 主要用于网格格式的列数计算。
///
/// # 返回值
/// * `usize` - 终端宽度（字符数），默认为 80
fn get_terminal_width() -> usize {
    crossterm::terminal::size()
        .map(|(width, _)| width as usize) // 获取终端宽度
        .unwrap_or(80) // 如果获取失败，使用默认值 80
}

/// 简化的终端检测模块
///
/// 这是一个简化的 atty 功能实现，用于检测输出流是否连接到终端。
/// 在真实的实现中，应该使用专门的 atty crate 来准确检测 TTY。
mod atty {
    /// 输出流类型枚举
    pub enum Stream {
        /// 标准输出流
        Stdout,
    }

    /// 检查指定的流是否连接到终端（TTY）
    ///
    /// # 参数
    /// * `_stream` - 要检查的流类型（目前未使用）
    ///
    /// # 返回值
    /// * `bool` - 如果连接到终端返回 true
    ///
    /// # 注意
    /// 这是一个简化的实现，总是返回 true。
    /// 在真实实现中，应该检查 stdout 是否真的是一个 TTY 设备。
    ///
    /// # TODO
    /// - 实现真正的 TTY 检测逻辑
    /// - 考虑不同平台的差异（Unix vs Windows）
    /// - 处理重定向和管道的情况
    pub fn is(_stream: Stream) -> bool {
        // 简化实现 - 目前总是返回 true
        // 在真实实现中，应该检查 stdout 是否为 TTY
        true
    }
}
