use chrono::{DateTime, Local};
use std::fs::{self, File, DirEntry};
use std::io::{self, BufRead, BufReader, Lines, Read, Write};
use std::vec::IntoIter;
use std::path::{Path, PathBuf};
use crate::Exception;

/// General file operations, such as reading and writing, directory creation, deletion, etc.
pub struct FileUtil;

impl FileUtil {
    /// 罗列指定路径下的子目录及文件。
    ///
    /// 参数 `path` 是要罗列内容的目标路径。
    /// 参数 `recurse` 决定是否以递归方式罗列子文件夹内的内容。
    ///
    /// 返回值是一个包含所有子目录及文件路径的 `Vec<String>`。
    /// 如果在读取目录时发生错误，将返回一个空的 `Vec`。
    pub fn list(path: &Path, recurse: bool) -> Vec<String> {
        let mut result = Vec::new();
        let entries = match fs::read_dir(path) {
            Ok(entries) => entries,
            Err(_) => return result,
        };

        for entry in entries {
            let entry = match entry {
                Ok(entry) => entry,
                Err(_) => continue,
            };
            let path = entry.path();
            if path.is_dir() {
                if recurse {
                    result.extend(Self::list(&path, true));
                }
                result.push(path.to_string_lossy().to_string());
            } else {
                result.push(path.to_string_lossy().to_string());
            }
        }
        result
    }

    pub fn list_files(path: &Path, recurse: bool) -> Vec<PathBuf> {
        let mut result = Vec::new();
        let entries = match fs::read_dir(path) {
            Ok(entries) => entries,
            Err(_) => return result,
        };

        for entry in entries {
            let entry = match entry {
                Ok(entry) => entry,
                Err(_) => continue,
            };
            let path = entry.path();
            if path.is_dir() {
                if recurse {
                    result.extend(Self::list_files(path.as_path(), true));
                }
            } else {
                result.push(path);
            }
        }
        result
    }

    /// 获取指定文件的元数据信息
    ///
    /// 该函数封装了标准库中的 fs::metadata 方法，用于获取文件的基本信息，
    /// 包括文件大小、创建时间、修改时间、文件类型等元数据。
    ///
    /// # 参数
    /// * `file_path` - 要获取元数据的文件路径字符串引用
    ///
    /// # 返回值
    /// 返回一个 io::Result<std::fs::Metadata> 类型：
    /// * 成功时返回包含文件元数据的 Metadata 对象
    /// * 失败时返回对应的 IO 错误信息
    ///
    /// # 错误处理
    /// 当文件不存在或没有访问权限时，会返回相应的 IO 错误
    pub fn metadata(file_path: &str) -> io::Result<std::fs::Metadata> {
        fs::metadata(file_path)
    }

    /// 获取指定文件的最后修改时间
    ///
    /// # 参数
    /// * `file_path` - 要查询的文件路径字符串引用
    ///
    /// # 返回值
    /// 返回 Result<String, io::Error> 类型：
    /// * 成功时返回格式化的时间字符串，格式为 "YYYY-MM-DD HH:MM:SS"
    /// * 失败时返回对应的 IO 错误
    ///
    /// # 错误处理
    /// 当文件不存在或无法访问时，会返回相应的 IO 错误
    pub fn last_midified(file_path: &str) -> io::Result<String> {
        // 获取文件元数据
        let metadata = fs::metadata(file_path)?;
        // 提取文件最后修改时间
        let last_modified = metadata.modified().unwrap();
        // 将系统时间转换为本地时间格式
        let last_modified: DateTime<Local> = last_modified.into();
        // 格式化时间为指定字符串格式并返回
        Ok(last_modified.format("%Y-%m-%d %H:%M:%S").to_string())
    }

    /// 读取本地文件内容并以UTF-8字符串形式返回。
    ///
    /// 参数 `path` 是要读取的文件路径。
    ///
    /// 如果文件成功打开并读取，将返回包含文件内容的 `String`。
    /// 如果在打开或读取文件时发生I/O错误，将返回对应的 `io::Error`。
    pub fn read_string(path: &Path) -> Result<String, io::Error> {
        let mut file = File::open(path)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        Ok(contents)
    }

