//! Unit tests for herodo library functions
//!
//! These tests focus on individual functions and components of the herodo library.

use std::fs;
use tempfile::TempDir;

/// Test the collect_rhai_files function indirectly through directory operations
#[test]
fn test_rhai_file_collection_logic() {
    let temp_dir = TempDir::new().expect("Failed to create temp directory");

    // Create various files
    fs::write(temp_dir.path().join("script1.rhai"), "// Script 1")
        .expect("Failed to write script1");
    fs::write(temp_dir.path().join("script2.rhai"), "// Script 2")
        .expect("Failed to write script2");
    fs::write(temp_dir.path().join("not_script.txt"), "Not a script")
        .expect("Failed to write txt file");
    fs::write(temp_dir.path().join("README.md"), "# README").expect("Failed to write README");

    // Create subdirectory with more scripts
    let sub_dir = temp_dir.path().join("subdir");
    fs::create_dir(&sub_dir).expect("Failed to create subdirectory");
    fs::write(sub_dir.join("sub_script.rhai"), "// Sub script")
        .expect("Failed to write sub script");

    // Count .rhai files manually
    let mut rhai_count = 0;
    for entry in fs::read_dir(temp_dir.path()).expect("Failed to read temp directory") {
        let entry = entry.expect("Failed to get directory entry");
        let path = entry.path();
        if path.is_file() && path.extension().map_or(false, |ext| ext == "rhai") {
            rhai_count += 1;
        }
    }

    // Should find 2 .rhai files in the main directory
    assert_eq!(
        rhai_count, 2,
        "Should find exactly 2 .rhai files in main directory"
    );

    // Verify subdirectory has 1 .rhai file
    let mut sub_rhai_count = 0;
    for entry in fs::read_dir(&sub_dir).expect("Failed to read subdirectory") {
        let entry = entry.expect("Failed to get directory entry");
        let path = entry.path();
        if path.is_file() && path.extension().map_or(false, |ext| ext == "rhai") {
            sub_rhai_count += 1;
        }
    }

    assert_eq!(
        sub_rhai_count, 1,
        "Should find exactly 1 .rhai file in subdirectory"
    );
}

/// Test path validation logic
#[test]
fn test_path_validation() {
    let temp_dir = TempDir::new().expect("Failed to create temp directory");
    let script_path = temp_dir.path().join("test.rhai");

    // Create a test script
    fs::write(&script_path, "println(\"test\");").expect("Failed to write test script");

    // Test file path validation
    assert!(script_path.exists(), "Script file should exist");
    assert!(script_path.is_file(), "Script path should be a file");

    // Test directory path validation
    assert!(temp_dir.path().exists(), "Temp directory should exist");
    assert!(temp_dir.path().is_dir(), "Temp path should be a directory");

    // Test non-existent path
    let nonexistent = temp_dir.path().join("nonexistent.rhai");
    assert!(!nonexistent.exists(), "Non-existent path should not exist");
}

/// Test file extension checking
#[test]
fn test_file_extension_checking() {
    let temp_dir = TempDir::new().expect("Failed to create temp directory");

    // Create files with different extensions
    let rhai_file = temp_dir.path().join("script.rhai");
    let txt_file = temp_dir.path().join("document.txt");
    let no_ext_file = temp_dir.path().join("no_extension");

    fs::write(&rhai_file, "// Rhai script").expect("Failed to write rhai file");
    fs::write(&txt_file, "Text document").expect("Failed to write txt file");
    fs::write(&no_ext_file, "No extension").expect("Failed to write no extension file");

    // Test extension detection
    assert_eq!(rhai_file.extension().unwrap(), "rhai");
    assert_eq!(txt_file.extension().unwrap(), "txt");
    assert!(no_ext_file.extension().is_none());

    // Test extension comparison
    assert!(rhai_file.extension().map_or(false, |ext| ext == "rhai"));
    assert!(!txt_file.extension().map_or(false, |ext| ext == "rhai"));
    assert!(!no_ext_file.extension().map_or(false, |ext| ext == "rhai"));
}

