use std::env;
use std::fs;
use std::io::Write;
use std::path::PathBuf;
use std::process::Command;

fn run_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 resolve_git_dir_from(repo_dir: &PathBuf) -> Option<PathBuf> {
    let mut cur = repo_dir.clone();
    loop {
        let dot_git = cur.join(".git");
        if dot_git.is_dir() {
            return Some(dot_git);
        }
        if dot_git.is_file() {
            // Worktree style: ".git" file contains "gitdir: <path>"
            let s = fs::read_to_string(&dot_git).ok()?;
            let line = s.lines().next()?.trim();
            let p = line.strip_prefix("gitdir:")?.trim();
            let gitdir = if PathBuf::from(p).is_absolute() {
                PathBuf::from(p)
            } else {
                cur.join(p)
            };
            return Some(gitdir);
        }
        if !cur.pop() {
            return None;
        }
    }
}

fn main() {
    let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let crate_dir = PathBuf::from(crate_dir);

    println!("cargo:rerun-if-changed=build.rs");
    println!("cargo:rerun-if-changed=aarch64.lds");
    println!("cargo:rerun-if-env-changed=XTASK_BUILD");
    println!("cargo:rerun-if-env-changed=RUSTFLAGS");
    println!("cargo:rerun-if-env-changed=CARGO_ENCODED_RUSTFLAGS");
    println!("cargo:rerun-if-env-changed=GITHUB_SHA");

    // Best-effort: rerun build script when HEAD/index changes (works for normal repo/worktree).
    if let Some(git_dir) = resolve_git_dir_from(&crate_dir) {
        let head = git_dir.join("HEAD");
        if head.exists() {
            println!("cargo:rerun-if-changed={}", head.display());
        }
        let index = git_dir.join("index");
        if index.exists() {
            println!("cargo:rerun-if-changed={}", index.display());
        }
    }

    // Prefer CI-provided SHA, fallback to local git.
    let git_sha = env::var("GITHUB_SHA")
        .ok()
        .filter(|s| !s.trim().is_empty())
        .or_else(|| run_git(&["rev-parse", "HEAD"]))
        .unwrap_or_else(|| "unknown".to_string());

    let git_sha_short = if git_sha != "unknown" && git_sha.len() >= 12 {
        git_sha[..12].to_string()
    } else {
        git_sha.clone()
    };

    let git_dirty = run_git(&["status", "--porcelain"])
        .map(|s| !s.is_empty())
        .unwrap_or(false);

    let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
    let dst = out_dir.join("build_info.rs");
    let mut f = fs::File::create(&dst).unwrap();
    writeln!(f, "// @generated by bootloader/build.rs").unwrap();
    writeln!(f, "pub const BUILD_GIT_SHA: &str = {:?};", git_sha).unwrap();
    writeln!(
        f,
        "pub const BUILD_GIT_SHA_SHORT: &str = {:?};",
        git_sha_short
    )
    .unwrap();
    writeln!(f, "pub const BUILD_GIT_DIRTY: bool = {:?};", git_dirty).unwrap();

    // Keep existing link behavior.
    println!("cargo:rustc-link-search={}", crate_dir.display());
    let rustflags = env::var("RUSTFLAGS").unwrap_or_default();
    let encoded_rustflags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap_or_default();
    let has_test_lds = rustflags.contains("test.lds") || encoded_rustflags.contains("test.lds");
    if !has_test_lds {
        let rpi4_lds = env::var_os("CARGO_FEATURE_RPI4").is_some();
        let script = if rpi4_lds {
            "aarch64-rpi4.lds"
        } else {
            "aarch64.lds"
        };
        println!("cargo:rustc-link-arg=-T{script}");
    }
}