    /// 读取本地文件内容并以迭代器形式返回。
    ///
    /// 迭代器返回每行内容。
    ///
    /// 参数 `file_path` 是要读取的文件路径。
    ///
    /// 如果文件成功打开并读取，将返回包含文件内容的 `Lines<BufReader<File>>`。
    /// 如果在打开或读取文件时发生I/O错误，将返回对应的 `io::Error`。
    /// 使用示例：
    /// ```rust
    /// let lines = FileUtil::read_string_by_iter("./hello.txt");
    /// let lines = lines.unwrap();
    /// for line in lines {
    ///         let line: String = line?;
    /// }
    /// ```
    pub fn read_string_by_iter(file_path: &str) -> Result<Lines<BufReader<File>>, io::Error> {
        let file = File::open(file_path)?;
        let reader = BufReader::new(file);
        let xx = reader.lines();
        Ok(xx)
    }

    /// 读取本地文件内容并以字节数组形式返回。
    ///
    /// 参数 `path` 是要读取的文件路径。
    ///
    /// 如果文件成功打开并读取，将返回包含文件内容的 `Vec<u8>`。
    /// 如果在打开或读取文件时发生I/O错误，将返回对应的 `io::Error`。
    pub fn read_bytes(path: &Path) -> Result<Vec<u8>, io::Error> {
        let mut file = File::open(path)?;
        let mut contents = Vec::new();
        file.read_to_end(&mut contents)?;
        Ok(contents)
    }

    /// 将给定的字符串内容以覆盖方式写入到指定路径的文本文件。
    ///
    /// 参数 `path` 是要写入的文件路径。
    /// 参数 `content` 是要写入文件的字符串内容。
    ///
    /// 如果文件成功创建并写入内容，将返回 `Ok(())`。
    /// 如果在创建或写入文件时发生I/O错误，将返回对应的 `io::Error`。
    pub fn write_string(path: &Path, content: String) -> io::Result<()> {
        let mut file = File::create(path)?;
        file.write_all(content.as_bytes())?;
        Ok(())
    }

    /// 将给定的字符串内容以追加方式写入到指定路径的文件。
    ///
    /// 参数 `path` 是要写入的文件路径。
    /// 参数 `content` 是要追加到文件的字符串内容。
    ///
    /// 如果文件成功打开并追加内容，将返回 `Ok(())`。
    /// 如果在打开或写入文件时发生I/O错误，将返回对应的 `io::Error`。
    pub fn append_string(path: &Path, content: String) -> io::Result<()> {
        let mut file = File::options().append(true).open(path)?;
        file.write_all(content.as_bytes())?;
        Ok(())
    }

    /// 将给定的字节数组内容写入到指定路径的文件。
    ///
    /// 参数 `path` 是要写入的文件路径。
    /// 参数 `bytes` 是要写入文件的字节数组内容。
    ///
    /// 如果文件成功创建并写入内容，将返回 `Ok(())`。
    /// 如果在创建或写入文件时发生I/O错误，将返回对应的 `io::Error`。
    pub fn write_bytes(path: &Path, bytes: &[u8]) -> io::Result<()> {
        let mut file = File::create(path)?;
        file.write_all(bytes)?;
        Ok(())
    }

    /// 创建目录及其所有必要的父目录
    pub fn create_dir_with_parents(dir_path: &str) -> io::Result<()> {
        fs::create_dir_all(dir_path)
    }

    /// 确保目录存在，如果不存在则进行创建
    pub fn ensure_dir_exists(dir_path: &str) -> io::Result<()> {
        if !Path::new(dir_path).exists() {
            fs::create_dir_all(dir_path)?;
        }
        Ok(())
    }

    /// 删除单个文件
    pub fn delete_file(file_path: &str) -> io::Result<()> {
        fs::remove_file(file_path)
    }

    /// 删除含有子文件的目录
    pub fn delete_directory(dir_path: &str) -> io::Result<()> {
        fs::remove_dir_all(dir_path)
    }

    /// 拷贝源目录到目标目录
    pub fn copy_dir(src: &Path, dest: &Path) -> io::Result<()> {
        // 创建目标目录（如果不存在）
        fs::create_dir_all(dest)?;

        // 遍历源目录中的所有条目
        for entry in fs::read_dir(src)? {
            let entry = entry?;
            let src_path = entry.path();
            let dest_path = dest.join(src_path.file_name().unwrap());

            if src_path.is_dir() {
                // 如果是目录，递归拷贝
                Self::copy_dir(&src_path, &dest_path)?;
            } else {
                // 如果是文件，直接拷贝
                fs::copy(&src_path, &dest_path)?;
            }
        }

        Ok(())
    }

