//! Integration test del path autotools:
//!   - Repo git con `configure.ac` + `Makefile.am` + `hello.c` (sin `configure` generado).
//!   - Receta SIN `[build.phases]` → la heurística debe detectar AutoconfRaw y emitir
//!     `autoreconf -fi && ./configure --prefix=/usr` + `make` + `make install DESTDIR=/out`.
//!   - El binario instalado en el store debe correr en el sandbox.
//!
//! Skip transparente si el rootfs Alpine o zig no están bootstrappeados.

use std::path::{Path, PathBuf};
use std::process::Command;

use hammer_build::{build, BuildConfig};
use hammer_core::{Recipe, Store};

const HELLO_C: &str = r#"
#include <stdio.h>
int main(void) { puts("hammer-autotools OK"); return 0; }
"#;

const CONFIGURE_AC: &str = r#"
AC_INIT([helloat], [1.0])
AM_INIT_AUTOMAKE([foreign -Wall])
AC_PROG_CC
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
"#;

const MAKEFILE_AM: &str = r#"
bin_PROGRAMS = helloat
helloat_SOURCES = hello.c
"#;

const RECIPE_TOML: &str = r#"
name    = "helloat"
version = "1.0"

[source]
repo   = "PLACEHOLDER_REPO_URL"
commit = "PLACEHOLDER_COMMIT"

[build]
compiler = "zig-cc"
target   = "x86_64-linux-musl"
link     = "static"
"#;

fn project_root() -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .parent()
        .unwrap()
        .parent()
        .unwrap()
        .to_path_buf()
}

fn skip_if_no_layout(cfg: &BuildConfig) -> bool {
    if !cfg.rootfs.join("bin/busybox").is_file() {
        eprintln!("SKIP: rootfs Alpine no encontrado en {}.", cfg.rootfs.display());
        return true;
    }
    // Las tools de autotools se metieron con `apk add make autoconf automake m4 ...`
    // — comprobamos uno para no engañarnos si el rootfs es el minirootfs pelado.
    if !cfg.rootfs.join("usr/bin/autoreconf").is_file() {
        eprintln!(
            "SKIP: rootfs sin autotools. Corre: bwrap --bind {} / --share-net /sbin/apk add make autoconf automake m4 patch coreutils libtool pkgconf bash",
            cfg.rootfs.display()
        );
        return true;
    }
    if !cfg.zig_dir.join("zig").is_file() {
        eprintln!("SKIP: zig no encontrado en {}.", cfg.zig_dir.display());
        return true;
    }
    false
}

fn git(args: &[&str], cwd: &Path) {
    let st = Command::new("git").args(args).current_dir(cwd).status().expect("git");
    assert!(st.success(), "git {args:?} falló");
}

fn init_repo(upstream: &Path) -> String {
    std::fs::create_dir_all(upstream).unwrap();
    std::fs::write(upstream.join("configure.ac"), CONFIGURE_AC).unwrap();
    std::fs::write(upstream.join("Makefile.am"), MAKEFILE_AM).unwrap();
    std::fs::write(upstream.join("hello.c"), HELLO_C).unwrap();
    git(&["init", "-q", "-b", "main"], upstream);
    git(&["config", "user.email", "test@hammer"], upstream);
    git(&["config", "user.name", "hammer-test"], upstream);
    git(&["add", "."], upstream);
    git(&["commit", "-q", "-m", "init"], upstream);
    let out = Command::new("git")
        .args(["rev-parse", "HEAD"])
        .current_dir(upstream)
        .output()
        .unwrap();
    String::from_utf8(out.stdout).unwrap().trim().to_string()
}

#[test]
fn build_autotools_hello_end_to_end() {
    let _ = tracing_subscriber::fmt().with_test_writer().try_init();

    let project = project_root();
    let cfg = BuildConfig {
        rootfs: project.join(".dev-fs/alpine"),
        zig_dir: project.join(".dev-fs/tools/zig"),
        work_root: tempfile::tempdir().unwrap().keep(),
        cache_root: Some(project.join(".dev-fs/cache")),
    };
    if skip_if_no_layout(&cfg) {
        return;
    }

    let tmp = tempfile::tempdir().unwrap();
    let store_root = tmp.path().join("store");
    std::fs::create_dir_all(&store_root).unwrap();

    let upstream = tmp.path().join("upstream");
    let commit = init_repo(&upstream);
    let repo_url = format!("file://{}", upstream.display());

    let recipe_dir = tmp.path().join("recipe");
    std::fs::create_dir_all(&recipe_dir).unwrap();
    let toml = RECIPE_TOML
        .replace("PLACEHOLDER_REPO_URL", &repo_url)
        .replace("PLACEHOLDER_COMMIT", &commit);
    let recipe_path = recipe_dir.join("helloat.toml");
    std::fs::write(&recipe_path, toml).unwrap();
    let recipe = Recipe::load_from_path(&recipe_path).expect("load recipe");

    let store = Store::open(&store_root).unwrap();
    let h = build(&recipe, &cfg, &store).expect("build helloat");
    let sealed = store.path_of(&h, &recipe.name);

    let bin = sealed.join("usr/bin/helloat");
    assert!(bin.is_file(), "esperaba {}", bin.display());

    let header = std::fs::read(&bin).unwrap();
    assert_eq!(&header[..4], b"\x7fELF", "es ELF");

    let out = Command::new("bwrap")
        .args([
            "--overlay-src",
            cfg.rootfs.to_str().unwrap(),
            "--tmp-overlay",
            "/",
            "--proc",
            "/proc",
            "--dev",
            "/dev",
            "--ro-bind",
            sealed.to_str().unwrap(),
            "/art",
            "--unshare-all",
            "/art/usr/bin/helloat",
        ])
        .output()
        .expect("spawn bwrap");
    assert!(out.status.success(), "helloat falló: {:?}", out);
    assert_eq!(String::from_utf8_lossy(&out.stdout).trim(), "hammer-autotools OK");
}
