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

fn build_grammar(dir: &PathBuf, lib_name: &str) {
    let out_dir = std::env::var("OUT_DIR").unwrap();
    let cache_dir = PathBuf::from(&out_dir).join(".grammar-cache");
    fs::create_dir_all(&cache_dir).unwrap();

    let cached_lib = cache_dir.join(format!("lib{}.a", lib_name));
    let parser_c = dir.join("parser.c");

    // Check if cached library exists and is newer than source
    let should_rebuild = if cached_lib.exists() {
        let cache_time = fs::metadata(&cached_lib).unwrap().modified().unwrap();
        let source_time = fs::metadata(&parser_c).unwrap().modified().unwrap();
        source_time > cache_time
    } else {
        true
    };

    if !should_rebuild {
        // Use cached library - copy to OUT_DIR (avoids recompilation across build flavors)
        fs::copy(
            &cached_lib,
            PathBuf::from(&out_dir).join(format!("lib{}.a", lib_name)),
        )
        .unwrap();
    } else {
        // Compile C code from scratch
        let mut build = cc::Build::new();
        build.include(dir).file(dir.join("parser.c"));

        // Include external scanner if present (required by some grammars like Rust)
        let scanner_c = dir.join("scanner.c");
        if scanner_c.exists() {
            build.file(scanner_c.clone());
        }

        // Suppress the specific warning from vendored tree-sitter code
        if build.get_compiler().is_like_clang() || build.get_compiler().is_like_gnu() {
            build.flag("-Wno-unused-but-set-variable");
        }

        // Compile to object file and link directly
        build.compile(lib_name);

        // Update cache for future builds
        let compiled_lib = PathBuf::from(&out_dir).join(format!("lib{}.a", lib_name));
        fs::copy(&compiled_lib, &cached_lib).unwrap();
    }

    // Link the static library explicitly
    println!("cargo:rustc-link-search=native={out_dir}");
    println!("cargo:rustc-link-arg={out_dir}/lib{lib_name}.a");
}

fn main() {
    // Only rerun build script if these specific files change
    println!("cargo:rerun-if-changed=build.rs");
    println!("cargo:rerun-if-changed=grammars/");

    // Override target-related environment variables to align with Nix expectations
    // The issue is cc crate converts aarch64-apple-darwin -> arm64-apple-macosx
    // but Nix expects arm64-apple-darwin
    unsafe {
        if let Ok(target) = std::env::var("TARGET") {
            if target == "aarch64-apple-darwin" {
                // Force cc crate to use the darwin naming that Nix expects
                std::env::set_var(
                    "CC_aarch64_apple_darwin",
                    std::env::var("CC").unwrap_or_else(|_| "clang".to_string()),
                );
                std::env::set_var("CFLAGS_aarch64_apple_darwin", "-target arm64-apple-darwin");
            }
        }
    }

    // Build Solidity grammar
    let solidity_dir: PathBuf = ["grammars", "solidity", "src"].iter().collect();
    build_grammar(&solidity_dir, "tree-sitter-solidity");

    // Build Rust grammar
    let rust_dir: PathBuf = ["grammars", "rust", "src"].iter().collect();
    build_grammar(&rust_dir, "tree-sitter-rust");

    // Build Go grammar
    let go_dir: PathBuf = ["grammars", "go", "src"].iter().collect();
    build_grammar(&go_dir, "tree-sitter-go");

    // Build JavaScript grammar
    let javascript_dir: PathBuf = ["grammars", "javascript", "src"].iter().collect();
    build_grammar(&javascript_dir, "tree-sitter-javascript");

    // Build TypeScript grammar
    let typescript_dir: PathBuf = ["grammars", "typescript", "src"].iter().collect();
    build_grammar(&typescript_dir, "tree-sitter-typescript");

    // Build TSX grammar (TypeScript + JSX)
    let tsx_dir: PathBuf = ["grammars", "tsx", "src"].iter().collect();
    build_grammar(&tsx_dir, "tree-sitter-tsx");

    // Build C++ grammar
    let cpp_dir: PathBuf = ["grammars", "cpp", "src"].iter().collect();
    build_grammar(&cpp_dir, "tree-sitter-cpp");

    // Build Sui Move grammar
    let move_dir: PathBuf = ["grammars", "move", "src"].iter().collect();
    build_grammar(&move_dir, "tree-sitter-move");
}