    /// 拷贝单个文件，如果目标文件所在目录不存在则创建
    pub fn copy_file(src: &Path, dest: &Path) -> io::Result<()> {
        fs::create_dir_all(dest.parent().unwrap())?;
        fs::copy(src, dest)?;
        Ok(())
    }


    /// 使用glob模式匹配文件路径
    ///
    /// 该函数接收一个glob模式字符串，搜索匹配的文件路径，并返回路径字符串的向量。
    ///
    /// # 参数
    /// * `pattern` - glob模式字符串，用于匹配文件路径
    ///
    /// # 返回值
    /// 返回一个Result类型，成功时包含匹配到的文件路径字符串向量，失败时包含IO错误
    ///
    /// # 示例
    /// ```
    /// let files = FileUtil::glob("D:/*/sub_dir").unwrap();
    /// ```
    pub fn glob(pattern: &str) -> io::Result<Vec<String>> {
        let mut results = Vec::new();
        let r = glob::glob(pattern);
        if r.is_err() {
            return Err(io::Error::new(io::ErrorKind::Other, r.err().unwrap().to_string()));
        }
        // 遍历glob模式匹配到的所有路径
        for entry in r.unwrap() {
            match entry {
                Ok(path) => {
                    let tmp = path.into_os_string().into_string().unwrap();
                    let tmp = tmp.replace("\\", "/");
                    results.push(tmp);
                },
                Err(e) => println!("{:?}", e),
            }
        }
        Ok(results)
    }

    /// 递归遍历指定路径下的所有文件（不会把目录作为结果返回）
    /// 
    /// 该函数会递归遍历给定路径下的所有文件和子目录中的文件，
    /// 并将所有文件路径收集到一个字符串向量中返回。
    /// 
    /// # 参数
    /// * `path` - 需要遍历的根目录路径字符串
    /// 
    /// # 返回值
    /// * `Ok(Vec<String>)` - 包含所有文件路径的字符串向量，路径分隔符统一为正斜杠
    /// * `Err(Exception)` - 遍历过程中发生的错误
    /// 
    /// # 错误
    /// 当路径不存在或无法访问时，会返回相应的错误信息
    pub fn recursive_files(path: &str) -> Result<Vec<String>, Exception> {
        // 创建递归文件迭代器来遍历目录
        let file_iterator = RecursiveFileIterator::new(Path::new(path))?;
        
        // 将所有文件路径转换为统一格式的字符串并收集到向量中
        let results: Vec<String> = file_iterator.map(|item| {
            let t = item.as_os_str().to_os_string().into_string().unwrap();
            t.replace("\\", "/")
        }).collect();
        
        Ok(results)
    }

    pub fn absolute_path_str(path: &Path) -> Result<String, Exception> {
        let absolute_path = path.canonicalize()?;
        let path_str = absolute_path.to_str()
            .ok_or_else(|| std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                "路径包含非 UTF-8 字符"
            ))?;

        // 去除 Windows 的 \\?\ 前缀
        let path_str = if cfg!(windows) && path_str.starts_with(r"\\?\") {
            &path_str[4..]
        } else {
            path_str
        };

        let path_str = if path.is_dir() && !path.ends_with("/") {
            format!("{}/", path_str)
        } else {
            path_str.to_string()
        };

        Ok(path_str.replace("\\", "/"))
    }
}

/// 递归文件迭代器结构体
struct RecursiveFileIterator {
    // 当前目录的条目迭代器
    current_dir_iter: Option<IntoIter<DirEntry>>,
    // 待处理的子目录队列
    subdirs: Vec<PathBuf>,
}

impl RecursiveFileIterator {
    pub fn new(root: &Path) -> Result<Self, std::io::Error> {
        // 检查路径是否存在且是目录
        if !root.exists() || !root.is_dir() {
            return Err(std::io::Error::new(std::io::ErrorKind::NotFound, "目录不存在或不是目录"))
        }

        // 读取根目录并转换为迭代器
        let entries: Vec<DirEntry> = std::fs::read_dir(root)?
            .collect::<Result<Vec<DirEntry>, std::io::Error>>()?;

        Ok(Self {
            current_dir_iter: Some(entries.into_iter()),
            subdirs: Vec::new(),
        })  
    }
}

