//! Integration tests for `mcptest schema-lint` (WOR-1388).
//!
//! The command lints the input schemas in a saved `tools/list` snapshot for
//! under-constrained shapes (`SCH-NNN`) and can apply the autofix that adds the
//! mechanical constraints. These tests drive the built binary over a temp
//! snapshot file, mirroring the other `assert_cmd` CLI tests.

use assert_cmd::Command;
use predicates::prelude::*;

fn mcptest() -> Command {
    Command::cargo_bin("mcptest").expect("binary builds")
}

/// Write a `tools/list` snapshot with one under-constrained tool (no
/// `additionalProperties: false`, no `required`) and return its path. Every
/// property carries a type, so the autofix tightens it to a clean catalog.
fn write_loose_snapshot(dir: &std::path::Path) -> std::path::PathBuf {
    let snapshot = r#"{
  "tools": [
    {
      "name": "search",
      "description": "Search the corpus.",
      "inputSchema": {
        "type": "object",
        "properties": { "query": { "type": "string", "maxLength": 200 } }
      }
    }
  ]
}"#;
    let path = dir.join("tools.json");
    std::fs::write(&path, snapshot).unwrap();
    path
}

#[test]
fn schema_lint_flags_an_underconstrained_snapshot() {
    let dir = tempfile::tempdir().unwrap();
    let snapshot = write_loose_snapshot(dir.path());
    mcptest()
        .args(["schema-lint", snapshot.to_str().unwrap()])
        .assert()
        .failure()
        .code(1)
        .stdout(predicate::str::contains("SCH-"));
}

#[test]
fn schema_lint_no_fail_exits_zero_with_findings() {
    let dir = tempfile::tempdir().unwrap();
    let snapshot = write_loose_snapshot(dir.path());
    mcptest()
        .args(["schema-lint", snapshot.to_str().unwrap(), "--no-fail"])
        .assert()
        .success()
        .stdout(predicate::str::contains("SCH-"));
}

#[test]
fn schema_lint_json_emits_a_summary_line() {
    let dir = tempfile::tempdir().unwrap();
    let snapshot = write_loose_snapshot(dir.path());
    mcptest()
        .args([
            "schema-lint",
            snapshot.to_str().unwrap(),
            "--format",
            "json",
        ])
        .assert()
        .code(1)
        .stdout(predicate::str::contains("\"summary\""));
}

#[test]
fn schema_lint_fix_prints_the_tightened_catalog() {
    let dir = tempfile::tempdir().unwrap();
    let snapshot = write_loose_snapshot(dir.path());
    mcptest()
        .args(["schema-lint", snapshot.to_str().unwrap(), "--fix"])
        .assert()
        .success()
        .stdout(predicate::str::contains("additionalProperties"));
}

#[test]
fn schema_lint_fix_write_rewrites_the_snapshot_clean() {
    let dir = tempfile::tempdir().unwrap();
    let snapshot = write_loose_snapshot(dir.path());
    // Apply the autofix in place.
    mcptest()
        .args([
            "schema-lint",
            snapshot.to_str().unwrap(),
            "--fix",
            "--write",
        ])
        .assert()
        .success();
    // The rewritten snapshot now lints clean.
    mcptest()
        .args(["schema-lint", snapshot.to_str().unwrap()])
        .assert()
        .success()
        .stdout(predicate::str::contains("no findings"));
}

#[test]
fn schema_lint_write_requires_fix() {
    let dir = tempfile::tempdir().unwrap();
    let snapshot = write_loose_snapshot(dir.path());
    // `--write` without `--fix` is a usage error (clap `requires`).
    mcptest()
        .args(["schema-lint", snapshot.to_str().unwrap(), "--write"])
        .assert()
        .failure()
        .code(2);
}
