mod vhdl;

#[allow(unused)]
const TESTFILES_DIR: &str = "/home/dide/project/digital-lsp-server/testfiles";

#[allow(unused)]
const DIGTIAL_IDE_TEST: &str = "/home/dide/project/Digital-Test/Digital-IDE-test/user";

#[allow(unused)]
const MIPS_DESIGN_TEST: &str = "/home/dide/project/Digital-Test/MipsDesign";

#[allow(unused)]
const TEST_FILE: &str = "/home/dide/project/Digital-Test/MipsDesign/src/MyCpu.v";

#[allow(unused)]
const INCOMPLETE_EXAMPLE: &str = "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/incomplete-example";

#[allow(unused)]
const DIGTIAL_IDE_FACTORY: &str = "/home/dide/project/Digital-Test/Digital-IDE-test/user/factory";

#[allow(unused)]
const TESTFILES_TEMP_DIR: &str = "/home/dide/project/Digital-Test/Digital-IDE-temp";

#[allow(unused)]
macro_rules! unwrap_result {
    ($expr:expr) => {
        match $expr {
            Ok(e) => e,
            Err(_) => return
        }
    };
}

#[allow(unused)]
macro_rules! unwrap_option {
    ($expr:expr) => {
        match $expr {
            Some(e) => e,
            None => return
        }
    };
}

#[cfg(test)]
mod test_fast {
    use std::{env, fs, path::Path};

    use crate::core::sv_parser::sv_parser;
    use super::*;

    #[test]
    fn test_mycpu() {
        let hdlparam = sv_parser("/home/dide/project/Digital-Test/Digital-IDE-test/user/src/svlog/tools/ivtest/undef.sv");
        // println!("{hdlparam:?}");
        assert!(hdlparam.is_some());
    }

    #[test]
    fn test_parent() {
        let fast = sv_parser("/home/dide/project/Digital-Test/Digital-IDE-temp/user/src/svlog/std2017/dependence/parent.sv");
        let fast = fast.unwrap();
        println!("{:#?}", fast.content);
    }

    #[test]
    fn test_controller() {
        let fast = sv_parser("/home/dide/project/Digital-Test/MipsDesign/src/Controller/controller.v");
        let fast = fast.unwrap();
        println!("{:#?}", fast.content[0].params);
    }

    #[test]
    fn test_testfiles() {
        let ws_path = env::current_dir().unwrap();
        let entries = unwrap_result!(fs::read_dir(TESTFILES_DIR));
        for entry in entries {
            let entry = unwrap_result!(entry);
            let file_path = entry.path();
            if file_path.is_file() {
                let extension = unwrap_option!(file_path.extension());
                let file_path = unwrap_option!(file_path.to_str());
                if extension == "v" || extension == "sv" {

                    let file_path = ws_path.join(file_path);
                    let file_path = file_path.to_str().unwrap();
                    println!("Test file: {:?}", file_path);

                    let _ = sv_parser(file_path);
                }
            }
        }
    }

    // cargo test --release --package digital-lsp --lib -- test::test_fast::test_digital_ide_test --nocapture
    #[test]
    fn test_digital_ide_test() {
        // 判断路径是否存在且为文件夹
        let path = Path::new(DIGTIAL_IDE_TEST);
        if path.exists() && path.is_dir() {
            // 递归遍历文件夹
            if let Err(e) = traverse_directory(path) {
                eprintln!("Error: {}", e);
            }
        } else {
            eprintln!("Path does not exist or is not a directory");
        }
    }

    #[test]
    fn test_factory() {
        // 判断路径是否存在且为文件夹
        let path = Path::new(DIGTIAL_IDE_FACTORY);
        if path.exists() && path.is_dir() {
            // 递归遍历文件夹
            if let Err(e) = traverse_directory(path) {
                eprintln!("Error: {}", e);
            }
        } else {
            eprintln!("Path does not exist or is not a directory");
        }
    }

