//! Guards AGENTS.md against layout drift (WOR-1340).
//!
//! AGENTS.md is the first file every coding agent reads; a stale layout
//! block sends them to crates that do not exist. These tests pin the
//! layout block to the real workspace so the map cannot rot silently.

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

fn workspace_root() -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("..")
        .join("..")
}

fn read(path: &Path) -> String {
    std::fs::read_to_string(path).unwrap_or_else(|e| panic!("cannot read {}: {e}", path.display()))
}

/// Workspace member paths parsed from the root Cargo.toml `members` list.
fn workspace_members(root: &Path) -> Vec<String> {
    let manifest = read(&root.join("Cargo.toml"));
    let start = manifest
        .find("members = [")
        .expect("workspace Cargo.toml has a members list");
    let rest = &manifest[start..];
    let end = rest.find(']').expect("members list is closed");
    rest[..end]
        .lines()
        .filter_map(|line| {
            let line = line.trim().trim_end_matches(',');
            line.strip_prefix('"')
                .and_then(|s| s.strip_suffix('"'))
                .map(str::to_string)
        })
        .collect()
}

#[test]
fn agents_md_layout_names_every_workspace_crate() {
    let root = workspace_root();
    let agents = read(&root.join("AGENTS.md"));
    let members = workspace_members(&root);
    assert!(!members.is_empty(), "parsed no workspace members");

    for member in &members {
        // The layout block lists crates by directory name; sdks crates are
        // covered by the single `sdks/` line item.
        let name = member
            .rsplit('/')
            .next()
            .expect("member has a final path component");
        let listed_individually = agents.contains(&format!("{name}/"));
        let covered_by_sdks_line = member.starts_with("sdks/") && agents.contains("sdks/");
        assert!(
            listed_individually || covered_by_sdks_line,
            "workspace member `{member}` is not reflected in the AGENTS.md layout block"
        );
    }
}

#[test]
fn agents_md_layout_lists_no_phantom_crates() {
    let root = workspace_root();
    let agents = read(&root.join("AGENTS.md"));
    let members = workspace_members(&root);

    // Every `mcptest-<thing>/` entry in the layout block must be a real
    // workspace member. This is the exact drift WOR-1340 caught (the block
    // listed mcptest-cassette and mcptest-report long after both died).
    for line in agents.lines() {
        let trimmed = line.trim();
        let Some(rest) = trimmed.strip_prefix("mcptest-") else {
            continue;
        };
        let Some(dir) = rest.split('/').next().filter(|_| rest.contains('/')) else {
            continue;
        };
        let crate_name = format!("mcptest-{dir}");
        let known = members.iter().any(|m| m.ends_with(&crate_name));
        assert!(
            known,
            "AGENTS.md layout lists `{crate_name}/` but it is not a workspace member"
        );
    }
}

#[test]
fn agents_md_module_size_ceiling_matches_the_gate_script() {
    let root = workspace_root();
    let agents = read(&root.join("AGENTS.md"));
    let script = read(&root.join("scripts").join("check-module-size.sh"));
    let threshold_line = script
        .lines()
        .find(|l| l.trim_start().starts_with("THRESHOLD="))
        .expect("check-module-size.sh defines THRESHOLD");
    let ceiling = threshold_line
        .trim()
        .trim_start_matches("THRESHOLD=")
        .trim();
    assert!(
        agents.contains(ceiling),
        "AGENTS.md must state the enforced module-size ceiling ({ceiling}) \
         from scripts/check-module-size.sh so the rule and the gate agree"
    );
}
