use gust_lang::{GoCodegen, RustCodegen, parse_program_with_errors};
use std::fs;
use std::path::Path;

#[test]
fn all_docs_gust_blocks_parse_and_codegen() {
    let docs_src = Path::new(env!("CARGO_MANIFEST_DIR"))
        .parent()
        .expect("workspace root")
        .join("docs")
        .join("src");

    let mut checked = 0usize;
    walk_markdown(&docs_src, &mut |path, content| {
        for block in extract_gust_blocks(content) {
            let file = path.to_string_lossy().to_string();
            let program = parse_program_with_errors(&block, &file)
                .expect("gust snippet in docs should parse");
            let rust = RustCodegen::new().generate(&program);
            let go = GoCodegen::new().generate(&program, "docstest");
            assert!(
                rust.contains("Generated by Gust compiler"),
                "rust codegen failed for {file}"
            );
            assert!(
                go.contains("Code generated by Gust compiler"),
                "go codegen failed for {file}"
            );
            checked += 1;
        }
    });

    assert!(checked > 0, "no gust code blocks were found in docs/src");
}

fn walk_markdown(dir: &Path, f: &mut impl FnMut(&Path, &str)) {
    for entry in fs::read_dir(dir).expect("docs dir should be readable") {
        let entry = entry.expect("dir entry");
        let path = entry.path();
        if path.is_dir() {
            walk_markdown(&path, f);
        } else if path.extension().and_then(|s| s.to_str()) == Some("md") {
            let content = fs::read_to_string(&path).expect("markdown file should be readable");
            f(&path, &content);
        }
    }
}

fn extract_gust_blocks(content: &str) -> Vec<String> {
    let mut blocks = Vec::new();
    let mut in_gust = false;
    let mut start = 0usize;

    for (idx, line) in content.lines().enumerate() {
        let trimmed = line.trim();
        if !in_gust && trimmed == "```gust" {
            in_gust = true;
            start = idx + 1;
            continue;
        }
        if in_gust && trimmed == "```" {
            let lines: Vec<&str> = content.lines().collect();
            let block = lines[start..idx].join("\n");
            blocks.push(block);
            in_gust = false;
        }
    }

    blocks
}
