//! Stamp build identity into the binary.
//!
//! A live FIFA trace has to be attributable to an exact binary. During the
//! mutation runs for the previous step, four sidecars were left listening —
//! two of them serving deliberately broken builds — and nothing in their output
//! said so. A later A/B against one of those would have read as a genuine
//! parity failure.
//!
//! So the host prints its commit, working-tree cleanliness and profile at
//! startup, and `dirty` is the important one: a mutation-tested build is a
//! dirty build, and now it announces itself.

use std::process::Command;

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

fn main() {
    let commit = git(&["rev-parse", "--short=7", "HEAD"]).unwrap_or_else(|| "unknown".into());

    // Scoped to the crates this binary is actually built from.
    //
    // A whole-repo check reads DIRTY permanently here, because unrelated
    // submodules carry pre-existing modifications. A warning that is always on
    // is a warning nobody reads — which would defeat the point, since the whole
    // job of this flag is to make a mutated build announce itself.
    //
    // Untracked files are excluded: scratch output is not a build difference,
    // but an edited source file certainly is.
    let dirty = match git(&[
        "status",
        "--porcelain",
        "--untracked-files=no",
        "--",
        "openfut-blaze-host",
        "openfut-adapter-fifa17",
        "openfut-protocol-blaze",
    ]) {
        Some(s) if !s.is_empty() => "DIRTY",
        Some(_) => "clean",
        None => "unknown",
    };

    println!("cargo:rustc-env=OPENFUT_BUILD_COMMIT={commit}");
    println!("cargo:rustc-env=OPENFUT_BUILD_DIRTY={dirty}");

    // Re-stamp when HEAD moves.
    //
    // IMPORTANT LIMITATION: this flag is best-effort and CAN BE STALE. Cargo
    // will not re-run a build script because some other crate's source changed,
    // so editing the adapter and rebuilding the host can leave `dirty` reading
    // "clean". Verified: appending a line to the adapter and rebuilding did not
    // flip it.
    //
    // So the compiled-in value is useful for naming the commit, and is NOT the
    // safeguard. `sidecar.sh` re-checks the working tree at launch and
    // `check-live-parity.sh` refuses to produce evidence from a dirty tree —
    // those run at the right moment and cannot go stale.
    for p in ["../.git/HEAD", "../.git/index"] {
        if std::path::Path::new(p).exists() {
            println!("cargo:rerun-if-changed={p}");
        }
    }
    // Committing updates refs/heads/<branch>, NOT the HEAD file, so watching
    // HEAD alone leaves the stamp one commit behind. Observed: the banner read
    // a84a72e immediately after committing 2337431.
    if let Some(rf) = git(&["symbolic-ref", "-q", "HEAD"]) {
        let path = format!("../.git/{rf}");
        if std::path::Path::new(&path).exists() {
            println!("cargo:rerun-if-changed={path}");
        }
    }
}
