//! Integration tests for `mcptest evidence pack` and `mcptest evidence diff`
//! (WOR-1412).
//!
//! `pack` folds a run report (plus optional sections) into a versioned
//! governance envelope; `diff` compares two packs and gates on regressions.
//! These tests write temp fixtures, run the binary, and assert on the rendered
//! pack and the diff's exit code, so they need no server or network.

use std::io::Write;

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

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

fn temp(body: &str) -> tempfile::NamedTempFile {
    let mut f = tempfile::NamedTempFile::new().expect("temp file");
    f.write_all(body.as_bytes()).expect("write");
    f
}

/// A minimal run report with metadata and a cost block.
fn report_json(passed: usize, failed: usize) -> String {
    let total = passed + failed;
    format!(
        r#"{{
  "schema_version": "0.1",
  "mcptest_version": "1.0.0",
  "generated_at": "2026-06-21T00:00:00Z",
  "run_id": "01J0",
  "summary": {{ "total": {total}, "passed": {passed}, "failed": {failed}, "skipped": 0, "duration_ms": 5 }},
  "tests": [],
  "cost": {{ "total_cost_usd": 0.25, "cost_per_juror_avg": 0.0, "cost_per_test_avg": 0.25, "juror_calls": 0, "tests_with_cost": 1 }},
  "metadata": {{
    "run_id": "01J0",
    "mcptest_version": "1.0.0",
    "runner_environment": "local",
    "os": "linux",
    "arch": "x86_64",
    "ended_at": "2026-06-21T00:00:00Z",
    "mcp_protocol_version": "2025-06-18",
    "servers": [{{ "name": "api", "transport": "stdio", "auth": "none" }}]
  }}
}}"#
    )
}

/// Build a pack JSON to a path via the CLI.
fn build_pack(report_body: &str, out: &std::path::Path) {
    let report = temp(report_body);
    mcptest()
        .arg("evidence")
        .arg("pack")
        .arg(report.path())
        .arg("--output")
        .arg(out)
        .assert()
        .code(0);
}

#[test]
fn pack_emits_a_versioned_envelope_with_sections() {
    let report = temp(&report_json(9, 1));
    mcptest()
        .args(["evidence", "pack"])
        .arg(report.path())
        .assert()
        .code(0)
        .stdout(
            predicate::str::contains("mcptest.dev/evidence-pack/v1")
                .and(predicate::str::contains("\"tests_passed\": 9"))
                // the run report's cost block folds in as a section
                .and(predicate::str::contains("total_cost_usd"))
                // the pack carries its own content digest
                .and(predicate::str::contains("\"digest\": \"sha256:")),
        );
}

#[test]
fn pack_markdown_is_human_readable() {
    let report = temp(&report_json(9, 1));
    mcptest()
        .args(["evidence", "pack"])
        .arg(report.path())
        .args(["--format", "markdown"])
        .assert()
        .code(0)
        .stdout(
            predicate::str::contains("# Evidence pack")
                .and(predicate::str::contains("9 passed, 1 failed")),
        );
}

#[test]
fn pack_folds_a_named_sidecar_attachment() {
    let report = temp(&report_json(5, 0));
    let sidecar = temp(r#"{"drift": "none"}"#);
    let arg = format!("model_compat={}", sidecar.path().display());
    mcptest()
        .args(["evidence", "pack"])
        .arg(report.path())
        .args(["--section", &arg])
        .assert()
        .code(0)
        .stdout(predicate::str::contains("\"model_compat\""));
}

#[test]
fn diff_flags_a_regression_and_exits_one() {
    let dir = tempfile::tempdir().unwrap();
    let old = dir.path().join("old.json");
    let new = dir.path().join("new.json");
    build_pack(&report_json(10, 0), &old);
    build_pack(&report_json(8, 2), &new);

    mcptest()
        .args(["evidence", "diff"])
        .arg(&old)
        .arg(&new)
        .assert()
        .code(1)
        .stdout(
            predicate::str::contains("tests_failed")
                .and(predicate::str::contains("REGRESSED"))
                .and(predicate::str::contains("regression(s)")),
        );
}

#[test]
fn diff_without_regression_exits_zero() {
    let dir = tempfile::tempdir().unwrap();
    let old = dir.path().join("old.json");
    let new = dir.path().join("new.json");
    build_pack(&report_json(8, 2), &old);
    // Strictly better: more passing, fewer failing.
    build_pack(&report_json(10, 0), &new);

    mcptest()
        .args(["evidence", "diff"])
        .arg(&old)
        .arg(&new)
        .assert()
        .code(0)
        .stdout(predicate::str::contains("improved"));
}

#[test]
fn diff_can_report_without_gating() {
    let dir = tempfile::tempdir().unwrap();
    let old = dir.path().join("old.json");
    let new = dir.path().join("new.json");
    build_pack(&report_json(10, 0), &old);
    build_pack(&report_json(8, 2), &new);

    // A regression is present, but --fail-on-regression false keeps exit 0.
    mcptest()
        .args(["evidence", "diff"])
        .arg(&old)
        .arg(&new)
        .args(["--fail-on-regression", "false"])
        .assert()
        .code(0);
}