    #[test]
    fn test_mips_design() {
        // 判断路径是否存在且为文件夹
        let path = Path::new(MIPS_DESIGN_TEST);
        if path.exists() && path.is_dir() {
            // 递归遍历文件夹
            if let Err(e) = traverse_directory(path) {
                eprintln!("Error: {}", e);
            }
        } else {
            eprintln!("Path does not exist or is not a directory");
        }
    }

    #[test]
    fn test_incomplete_example() {
        let path = Path::new(INCOMPLETE_EXAMPLE);
        if path.exists() && path.is_dir() {
            // 递归遍历文件夹
            if let Err(e) = traverse_directory(path) {
                eprintln!("Error: {}", e);
            }
        } else {
            eprintln!("Path does not exist or is not a directory");
        }
    }

    fn traverse_directory(dir: &Path) -> Result<(), Box<dyn std::error::Error>> {
        if dir.is_dir() {
            for entry in fs::read_dir(dir)? {
                let entry = entry?;
                let path = entry.path();
    
                if path.is_dir() {
                    // 递归遍历子文件夹
                    traverse_directory(&path)?;
                } else if path.is_file() {
                    // 检查文件扩展名
                    if let Some(ext) = path.extension() {
                        if ext == "v" || ext == "sv" {
                            println!("Test file: {:?}", path);
                            // TODO: Check Stack Overflow tests
                            if path.to_str().unwrap() == "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/svlog/tools/ivtest/comp1001.sv" {
                                continue;
                            }
                            if path.to_str().unwrap() == "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/svlog/tools/ivtest/comp1000.sv" {
                                continue;
                            }
                            if path.to_str().unwrap() == "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/svlog/tools/ivtest/br_gh330.sv" {
                                continue;
                            }
                            if path.to_str().unwrap() == "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/svlog/tools/hdlconv/pri_encoder_using_assign.sv" {
                                continue;
                            }
                            if path.to_str().unwrap() == "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/svlog/tools/ivtest/macro_with_args.sv" {
                                continue;
                            }
                            if path.to_str().unwrap() == "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/vlog/element/macro.v" {
                                continue;
                            }
                            
                            let file_path = path.to_str().unwrap();
                            let result = sv_parser(file_path);
                            assert!(result.is_some());
                        }
                    }
                }
            }
        }
        Ok(())
    }
}

#[cfg(test)]
mod test_svparse {
    use std::{fs, path::{Path, PathBuf}};

    use ropey::Rope;
    use tower_lsp::lsp_types::{Position, Range, Url};
    use crate::sources::recovery_sv_parse_with_retry;

    use super::{INCOMPLETE_EXAMPLE, TEST_FILE, DIGTIAL_IDE_FACTORY};

    #[test]
    /// 测试单独的文件
    fn test_mycpu() {
        let includes: Vec<PathBuf> = Vec::new();
        let path = TEST_FILE;

        let text = match fs::read_to_string(path) {
            Ok(text) => text,
            Err(_) => return
        };

        let doc = Rope::from_str(&text);
        let uri = Url::from_file_path(&path).unwrap();
        let last_change_range = Range::new(
            Position { line: 0, character: 0 },
            Position { line: 0, character: 0 },
        );
        let result = recovery_sv_parse_with_retry(&doc, &uri, &Some(last_change_range), &includes);
        match result {
            Some(syntax_tree) => {
                println!("success");
                println!("{:?}", syntax_tree);
            },
            None => {
                eprintln!("gen None");
            }
        }
    }

    #[test]
    /// 测试语法不完整的例子
    fn test_incomplete_example() {
        let path = Path::new(INCOMPLETE_EXAMPLE);
        if path.exists() && path.is_dir() {
            // 递归遍历文件夹
            if let Err(e) = traverse_directory(path) {
                eprintln!("Error: {}", e);
            }
        } else {
            eprintln!("Path does not exist or is not a directory");
        }
    }

