//! Integration test for `arai canonicalize`.
//!
//! Runs the built binary against this repo (the test's CWD is
//! `CARGO_MANIFEST_DIR`), captures the generated TOML in a tempfile, and
//! asserts the output is well-formed and contains the schema fields
//! documented in `docs/rules-file-spec.md`.
//!
//! The repo's own CLAUDE.md + AGENTS.md guarantee a non-empty rule set,
//! so we don't need a custom fixture; this also catches accidental
//! regressions where discovery silently stops finding the project files.

use std::process::Command;

#[test]
fn canonicalize_emits_valid_toml() {
    let bin = env!("CARGO_BIN_EXE_arai");
    let tmp = std::env::temp_dir().join(format!(
        "arai-canonicalize-test-{}.toml",
        std::process::id()
    ));
    // Ensure a clean slate so --force semantics aren't exercised here.
    let _ = std::fs::remove_file(&tmp);

    let status = Command::new(bin)
        .arg("canonicalize")
        .arg("--output")
        .arg(&tmp)
        .status()
        .expect("spawn arai canonicalize");
    assert!(status.success(), "arai canonicalize exited non-zero");

    let body = std::fs::read_to_string(&tmp).expect("read output file");
    let _ = std::fs::remove_file(&tmp);

    // Header comment is present before the TOML body.
    assert!(
        body.contains("# Generated by `arai canonicalize`"),
        "missing generated-by header"
    );
    assert!(
        body.contains("# Schema: docs/rules-file-spec.md"),
        "missing schema pointer"
    );

    // Parse as TOML and assert schema-conforming shape.
    let v: toml::Value = toml::from_str(&body).expect("output should parse as TOML");

    let meta = v.get("meta").expect("meta table required");
    assert_eq!(
        meta.get("schema_version").and_then(|x| x.as_integer()),
        Some(1),
        "schema_version must be 1"
    );

    let rules = v
        .get("rule")
        .and_then(|r| r.as_array())
        .expect("rule array required");
    assert!(
        !rules.is_empty(),
        "this repo has CLAUDE.md + AGENTS.md — expected at least one rule"
    );

    // Spot-check that the first rule carries the schema's required fields.
    let r0 = &rules[0];
    for field in ["id", "description", "severity"] {
        assert!(
            r0.get(field).is_some(),
            "rule missing required field `{field}`"
        );
    }
    assert!(
        r0.get("when").and_then(|w| w.get("tool")).is_some(),
        "rule missing when.tool"
    );
    assert!(
        r0.get("then").and_then(|t| t.get("action")).is_some(),
        "rule missing then.action"
    );
    assert!(
        r0.get("then").and_then(|t| t.get("message")).is_some(),
        "rule missing then.message"
    );

    // Severity must be one of the documented values.
    let sev = r0.get("severity").and_then(|x| x.as_str()).unwrap_or("");
    assert!(
        matches!(sev, "block" | "warn" | "inform"),
        "severity `{sev}` not in {{block, warn, inform}}"
    );
}

#[test]
fn canonicalize_refuses_to_overwrite_without_force() {
    let bin = env!("CARGO_BIN_EXE_arai");
    let tmp = std::env::temp_dir().join(format!(
        "arai-canonicalize-overwrite-test-{}.toml",
        std::process::id()
    ));
    std::fs::write(&tmp, "# pre-existing content\n").expect("seed tempfile");

    let out = Command::new(bin)
        .arg("canonicalize")
        .arg("--output")
        .arg(&tmp)
        .output()
        .expect("spawn arai canonicalize");
    assert!(
        !out.status.success(),
        "should refuse to overwrite without --force"
    );
    let stderr = String::from_utf8_lossy(&out.stderr);
    assert!(
        stderr.contains("already exists") || stderr.contains("--force"),
        "error message should mention --force; got: {stderr}"
    );

    // File content untouched.
    let body = std::fs::read_to_string(&tmp).expect("read tempfile");
    assert_eq!(body, "# pre-existing content\n");
    let _ = std::fs::remove_file(&tmp);
}
