use workflow_context::{WorkerContext, ContextShell, OutputFormat, FindOptions, Vgcs, ContextStore};
use tempfile::TempDir;

const ZERO_OID: &str = "0000000000000000000000000000000000000000";

fn setup_env() -> (TempDir, String, String) {
    let temp_dir = TempDir::new().unwrap();
    let data_path = temp_dir.path().to_str().unwrap().to_string();
    let req_id = "req-shell-test".to_string();
    
    // Init Repo
    let vgcs = Vgcs::new(&data_path);
    vgcs.init_repo(&req_id).unwrap();
    
    (temp_dir, data_path, req_id)
}

#[test]
fn test_shell_comprehensive() -> anyhow::Result<()> {
    let (_tmp, data_path, req_id) = setup_env();
    
    // 1. Setup Initial Context
    let mut ctx = WorkerContext::new(&data_path, &req_id, ZERO_OID);
    
    ctx.write_file("README.md", "Project Root\n\nIntroduction here.")?;
    ctx.write_file("src/main.rs", "fn main() {\n    println!(\"Hello\");\n    println!(\"Hello\");\n}")?; // Double Hello for ambiguity test
    ctx.write_file("src/util.rs", "pub fn util() -> i32 { 42 }")?;
    ctx.write_file("data/config.json", "{\n  \"key\": \"value\",\n  \"retries\": 3\n}")?;
    ctx.write_file("文档/说明.txt", "这是一个中文文件。")?; // Unicode Path & Content
    
    let commit_1 = ctx.commit("Init")?;
    let mut ctx = WorkerContext::new(&data_path, &req_id, &commit_1);

    // --- Find Tests ---
    println!("Testing Find...");
    
    // Test: Recursive vs Non-recursive
    // Note: Includes directories (src, data, 文档) + files (5) = 8
    let all_nodes = ctx.find("**/*", FindOptions { recursive: true, ..Default::default() })?;
    assert_eq!(all_nodes.len(), 8);
    
    // Test: Only Files
    let only_files = ctx.find("**/*", FindOptions { 
        recursive: true, 
        type_filter: Some("File".to_string()),
        ..Default::default() 
    })?;
    assert_eq!(only_files.len(), 5);

    // Test: Non-recursive (Top level)
    let root_nodes = ctx.find("*", FindOptions { recursive: false, ..Default::default() })?;
    // Expect README.md, src(dir), data(dir), 文档(dir)
    assert!(root_nodes.iter().any(|f| f.path == "README.md"));
    assert!(root_nodes.iter().any(|f| f.path == "src"));
    
    // Test: Type Filter (Dir)
    let dirs = ctx.find("**/*", FindOptions { 
        recursive: true, 
        type_filter: Some("Dir".to_string()), 
        ..Default::default() 
    })?;
    assert!(dirs.iter().any(|d| d.path == "src"));
    assert!(dirs.iter().any(|d| d.path == "data"));
    assert!(dirs.iter().any(|d| d.path == "文档"));
    assert!(!dirs.iter().any(|d| d.path == "README.md"));

    // --- Grep Tests ---
    println!("Testing Grep...");
    
    // Test: Regex Match
    let matches = ctx.grep(r"fn \w+\(\)", None)?;
    assert_eq!(matches.len(), 2); // main() and util()
    
    // Test: Unicode Content
    let zh_matches = ctx.grep("中文", None)?;
    assert_eq!(zh_matches.len(), 1);
    assert_eq!(zh_matches[0].path, "文档/说明.txt");

    // Test: Invalid Regex
    let bad_regex = ctx.grep("(", None);
    assert!(bad_regex.is_err());

    // --- Patch Tests ---
    println!("Testing Patch...");

    // Test: Ambiguous Match (Safety Check)
    // src/main.rs has two "println!(\"Hello\");"
    let res = ctx.patch("src/main.rs", "println!(\"Hello\");", "println!(\"World\");");
    assert!(res.is_err(), "Should fail on ambiguous match");
    let err_msg = res.unwrap_err().to_string();
    assert!(err_msg.contains("Ambiguous match"), "Error message mismatch: {}", err_msg);

    // Test: Unique Match
    // Patch "Introduction here." to "Intro v2." in README.md
    ctx.patch("README.md", "Introduction here.", "Intro v2.")?;
    ctx.commit("Patch 1")?; // Must commit to verify via read (if read uses committed state)
    
    // Verify
    let readme = ctx.read_text("README.md")?;
    assert!(readme.contains("Intro v2."));
    
    // Test: Special Characters (Literal Match)
    // Let's try to patch JSON which has braces and quotes
    ctx.patch("data/config.json", "\"retries\": 3", "\"retries\": 5")?;
    ctx.commit("Patch 2")?;
    
    let config = ctx.read_text("data/config.json")?;
    assert!(config.contains("\"retries\": 5"));
    
    // Test: Cross-line Patch
    // Replace the whole function body in util.rs
    let old_block = "pub fn util() -> i32 { 42 }";
    let new_block = "pub fn util() -> i32 {\n    return 100;\n}";
    ctx.patch("src/util.rs", old_block, new_block)?;
    ctx.commit("Patch 3")?;
    
    let util = ctx.read_text("src/util.rs")?;
    assert!(util.contains("return 100;"));

    // Test: Patch non-existent file
    let res = ctx.patch("ghost.txt", "foo", "bar");
    assert!(res.is_err());

    Ok(())
}

#[test]
fn test_tool_schema_validity() {
    let defs = WorkerContext::get_tool_definitions();
    assert!(defs.is_array());
    let arr = defs.as_array().unwrap();
    
    // Verify critical fields exist for OpenAI
    for tool in arr {
        let obj = tool.as_object().unwrap();
        assert_eq!(obj["type"], "function");
        let func = obj["function"].as_object().unwrap();
        assert!(func.contains_key("name"));
        assert!(func.contains_key("description"));
        assert!(func.contains_key("parameters"));
    }
}