    #[test]
    fn test_factory() {
        let path = Path::new(DIGTIAL_IDE_FACTORY);
        if path.exists() && path.is_dir() {
            // 递归遍历文件夹
            if let Err(e) = traverse_directory(path) {
                eprintln!("Error: {}", e);
            }
        } else {
            eprintln!("Path does not exist or is not a directory");
        }
    }
    
    fn traverse_directory(dir: &Path) -> Result<(), Box<dyn std::error::Error>> {
        if dir.is_dir() {
            for entry in fs::read_dir(dir)? {
                let entry = entry?;
                let path = entry.path();
    
                if path.is_dir() {
                    // 递归遍历子文件夹
                    traverse_directory(&path)?;
                } else if path.is_file() {
                    // 检查文件扩展名
                    if let Some(ext) = path.extension() {
                        if ext == "v" || ext == "sv" {
                            println!("Test file: {:?}", path);
                            // TODO: Check Stack Overflow tests
                            if path.to_str().unwrap() == "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/svlog/tools/ivtest/comp1001.sv" {
                                continue;
                            }
                            if path.to_str().unwrap() == "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/svlog/tools/ivtest/comp1000.sv" {
                                continue;
                            }
                            if path.to_str().unwrap() == "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/svlog/tools/ivtest/br_gh330.sv" {
                                continue;
                            }
                            if path.to_str().unwrap() == "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/svlog/tools/hdlconv/pri_encoder_using_assign.sv" {
                                continue;
                            }
                            let file_path = path.to_str().unwrap();
                            let text = match fs::read_to_string(file_path) {
                                Ok(text) => text,
                                Err(_) => continue
                            };
                    
                            let includes: Vec<PathBuf> = Vec::new();
                            let doc = Rope::from_str(&text);
                            let uri = Url::from_file_path(file_path).unwrap();
                            let result = recovery_sv_parse_with_retry(&doc, &uri, &None, &includes);

                            assert!(result.is_some());

                        }
                    }
                }
            }
        }
        Ok(())
    }
}

#[cfg(test)]
mod test_scope_tree {
    use std::{fs, path::{Path, PathBuf}};
    use crate::sources::recovery_sv_parse_with_retry;
    use crate::core::scope_tree::get_scopes_from_syntax_tree;
    use crate::core::scope_tree::common::GenericScope;
    use ropey::Rope;
    use tower_lsp::lsp_types::Url;

    use super::*;

    fn get_scope_tree(file_path: &str) -> Option<GenericScope> {
        let includes: Vec<PathBuf> = Vec::new();

        let text = match fs::read_to_string(file_path) {
            Ok(text) => text,
            Err(_) => return None
        };

        let doc = Rope::from_str(&text);
        let uri = Url::from_file_path(&file_path).unwrap();
        let result = recovery_sv_parse_with_retry(&doc, &uri, &None, &includes);
        // let result = recovery_sv_parse(&doc, &uri, &None, &includes, true);

        if let Some((syntax_tree, _)) = result {
            let file_url = format!("file://{}", file_path);
            let uri = Url::parse(&file_url);
            if let Ok(uri) = uri {
                return get_scopes_from_syntax_tree(&syntax_tree, &uri);
            } else {
                eprintln!("error happen when unwrap uri: {:?}", uri);
                return None;
            }
        }
        None
    }

    #[test]
    fn test_mycpu() {
        let result = get_scope_tree(TEST_FILE);
        if let Some(scope) = result {
            println!("{:?}", scope);
        } else {
            panic!("error parsing");
        }
    }

    #[test]
    fn test_testfiles() {
        let entries = unwrap_result!(fs::read_dir(TESTFILES_DIR));
        for entry in entries {
            let entry = unwrap_result!(entry);
            let file_path = entry.path();
            if file_path.is_file() {
                let extension = unwrap_option!(file_path.extension());
                let file_path = unwrap_option!(file_path.to_str());
                if extension == "v" || extension == "sv" {
                    println!("Test file: {:?}", file_path);
                    let _ = get_scope_tree(file_path);
                }
            }
        }
    }