/// 实现迭代器trait
impl Iterator for RecursiveFileIterator {
    type Item = PathBuf;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            // 检查目录迭代器是否有内容
            if let Some(iter) = &mut self.current_dir_iter {
                if let Some(entry) = iter.next() {
                    let path = entry.path();
                    if path.is_dir() {
                        self.subdirs.push(path);
                    } else {
                        return Some(path);
                    }
                } else {
                    // 当前目录迭代器已耗尽，清楚它
                    self.current_dir_iter = None;
                }
            } else {
                // 当前目录迭代器已耗尽，尝试处理下一个子目录
                if let Some(subdir) = self.subdirs.pop() {
                    match std::fs::read_dir(&subdir) {
                        Ok(entries) => {
                            // 将目录条目转为迭代器
                            let entries:Vec<DirEntry> = entries.filter_map(|e| e.ok()).collect();
                            self.current_dir_iter = Some(entries.into_iter());
                        },
                        Err(e) => {
                            eprintln!("无法读取目录：{:?}: {}", subdir, e);
                        }
                    }
                } else {
                    // 没有更多子目录需要处理，迭代结束
                    return None;
                }
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    static ROOT_DIR: &str = "d:/tmp/";

    #[test]
    fn test_glob() {
        let xx = FileUtil::glob("D:/*/sub_dir").unwrap();
        println!("{:?}", xx);
    }

    #[test]
    fn test_list() {
        let temp_dir = Path::new(ROOT_DIR);
        let sub_dir = temp_dir.join("sub_dir");
        std::fs::create_dir(&sub_dir).expect("Failed to create sub dir");
        let file_path = sub_dir.join("test_file.txt");
        std::fs::File::create(&file_path).expect("Failed to create file");

        let result = FileUtil::list(temp_dir, true);
        assert!(result.contains(&file_path.to_string_lossy().to_string()));
    }

    #[test]
    fn test_read_string() -> io::Result<()> {
        let temp_path = Path::new(ROOT_DIR).join("temp_file.txt");
        println!("{:?}", temp_path);
        let mut temp_file = File::options()
            .write(true)
            .create(true)
            .open(temp_path.as_path())?;
        let content = "test content".to_string();
        temp_file
            .write_all(content.as_bytes())
            .expect("Failed to write to temp file");

        let result = FileUtil::read_string(temp_path.as_path());
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), content);
        Ok(())
    }

    #[test]
    fn test_read_bytes() {
        let temp_path = Path::new(ROOT_DIR).join("temp_file.txt");
        let mut temp_file = File::options()
            .write(true)
            .open(temp_path.as_path())
            .unwrap();
        let content = [1, 2, 3];
        temp_file
            .write_all(&content)
            .expect("Failed to write to temp file");

        let result = FileUtil::read_bytes(temp_path.as_path());
        assert!(result.is_ok());
    }

    #[test]
    fn test_write_string() {
        let temp_path = Path::new(ROOT_DIR).join("temp_file.txt");
        let _temp_file = File::options().write(true).open(&temp_path).unwrap();
        let content = "test write content".to_string();

        let result = FileUtil::write_string(&temp_path, content.clone());
        assert!(result.is_ok());

        let read_result = FileUtil::read_string(&temp_path);
        assert!(read_result.is_ok());
        assert_eq!(read_result.unwrap(), content);
    }

    #[test]
    fn test_append_string() {
        let temp_path = Path::new(ROOT_DIR).join("temp_file.txt");
        let _temp_file = File::open(&temp_path).unwrap();
        let initial_content = "initial content".to_string();
        let append_content = " appended content".to_string();
        FileUtil::write_string(&temp_path, initial_content.clone())
            .expect("Failed to write initial content");

        let append_result = FileUtil::append_string(&temp_path, append_content.clone());
        assert!(append_result.is_ok());

        let read_result = FileUtil::read_string(&temp_path);
        assert!(read_result.is_ok());
        assert_eq!(read_result.unwrap(), initial_content + &append_content);
    }

    #[test]
    fn test_write_bytes() {
        let temp_path = Path::new(ROOT_DIR).join("temp_file.txt");
        let _temp_file = File::open(&temp_path).unwrap();
        let content = [4, 5, 6];

        let result = FileUtil::write_bytes(&temp_path, &content);
        assert!(result.is_ok());

        let read_result = FileUtil::read_bytes(&temp_path);
        assert!(read_result.is_ok());
        assert_eq!(read_result.unwrap(), content);
    }

    #[test]
    fn test_write_bytes_and_read_bytes_with_invalid_path() {

        let results = FileUtil::recursive_files(r"D:\tmp\originalTifData\2025-02-24").unwrap();
        for r in results {
            println!("{}", r);
        }
    }
}