/// Test script content reading
#[test]
fn test_script_content_reading() {
    let temp_dir = TempDir::new().expect("Failed to create temp directory");
    let script_path = temp_dir.path().join("content_test.rhai");

    let expected_content = r#"
        println("Testing content reading");
        let value = 42;
        value * 2
    "#;

    fs::write(&script_path, expected_content).expect("Failed to write script content");

    // Read the content back
    let actual_content = fs::read_to_string(&script_path).expect("Failed to read script content");
    assert_eq!(
        actual_content, expected_content,
        "Script content should match"
    );

    // Verify content contains expected elements
    assert!(
        actual_content.contains("println"),
        "Content should contain println"
    );
    assert!(
        actual_content.contains("let value = 42"),
        "Content should contain variable declaration"
    );
    assert!(
        actual_content.contains("value * 2"),
        "Content should contain expression"
    );
}

/// Test directory traversal logic
#[test]
fn test_directory_traversal() {
    let temp_dir = TempDir::new().expect("Failed to create temp directory");

    // Create nested directory structure
    let level1 = temp_dir.path().join("level1");
    let level2 = level1.join("level2");
    let level3 = level2.join("level3");

    fs::create_dir_all(&level3).expect("Failed to create nested directories");

    // Create scripts at different levels
    fs::write(temp_dir.path().join("root.rhai"), "// Root script")
        .expect("Failed to write root script");
    fs::write(level1.join("level1.rhai"), "// Level 1 script")
        .expect("Failed to write level1 script");
    fs::write(level2.join("level2.rhai"), "// Level 2 script")
        .expect("Failed to write level2 script");
    fs::write(level3.join("level3.rhai"), "// Level 3 script")
        .expect("Failed to write level3 script");

    // Verify directory structure
    assert!(temp_dir.path().is_dir(), "Root temp directory should exist");
    assert!(level1.is_dir(), "Level 1 directory should exist");
    assert!(level2.is_dir(), "Level 2 directory should exist");
    assert!(level3.is_dir(), "Level 3 directory should exist");

    // Verify scripts exist at each level
    assert!(
        temp_dir.path().join("root.rhai").exists(),
        "Root script should exist"
    );
    assert!(
        level1.join("level1.rhai").exists(),
        "Level 1 script should exist"
    );
    assert!(
        level2.join("level2.rhai").exists(),
        "Level 2 script should exist"
    );
    assert!(
        level3.join("level3.rhai").exists(),
        "Level 3 script should exist"
    );
}

/// Test sorting behavior for script execution order
#[test]
fn test_script_sorting_order() {
    let temp_dir = TempDir::new().expect("Failed to create temp directory");

    // Create scripts with names that should be sorted
    let scripts = vec![
        "03_third.rhai",
        "01_first.rhai",
        "02_second.rhai",
        "10_tenth.rhai",
        "05_fifth.rhai",
    ];

    for script in &scripts {
        fs::write(
            temp_dir.path().join(script),
            format!("// Script: {}", script),
        )
        .expect("Failed to write script");
    }

    // Collect and sort the scripts manually to verify sorting logic
    let mut found_scripts = Vec::new();
    for entry in fs::read_dir(temp_dir.path()).expect("Failed to read directory") {
        let entry = entry.expect("Failed to get directory entry");
        let path = entry.path();
        if path.is_file() && path.extension().map_or(false, |ext| ext == "rhai") {
            found_scripts.push(path.file_name().unwrap().to_string_lossy().to_string());
        }
    }

    found_scripts.sort();

    // Verify sorting order
    let expected_order = vec![
        "01_first.rhai",
        "02_second.rhai",
        "03_third.rhai",
        "05_fifth.rhai",
        "10_tenth.rhai",
    ];

    assert_eq!(
        found_scripts, expected_order,
        "Scripts should be sorted in correct order"
    );
}

/// Test empty directory handling
#[test]
fn test_empty_directory_detection() {
    let temp_dir = TempDir::new().expect("Failed to create temp directory");
    let empty_subdir = temp_dir.path().join("empty");

    fs::create_dir(&empty_subdir).expect("Failed to create empty subdirectory");

    // Verify directory is empty
    let entries: Vec<_> = fs::read_dir(&empty_subdir)
        .expect("Failed to read empty directory")
        .collect();

    assert!(entries.is_empty(), "Directory should be empty");

    // Count .rhai files in empty directory
    let mut rhai_count = 0;
    for entry in fs::read_dir(&empty_subdir).expect("Failed to read empty directory") {
        let entry = entry.expect("Failed to get directory entry");
        let path = entry.path();
        if path.is_file() && path.extension().map_or(false, |ext| ext == "rhai") {
            rhai_count += 1;
        }
    }

    assert_eq!(
        rhai_count, 0,
        "Empty directory should contain no .rhai files"
    );
}
