// Embed `templates/` into the binary so `koala-core init` can scaffold
// new projects without needing the source tree at runtime. Walks the
// directory deterministically and emits a sorted `(rel_path, &[u8])`
// table consumed by `commands::init`.
//
// Also captures git HEAD short-SHA + commit ISO date as compile-time
// env vars so `koala-core --version` shows which commit is running.
// Tied to HEAD via `cargo:rerun-if-changed=.git/HEAD`, so the
// re-embed only fires when the commit changes.

use std::env;
use std::fs;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;

fn main() {
    let manifest = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
    let templates = manifest
        .join("templates")
        .canonicalize()
        .expect("templates/ directory must exist inside crates/koala-cli");

    println!("cargo:rerun-if-changed={}", templates.display());

    embed_git_metadata(&manifest);

    let mut entries: Vec<(String, PathBuf)> = Vec::new();
    walk(&templates, &templates, &mut entries);
    entries.sort_by(|a, b| a.0.cmp(&b.0));

    let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
    let dest = out_dir.join("templates_embedded.rs");
    let mut f = fs::File::create(&dest).expect("create templates_embedded.rs");

    writeln!(f, "// @generated by build.rs — do not edit").unwrap();
    writeln!(f, "pub static TEMPLATE_FILES: &[(&str, &[u8])] = &[").unwrap();
    for (rel, abs) in &entries {
        let rel = rel.replace('\\', "/");
        writeln!(
            f,
            "    ({:?}, include_bytes!({:?})),",
            rel,
            abs.to_string_lossy()
        )
        .unwrap();
        println!("cargo:rerun-if-changed={}", abs.display());
    }
    writeln!(f, "];").unwrap();
}

/// Capture git short-SHA + commit date and emit them as compile-time
/// env vars (`KOALA_BUILD_SHA`, `KOALA_BUILD_DATE`). When the repo
/// state isn't observable (e.g. building from a vendored tarball),
/// fall back to `unknown`. Re-runs only on `.git/HEAD` change so
/// unrelated edits don't bust the cache.
fn embed_git_metadata(manifest: &Path) {
    let repo_root = manifest.join("../..");

    // Tie the cache to commit movement, not every build.
    for hint in [".git/HEAD", ".git/refs/heads/main"] {
        let p = repo_root.join(hint);
        if p.exists() {
            println!("cargo:rerun-if-changed={}", p.display());
        }
    }

    let sha =
        git(&repo_root, &["rev-parse", "--short=12", "HEAD"]).unwrap_or_else(|| "unknown".into());
    let date = git(&repo_root, &["show", "-s", "--format=%cI", "HEAD"])
        .unwrap_or_else(|| "unknown".into());

    println!("cargo:rustc-env=KOALA_BUILD_SHA={sha}");
    println!("cargo:rustc-env=KOALA_BUILD_DATE={date}");
}

fn git(cwd: &Path, args: &[&str]) -> Option<String> {
    let out = Command::new("git")
        .args(args)
        .current_dir(cwd)
        .output()
        .ok()?;
    if !out.status.success() {
        return None;
    }
    Some(String::from_utf8_lossy(&out.stdout).trim().to_string())
}

fn walk(root: &Path, dir: &Path, out: &mut Vec<(String, PathBuf)>) {
    let rd = match fs::read_dir(dir) {
        Ok(rd) => rd,
        Err(e) => panic!("read_dir {}: {e}", dir.display()),
    };
    for entry in rd {
        let entry = entry.expect("read entry");
        let p = entry.path();
        let ft = entry.file_type().expect("file_type");
        if ft.is_dir() {
            walk(root, &p, out);
        } else if ft.is_file() {
            let rel = p
                .strip_prefix(root)
                .expect("strip_prefix")
                .to_string_lossy()
                .to_string();
            out.push((rel, p));
        }
    }
}