    #[test]
    fn test_digital_ide_test() {
        // 判断路径是否存在且为文件夹
        let path = Path::new(DIGTIAL_IDE_TEST);
        if path.exists() && path.is_dir() {
            // 递归遍历文件夹
            if let Err(e) = traverse_directory(path) {
                eprintln!("Error: {}", e);
            }
        } else {
            eprintln!("Path does not exist or is not a directory");
        }
    }

    #[test]
    fn test_incomplete_example() {
        let path = Path::new(INCOMPLETE_EXAMPLE);
        if path.exists() && path.is_dir() {
            // 递归遍历文件夹
            if let Err(e) = traverse_directory(path) {
                eprintln!("Error: {}", e);
            }
        } else {
            eprintln!("Path does not exist or is not a directory");
        }
    }

    #[test]
    fn test_factory() {
        let path = Path::new(DIGTIAL_IDE_FACTORY);
        if path.exists() && path.is_dir() {
            // 递归遍历文件夹
            if let Err(e) = traverse_directory(path) {
                eprintln!("Error: {}", e);
            }
        } else {
            eprintln!("Path does not exist or is not a directory");
        }
    }

    fn traverse_directory(dir: &Path) -> Result<(), Box<dyn std::error::Error>> {
        if dir.is_dir() {
            for entry in fs::read_dir(dir)? {
                let entry = entry?;
                let path = entry.path();
    
                if path.is_dir() {
                    // 递归遍历子文件夹
                    traverse_directory(&path)?;
                } else if path.is_file() {
                    // 检查文件扩展名
                    if let Some(ext) = path.extension() {
                        if ext == "v" || ext == "sv" {
                            println!("Test file: {:?}", path);
                            // TODO: Check Macro tests
                            if path.to_str().unwrap() == "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/svlog/tools/ivtest/macro_with_args.sv" {
                                continue;
                            }
                            if path.to_str().unwrap() == "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/vlog/element/macro.v" {
                                continue;
                            }
                            let file_path = path.to_str().unwrap();
                            let scope = get_scope_tree(file_path);
                            assert!(scope.is_some());
                        }
                    }
                }
            }
        }
        Ok(())
    }
}

#[cfg(test)]
mod test_file {
    use std::fs;

    use crate::utils::{file_size_in_kb, get_language_id_by_pathbuf, is_character_ordered_match, RecursiveFileIterator};
    use super::*;

    #[test]
    fn test_big_file_detect() {
        let file_iter = RecursiveFileIterator::new(DIGTIAL_IDE_TEST);
        // 超过 1 MB 的文件，我们都认为是大文件
        let threshold: u64 = 1024;
        for pathbuf in file_iter {
            let language_id = get_language_id_by_pathbuf(&pathbuf);
            if language_id == "verilog" || language_id == "systemverilog" {
                let size = file_size_in_kb(pathbuf.to_str().unwrap());
                if let Ok(size) = size {
                    if size >= threshold {
                        println!("detect big file : {:?}, size: {}", pathbuf, size);
                    }
                } else {
                    eprintln!("error to get size of {:?}", pathbuf);
                }
            }
        }
    }

    #[test]
    fn test_cache() {
        let _ = fs::create_dir_all("/home/dide/project/digital-lsp-server/.cache");
    }

    #[test]
    fn test_utils() {
        println!("enter function");
        assert!(is_character_ordered_match("parm", "param"));
        assert!(is_character_ordered_match("param", "param"));
        assert!(is_character_ordered_match("prm", "PARAM"));
        assert!(is_character_ordered_match("car", "careful"));
        assert!(!is_character_ordered_match("suprt", "super"));
    }
}