/// Integration test — compile every example .ds file through the full pipeline.
/// This is the ultimate regression guard: if any of the 51 examples break, this test catches it.

use std::fs;
use std::path::PathBuf;

/// Get the workspace root (two levels up from ds-cli's Cargo.toml).
fn workspace_root() -> PathBuf {
    let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    manifest.parent().unwrap().parent().unwrap().to_path_buf()
}

/// Compile a .ds source string through the full pipeline (lex → parse → analyze → codegen).
fn compile_source(source: &str) -> Result<String, String> {
    let mut lexer = ds_parser::Lexer::new(source);
    let tokens = lexer.tokenize();

    for tok in &tokens {
        if let ds_parser::TokenKind::Error(msg) = &tok.kind {
            return Err(format!("Lexer error at line {}: {}", tok.line, msg));
        }
    }

    let mut parser = ds_parser::Parser::with_source(tokens, source);
    let program = parser.parse_program().map_err(|e| e.to_string())?;

    let graph = ds_analyzer::SignalGraph::from_program(&program);
    let views = ds_analyzer::SignalGraph::analyze_views(&program);

    let html = ds_codegen::JsEmitter::emit_html(&program, &graph, &views, false);
    Ok(html)
}

#[test]
fn test_compile_all_examples() {
    // Spawn with 8MB stack to handle deeply nested examples
    let builder = std::thread::Builder::new()
        .name("compile_examples".into())
        .stack_size(8 * 1024 * 1024);

    let handle = builder.spawn(|| {
        let examples_dir = workspace_root().join("examples");
        assert!(examples_dir.exists(), "examples/ directory not found at {:?}", examples_dir);

        let mut ds_files: Vec<PathBuf> = fs::read_dir(&examples_dir)
            .expect("cannot read examples/")
            .filter_map(|e| e.ok())
            .map(|e| e.path())
            .filter(|p| p.extension().map_or(false, |ext| ext == "ds"))
            .collect();

        ds_files.sort();

        assert!(!ds_files.is_empty(), "no .ds files found in examples/");

        let mut pass = 0;
        let mut fail = 0;
        let mut failures = Vec::new();

        for path in &ds_files {
            let source = fs::read_to_string(path)
                .unwrap_or_else(|e| panic!("cannot read {}: {}", path.display(), e));

            let name = path.file_name().unwrap().to_str().unwrap();

            match compile_source(&source) {
                Ok(html) => {
                    assert!(!html.is_empty(), "{}: produced empty output", name);
                    pass += 1;
                }
                Err(e) => {
                    failures.push(format!("  {} — {}", name, e));
                    fail += 1;
                }
            }
        }

        eprintln!("\n  Examples: {} passed, {} failed, {} total", pass, fail, ds_files.len());

        if !failures.is_empty() {
            panic!(
                "\n{} example(s) failed to compile:\n{}\n",
                fail,
                failures.join("\n")
            );
        }
    }).expect("failed to spawn test thread");

    handle.join().expect("test thread panicked");
}
