//! End-to-end: spawn the koala-core binary with `.koala/invariants/<x>.toml`
//! present and confirm the user-defined rule shows up in the output.

use std::fs;
use std::process::Command;
use tempfile::TempDir;

fn binary_path() -> &'static str {
    env!("CARGO_BIN_EXE_koala-core")
}

fn write(root: &std::path::Path, rel: &str, body: &str) {
    let p = root.join(rel);
    fs::create_dir_all(p.parent().unwrap()).unwrap();
    fs::write(p, body).unwrap();
}

fn minimal_repo(tmp: &std::path::Path) {
    // Enough scaffolding that the builtin invariants either Pass or
    // Skip — we only care that the user TOML rule rides along.
    write(
        tmp,
        "README.md",
        "# x\n\nA fixture README with enough body to satisfy the docs.readme-exists invariant's byte threshold.\n",
    );
    write(
        tmp,
        "CLAUDE.md",
        "# CLAUDE.md\n\nProject onboarding notes.\n",
    );
    write(
        tmp,
        "Cargo.toml",
        "[workspace]\nresolver = \"2\"\n[workspace.package]\nedition = \"2021\"\nlicense = \"Apache-2.0\"\n",
    );
    write(tmp, "LICENSE", "Apache 2.0\n");
}

#[test]
fn check_loads_user_toml_invariants() {
    let tmp = TempDir::new().unwrap();
    minimal_repo(tmp.path());
    write(
        tmp.path(),
        ".koala/invariants/biz.toml",
        r##"
[[rule]]
id       = "biz.readme-greets"
category = "docs"
intent   = "README must include a banner heading."

[rule.match]
kind   = "require-substring"
glob   = "README.md"
needle = "# x"
"##,
    );

    let out = Command::new(binary_path())
        .arg("check")
        .arg("--root")
        .arg(tmp.path())
        .arg("--profile")
        .arg("light")
        .arg("--invariant-only")
        .output()
        .expect("run koala-core check");
    let stdout = String::from_utf8_lossy(&out.stdout);

    assert!(
        stdout.contains("[+1 user-defined]"),
        "expected `[+1 user-defined]` annotation in stdout:\n{stdout}"
    );
}

#[test]
fn check_user_toml_failure_blocks_run() {
    let tmp = TempDir::new().unwrap();
    minimal_repo(tmp.path());
    write(
        tmp.path(),
        ".koala/invariants/banned.toml",
        r##"
[[rule]]
id       = "biz.no-banned-word"
category = "docs"
intent   = "README must not contain the banned word."

[rule.match]
kind   = "forbid-substring"
glob   = "README.md"
needle = "fixture"
"##,
    );

    let out = Command::new(binary_path())
        .arg("check")
        .arg("--root")
        .arg(tmp.path())
        .arg("--profile")
        .arg("light")
        .arg("--invariant-only")
        .output()
        .expect("run koala-core check");
    let stdout = String::from_utf8_lossy(&out.stdout);

    assert!(
        !out.status.success(),
        "expected non-zero exit on failing user rule; stdout:\n{stdout}"
    );
    assert!(
        stdout.contains("FAIL  biz.no-banned-word"),
        "expected FAIL line for user rule:\n{stdout}"
    );
    assert!(
        stdout.contains("forbidden substring `fixture`"),
        "expected detail message:\n{stdout}"
    );
}

#[test]
fn check_skips_when_user_dir_absent() {
    let tmp = TempDir::new().unwrap();
    minimal_repo(tmp.path());
    let out = Command::new(binary_path())
        .arg("check")
        .arg("--root")
        .arg(tmp.path())
        .arg("--profile")
        .arg("light")
        .arg("--invariant-only")
        .output()
        .expect("run koala-core check");
    let stdout = String::from_utf8_lossy(&out.stdout);
    assert!(
        !stdout.contains("user-defined"),
        "no `user-defined` annotation when `.koala/invariants/` absent:\n{stdout}"
    );
    assert!(
        out.status.success(),
        "minimal repo with no user rules should pass:\n{stdout}"
    );
}
