//! Stable troubleshooting URLs appended to runtime error messages.
//!
//! Why one module: docs/troubleshooting.md is symptom-keyed, but until
//! WOR-1268 no error message pointed at it, so the operator (or agent)
//! who hit "schema validation failed" had to find the entry by hand.
//! Each constant here is the full `https://mcptest.sh/docs/troubleshooting`
//! URL for one error family; emission sites append it as a trailing
//! `see: <url>` reference. The anchors are contracts with the doc's
//! `<a id="...">` tags, enforced by the doc-contract test in
//! `crates/mcptest/tests/docs_troubleshooting_anchors.rs`.

/// Base URL of the troubleshooting page. Every anchor constant below is
/// this base plus a `#fragment`; the doc-contract test pins that shape.
pub const TROUBLESHOOTING_BASE: &str = "https://mcptest.sh/docs/troubleshooting";

/// Connection refused or server unreachable (TCP-layer failures).
pub const SERVER_UNREACHABLE: &str = "https://mcptest.sh/docs/troubleshooting#server-unreachable";

/// User YAML failed schema validation at load time.
pub const CONFIG_VALIDATION_FAILED: &str =
    "https://mcptest.sh/docs/troubleshooting#config-validation-failed";

/// An `expect:` block names a matcher the catalog does not know.
pub const UNKNOWN_MATCHER: &str = "https://mcptest.sh/docs/troubleshooting#unknown-matcher";

/// An assertion target path did not resolve inside the response envelope.
pub const TARGET_DID_NOT_RESOLVE: &str =
    "https://mcptest.sh/docs/troubleshooting#target-did-not-resolve";

/// The server rejected the credential (HTTP 401 or 403).
pub const AUTH_FAILURE: &str = "https://mcptest.sh/docs/troubleshooting#401-after-mcptest-login";

/// A test hit its per-test timeout before the matcher completed.
pub const TEST_TIMEOUT: &str = "https://mcptest.sh/docs/troubleshooting#tests-time-out-at-60s";

/// A live request did not match any remaining recorded cassette exchange.
pub const CASSETTE_REPLAY_MISMATCH: &str =
    "https://mcptest.sh/docs/troubleshooting#cassette-replay-mismatch";

/// A saved snapshot no longer matches the live response.
pub const SNAPSHOT_DRIFT: &str =
    "https://mcptest.sh/docs/troubleshooting#snapshot-mismatch-on-every-run";

/// A `${VAR}` reference could not be interpolated at load time.
pub const VARIABLE_INTERPOLATION: &str = "https://mcptest.sh/docs/troubleshooting#env-var-not-set";

/// A stdio server command failed to spawn (missing binary, bad permissions).
pub const STDIO_SPAWN_FAILED: &str = "https://mcptest.sh/docs/troubleshooting#stdio-spawn-failed";

/// A stdio server process started but exited before completing the MCP
/// `initialize` handshake (WOR-1326).
pub const SERVER_EXITED_DURING_STARTUP: &str =
    "https://mcptest.sh/docs/troubleshooting#server-exited-during-startup";

/// An agent cassette's recorded model or prompts drifted from the YAML
/// (WOR-1288); re-record instead of replaying the wrong recording.
pub const AGENT_CASSETTE_STALE: &str =
    "https://mcptest.sh/docs/troubleshooting#agent-cassette-stale";

/// Every anchor constant, so the doc-contract test can sweep them without
/// hand-maintaining a second list that drifts from this module.
pub const ALL: [&str; 12] = [
    SERVER_UNREACHABLE,
    CONFIG_VALIDATION_FAILED,
    UNKNOWN_MATCHER,
    TARGET_DID_NOT_RESOLVE,
    AUTH_FAILURE,
    TEST_TIMEOUT,
    CASSETTE_REPLAY_MISMATCH,
    SNAPSHOT_DRIFT,
    VARIABLE_INTERPOLATION,
    STDIO_SPAWN_FAILED,
    SERVER_EXITED_DURING_STARTUP,
    AGENT_CASSETTE_STALE,
];

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn every_anchor_extends_the_base_url_with_a_fragment() {
        for url in ALL {
            let rest = url
                .strip_prefix(TROUBLESHOOTING_BASE)
                .unwrap_or_else(|| panic!("`{url}` does not start with the base URL"));
            assert!(
                rest.starts_with('#') && rest.len() > 1,
                "`{url}` has no #fragment"
            );
        }
    }

    #[test]
    fn anchors_are_unique_per_family() {
        let mut seen = std::collections::BTreeSet::new();
        for url in ALL {
            assert!(seen.insert(url), "duplicate anchor URL `{url}`");
        }
    }
}
