//! ADR ref resolves / superseded checks.

use koala_core::invariant::Context;
use koala_drift::checks::feature_adr_refs::FeatureAdrRefs;
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();
}

fn adr(id: &str, status: &str, extra_fm: &str) -> String {
    format!("---\nid: {id}\ntitle: t\nstatus: {status}\ndate: 2026-01-01\n{extra_fm}---\n\n# ADR-{id}\n\nbody\n")
}

#[test]
fn dangling_adr_reference_blocks() {
    let tmp = TempDir::new().unwrap();
    write(
        tmp.path(),
        "wiki/features/x.md",
        "---\nid: x\nstatus: planned\n---\n\n## Related decisions\n\n- ADR-9999：phantom decision\n",
    );
    write(
        tmp.path(),
        "wiki/decisions/0001-real.md",
        &adr("0001", "accepted", ""),
    );

    let ctx = Context::new(tmp.path().to_path_buf());
    let findings = FeatureAdrRefs.run(&ctx);
    assert_eq!(findings.len(), 1, "{findings:#?}");
    assert_eq!(findings[0].kind, FindingKind::AdrRefDangling);
    assert_eq!(findings[0].severity, Severity::Hard);
}

#[test]
fn superseded_adr_reference_blocks() {
    let tmp = TempDir::new().unwrap();
    write(
        tmp.path(),
        "wiki/features/x.md",
        "---\nid: x\nstatus: planned\n---\n\n## Related decisions\n\n- ADR-0001：old\n",
    );
    write(
        tmp.path(),
        "wiki/decisions/0001-old.md",
        &adr("0001", "superseded", "superseded-by: 0002\n"),
    );
    write(
        tmp.path(),
        "wiki/decisions/0002-new.md",
        &adr("0002", "accepted", ""),
    );

    let ctx = Context::new(tmp.path().to_path_buf());
    let findings = FeatureAdrRefs.run(&ctx);
    assert_eq!(findings.len(), 1, "{findings:#?}");
    match &findings[0].kind {
        FindingKind::AdrRefSuperseded { superseder } => {
            assert_eq!(superseder.as_deref(), Some("0002"));
        }
        other => panic!("unexpected kind {other:?}"),
    }
    assert!(findings[0]
        .fix_hint
        .as_deref()
        .unwrap()
        .contains("ADR-0002"));
}

#[test]
fn fenced_examples_ignored() {
    let tmp = TempDir::new().unwrap();
    let body =
        "---\nid: x\nstatus: planned\n---\n\n## Why\n\n```\nADR-9999 in example output\n```\n";
    write(tmp.path(), "wiki/features/x.md", body);

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

#[test]
fn duplicate_id_on_same_line_dedup() {
    let tmp = TempDir::new().unwrap();
    write(
        tmp.path(),
        "wiki/features/x.md",
        "---\nid: x\n---\n\n## R\n\n- ADR-9999 see also ADR-9999\n",
    );
    let ctx = Context::new(tmp.path().to_path_buf());
    assert_eq!(FeatureAdrRefs.run(&ctx).len(), 1);
}

#[test]
fn five_digit_typo_not_silently_absorbed() {
    // `ADR-99999` (one extra digit) must NOT be silently parsed as a
    // reference to ADR-9999. The 4-digit ADR exists and is accepted; if
    // the regex over-matched, the typo would silently pass. Word
    // boundaries prevent that — the typo is unmatched, not misdirected.
    let tmp = TempDir::new().unwrap();
    write(
        tmp.path(),
        "wiki/features/x.md",
        "---\nid: x\n---\n\n## Related decisions\n\n- ADR-99999 typo not real\n",
    );
    write(
        tmp.path(),
        "wiki/decisions/9999-real.md",
        &adr("9999", "accepted", ""),
    );

    let ctx = Context::new(tmp.path().to_path_buf());
    let findings = FeatureAdrRefs.run(&ctx);
    assert_eq!(
        findings.len(),
        0,
        "ADR-99999 must not match ADR-9999: {findings:#?}"
    );
}

#[test]
fn accepted_adr_passes() {
    let tmp = TempDir::new().unwrap();
    write(
        tmp.path(),
        "wiki/features/x.md",
        "---\nid: x\n---\n\n## Related decisions\n\n- ADR-0001：fine\n",
    );
    write(
        tmp.path(),
        "wiki/decisions/0001-real.md",
        &adr("0001", "accepted", ""),
    );

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