//! WOR-1312: no silent stub-provider fallback for agent tests.
//!
//! With no provider API key in the environment, `mcptest run` used to warn
//! and silently dispatch agent cells against the canned stub provider, so a
//! green report could mean nothing was tested. The default is now a hard,
//! actionable error naming the env var (or the unsupported model string) and
//! the agent/test. `--allow-stub-provider` restores the old behavior for
//! offline smoke runs, and every affected cell is marked `stub:` in the
//! output so the report is unmistakable.
//!
//! The fixture server is the native `mcptest mock` so the tests need no
//! Python or Node, mirroring `cli_passk_floor.rs`.

#![cfg(unix)]

use std::path::Path;

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

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

/// Write a `mcptest mock` manifest exposing a `get_weather` tool and return
/// the JSON-array server command to splice into a suite's `command:`.
fn weather_server_command(dir: &Path) -> String {
    let manifest = dir.join("get_weather.yaml");
    std::fs::write(
        &manifest,
        r#"mock_server:
  name: weather-fixture
  tools:
    - name: get_weather
      description: "Get the weather for a city"
      input_schema:
        type: object
        properties:
          city: { type: string }
        required: [city]
      response:
        content:
          - type: text
            text: "It is 72 F and sunny."
"#,
    )
    .expect("write mock manifest");
    let bin = env!("CARGO_BIN_EXE_mcptest");
    format!(
        r#"['{bin}', 'mock', '--tools-from', '{}']"#,
        manifest.display()
    )
}

/// Strip provider credentials so the run cannot resolve a real provider
/// regardless of the developer's shell.
fn strip_provider_keys(cmd: &mut Command) {
    for var in [
        "ANTHROPIC_API_KEY",
        "OPENAI_API_KEY",
        "GEMINI_API_KEY",
        "GOOGLE_API_KEY",
        "MISTRAL_API_KEY",
    ] {
        cmd.env_remove(var);
    }
}

/// One-agent suite against the mock weather server, with `model` spliced in.
fn write_agent_suite(dir: &Path, model: &str) -> std::path::PathBuf {
    let server_cmd = weather_server_command(dir);
    let yaml = format!(
        r#"servers:
  weather:
    command: {server_cmd}
agents:
  - name: weather agent
    model: {model}
    servers: [weather]
    prompt: What is the weather in Sacramento?
"#,
    );
    let path = dir.join("mcptest.yaml");
    std::fs::write(&path, yaml).expect("write suite");
    path
}

#[test]
fn missing_credential_fails_the_run_naming_the_env_var_and_agent() {
    let dir = tempfile::tempdir().unwrap();
    let config = write_agent_suite(dir.path(), "claude-sonnet-4-5");

    let mut cmd = mcptest();
    strip_provider_keys(&mut cmd);
    cmd.current_dir(dir.path())
        .args(["run", "--config"])
        .arg(&config)
        .assert()
        .code(1)
        .stderr(
            predicate::str::contains("ANTHROPIC_API_KEY")
                .and(predicate::str::contains("weather agent"))
                .and(predicate::str::contains("--allow-stub-provider")),
        );
}

#[test]
fn unsupported_model_family_fails_the_run_naming_the_model() {
    let dir = tempfile::tempdir().unwrap();
    let config = write_agent_suite(dir.path(), "mystery-model-9000");

    let mut cmd = mcptest();
    strip_provider_keys(&mut cmd);
    cmd.current_dir(dir.path())
        .args(["run", "--config"])
        .arg(&config)
        .assert()
        .code(1)
        .stderr(
            predicate::str::contains("mystery-model-9000")
                .and(predicate::str::contains("--allow-stub-provider")),
        );
}

#[test]
fn allow_stub_provider_runs_offline_and_marks_the_stub_in_the_output() {
    let dir = tempfile::tempdir().unwrap();
    let config = write_agent_suite(dir.path(), "claude-sonnet-4-5");

    let mut cmd = mcptest();
    strip_provider_keys(&mut cmd);
    cmd.current_dir(dir.path())
        .args(["run", "--allow-stub-provider", "--config"])
        .arg(&config)
        .assert()
        .code(0)
        // The per-cell stub line plus the `(provider)` report row both lead
        // with `stub:` so neither a human scan nor a machine diff can read
        // this as a real model run.
        .stdout(
            predicate::str::contains("stub:claude-sonnet-4-5")
                .and(predicate::str::contains("weather agent (provider)")),
        );
}
