//! Acceptance criterion → real path drift check.

use koala_core::invariant::Context;
use koala_drift::checks::feature_acceptance::FeatureAcceptanceTestRef;
use koala_drift::{Check, FindingKind, Severity};
use std::fs;
use tempfile::TempDir;

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

const FEATURE_HEAD: &str =
    "---\nid: x\nstatus: in-progress\n---\n# X\n\n## Acceptance criteria（机械可验证）\n\n";

#[test]
fn missing_test_blocks_pr() {
    let tmp = TempDir::new().unwrap();
    let body = format!("{FEATURE_HEAD}- [ ] does the thing\n  → crates/x/tests/missing.rs#none\n");
    write(tmp.path(), "wiki/features/x.md", &body);

    let ctx = Context::new(tmp.path().to_path_buf());
    let findings = FeatureAcceptanceTestRef.run(&ctx);
    assert_eq!(findings.len(), 1, "expected one finding, got {findings:#?}");
    let f = &findings[0];
    assert_eq!(f.severity, Severity::Hard);
    assert_eq!(f.kind, FindingKind::AcceptanceTestRefMissing);
    assert_eq!(f.line, 10);
    assert!(f
        .fix_hint
        .as_deref()
        .unwrap()
        .contains("crates/x/tests/missing.rs"));
}

#[test]
fn forecast_marker_skipped() {
    let tmp = TempDir::new().unwrap();
    let body =
        format!("{FEATURE_HEAD}- [ ] forecast\n  → crates/x/tests/missing.rs#none（待创建）\n");
    write(tmp.path(), "wiki/features/x.md", &body);

    let ctx = Context::new(tmp.path().to_path_buf());
    assert_eq!(FeatureAcceptanceTestRef.run(&ctx).len(), 0);
}

#[test]
fn existing_path_passes() {
    let tmp = TempDir::new().unwrap();
    let body = format!("{FEATURE_HEAD}- [x] real\n  → crates/x/tests/real.rs#name\n");
    write(tmp.path(), "wiki/features/x.md", &body);
    write(tmp.path(), "crates/x/tests/real.rs", "// stub\n");

    let ctx = Context::new(tmp.path().to_path_buf());
    assert_eq!(FeatureAcceptanceTestRef.run(&ctx).len(), 0);
}

#[test]
fn arrow_in_prose_ignored() {
    let tmp = TempDir::new().unwrap();
    let body = format!(
        "{FEATURE_HEAD}\
- [ ] commit → verify flow works\n\
  → crates/x/tests/real.rs#name\n"
    );
    write(tmp.path(), "wiki/features/x.md", &body);
    write(tmp.path(), "crates/x/tests/real.rs", "// stub\n");

    let ctx = Context::new(tmp.path().to_path_buf());
    let findings = FeatureAcceptanceTestRef.run(&ctx);
    assert_eq!(
        findings.len(),
        0,
        "prose `commit → verify` must not be parsed as a claim"
    );
}

#[test]
fn outside_acceptance_section_ignored() {
    let tmp = TempDir::new().unwrap();
    // Real ADR-style → line in a body section (not Acceptance criteria) — must
    // not be treated as an acceptance claim.
    let body =
        "---\nid: x\nstatus: planned\n---\n# X\n\n## Why\n\n  → crates/x/tests/missing.rs#none\n";
    write(tmp.path(), "wiki/features/x.md", body);

    let ctx = Context::new(tmp.path().to_path_buf());
    assert_eq!(FeatureAcceptanceTestRef.run(&ctx).len(), 0);
}

#[test]
fn templates_with_underscore_prefix_skipped() {
    let tmp = TempDir::new().unwrap();
    let body = format!("{FEATURE_HEAD}- [ ] placeholder\n  → tests/integration/<file>.rs#<name>\n");
    write(tmp.path(), "wiki/features/_template.md", &body);

    let ctx = Context::new(tmp.path().to_path_buf());
    assert_eq!(FeatureAcceptanceTestRef.run(&ctx).len(), 0);
}
