use camino::Utf8Path;
use std::process::Command;

fn main() {
    commit_info();

    let target = std::env::var("TARGET").unwrap();
    println!("cargo:rustc-env=NEXTEST_BUILD_HOST_TARGET={target}");
}

fn commit_info() {
    println!("cargo:rerun-if-env-changed=CFG_OMIT_COMMIT_HASH");
    if std::env::var_os("CFG_OMIT_COMMIT_HASH").is_some() {
        return;
    }

    if let Some(info) = CommitInfo::get() {
        println!("cargo:rustc-env=NEXTEST_BUILD_COMMIT_HASH={}", info.hash);
        println!(
            "cargo:rustc-env=NEXTEST_BUILD_COMMIT_SHORT_HASH={}",
            info.short_hash,
        );
        println!("cargo:rustc-env=NEXTEST_BUILD_COMMIT_DATE={}", info.date);
    }
}

struct CommitInfo {
    hash: String,
    short_hash: String,
    date: String,
}

impl CommitInfo {
    fn get() -> Option<Self> {
        // Prefer git info over crate metadata to match what Cargo and rustc do.
        Self::from_git().or_else(Self::from_metadata)
    }

    fn from_git() -> Option<Self> {
        // cargo-nextest is one level down from the root of the repository.
        if !Utf8Path::new("../.git").exists() {
            return None;
        }

        let output = match Command::new("git")
            .arg("log")
            .arg("-1")
            .arg("--date=short")
            .arg("--format=%H %h %cd")
            .arg("--abbrev=9")
            .output()
        {
            Ok(output) if output.status.success() => output,
            _ => return None,
        };

        let stdout = String::from_utf8(output.stdout).expect("git output is ASCII");
        Self::from_string(&stdout)
    }

    fn from_metadata() -> Option<Self> {
        // This file is generated by scripts/cargo-release-publish.sh.
        let path = Utf8Path::new("nextest-commit-info");
        if !path.exists() {
            return None;
        }

        println!("cargo:rerun-if-changed={path}");

        let content = std::fs::read_to_string(path).ok()?;
        Self::from_string(&content)
    }

    fn from_string(s: &str) -> Option<Self> {
        let mut parts = s.split_whitespace().map(|s| s.to_string());
        Some(CommitInfo {
            hash: parts.next()?,
            short_hash: parts.next()?,
            date: parts.next()?,
        })
    }
}
