//! Arguments for `mcptest generate`.

use std::path::PathBuf;

use clap::{Args, Subcommand};

/// Arguments for `mcptest generate`.
///
/// Wrapped under its own subcommand enum so future generators (state-machine
/// sequences, fixtures, etc.) can land as siblings under the
/// same namespace.
#[derive(Debug, Args)]
pub struct GenerateArgs {
    /// Which generator to run.
    #[command(subcommand)]
    pub command: GenerateCommand,
}

/// Subcommands for `mcptest generate`.
#[derive(Debug, Subcommand)]
pub enum GenerateCommand {
    /// Emit one runnable YAML test stub per advertised tool.
    Stubs(GenerateStubsCliArgs),
    /// Synthesize one starter suite from a server's declared tools.
    Suite(GenerateSuiteCliArgs),
    /// Synthesize a protocol-conformance suite for the server's capabilities.
    Conformance(GenerateConformanceCliArgs),
    /// Synthesize behavioral red-team probes tuned to the server's tools.
    Redteam(GenerateRedteamCliArgs),
    /// Synthesize an agent-loop eval (one judged case per tool).
    Eval(GenerateEvalCliArgs),
}

/// Arguments for `mcptest generate conformance`.
///
/// Reads the server's advertised capabilities from a config/snapshot file (an
/// `initialize` snapshot with a `capabilities` object, or any catalog whose
/// declared `tools[]`/`resources[]`/`prompts[]` imply the capability) and
/// emits a `compliance:` suite with one check per advertised capability.
#[derive(Debug, Args)]
pub struct GenerateConformanceCliArgs {
    /// Read advertised capabilities from this file. Accepts an `initialize`
    /// snapshot (`{"capabilities": {...}}`), a `tools/list` snapshot, or a
    /// mock manifest (`mock_server.{tools,resources,prompts}[]`).
    #[arg(long = "from-config", value_name = "FILE")]
    pub from_config: PathBuf,

    /// Server key the generated checks reference, also the `servers:` entry.
    #[arg(long, value_name = "NAME", default_value = "server")]
    pub server_name: String,

    /// MCP spec revision to pin under `compliance.spec_version`. Omit to use
    /// the built-in default revision.
    #[arg(long = "spec-version", value_name = "VERSION")]
    pub spec_version: Option<String>,

    /// Write the suite to this path instead of stdout.
    #[arg(long, value_name = "PATH")]
    pub output: Option<PathBuf>,
}

/// Arguments for `mcptest generate redteam`.
///
/// Reads a tool catalog from a config/snapshot file and emits behavioral
/// injection-resistance probes, one agent test per tool.
#[derive(Debug, Args)]
pub struct GenerateRedteamCliArgs {
    /// Read the declared tool list from this file. Accepts a `tools/list`
    /// snapshot, a bare tools array, or a mock manifest (`mock_server.tools[]`).
    #[arg(long = "from-config", value_name = "FILE")]
    pub from_config: PathBuf,

    /// Server key the generated probes reference, also the `servers:` entry.
    #[arg(long, value_name = "NAME", default_value = "server")]
    pub server_name: String,

    /// Model identifier each probe runs against. Omit to use the default.
    #[arg(long, value_name = "ID")]
    pub model: Option<String>,

    /// Write the suite to this path instead of stdout.
    #[arg(long, value_name = "PATH")]
    pub output: Option<PathBuf>,
}

/// Arguments for `mcptest generate eval`.
///
/// Reads a tool catalog from a config/snapshot file and emits an agent-loop
/// eval, one judged case per tool.
#[derive(Debug, Args)]
pub struct GenerateEvalCliArgs {
    /// Read the declared tool list from this file. Accepts a `tools/list`
    /// snapshot, a bare tools array, or a mock manifest (`mock_server.tools[]`).
    #[arg(long = "from-config", value_name = "FILE")]
    pub from_config: PathBuf,

    /// Server key the generated agent tests reference, also the `servers:`
    /// entry.
    #[arg(long, value_name = "NAME", default_value = "server")]
    pub server_name: String,

    /// Model identifier each agent test runs against. Omit to use the default.
    #[arg(long, value_name = "ID")]
    pub model: Option<String>,

    /// Write the suite to this path instead of stdout.
    #[arg(long, value_name = "PATH")]
    pub output: Option<PathBuf>,
}

/// Arguments for `mcptest generate suite`.
///
/// Reads a tool catalog from a config/snapshot file (so generation is
/// deterministic and CI-friendly), synthesizes a valid-arg, edge, and
/// schema-violation case per tool, and emits one YAML document. Live
/// `tools/list` introspection is the follow-up; `generate stubs --url` already
/// shows the connector path and this command reuses the same reader once that
/// lands.
#[derive(Debug, Args)]
pub struct GenerateSuiteCliArgs {
    /// Read the declared tool list from this file. Accepts a `tools/list`
    /// JSON snapshot (`{"tools": [...]}`), a bare tools array, or a mock
    /// manifest (`mock_server.tools[]`) in YAML.
    #[arg(long = "from-config", value_name = "FILE")]
    pub from_config: PathBuf,

    /// Server key the generated tests reference, also the `servers:` entry.
    #[arg(long, value_name = "NAME", default_value = "server")]
    pub server_name: String,

    /// Comma-separated model identifiers for the `model_compatibility:`
    /// matrix. Omit to use the built-in multi-model default list.
    #[arg(long, value_name = "ID,ID,...", value_delimiter = ',')]
    pub models: Option<Vec<String>>,

    /// Skip the boundary edge case per tool.
    #[arg(long)]
    pub no_edge: bool,

    /// Skip the schema-violation case per tool.
    #[arg(long)]
    pub no_violation: bool,

    /// Write the suite to this path instead of stdout.
    #[arg(long, value_name = "PATH")]
    pub output: Option<PathBuf>,

    /// Merge into an existing suite at this path: keep every hand-authored
    /// test, append only tests whose `name` is new. Mutually exclusive with
    /// `--output`.
    #[arg(long, value_name = "PATH", conflicts_with = "output")]
    pub update: Option<PathBuf>,

    /// Skip the output-schema conformance test emitted for every tool that
    /// declares an `outputSchema`.
    #[arg(long = "no-output-schema-tests")]
    pub no_output_schema_tests: bool,

    /// Additionally emit deterministic probe cases per tool: boundary values
    /// at declared schema bounds, just-outside violation probes, empty-value
    /// probes, and an unexpected-extra-property probe. Capped at 12 per tool.
    #[arg(long)]
    pub probe: bool,
}

/// Arguments for `mcptest generate stubs`.
#[derive(Debug, Args)]
pub struct GenerateStubsCliArgs {
    /// MCP server endpoint to introspect. Exactly one of `--url` or
    /// `--server-command` is required.
    #[arg(long, value_name = "URL", conflicts_with = "server_command")]
    pub url: Option<String>,

    /// Stdio MCP server to spawn and introspect, as one shell-quoted string
    /// (`--server-command "./dev-server --debug"`), mirroring the global
    /// `--server-command` flag. Exactly one of `--url` or `--server-command`
    /// is required.
    #[arg(long = "server-command", value_name = "CMD")]
    pub server_command: Option<String>,

    /// Server key the generated tests reference, also the `servers:` entry
    /// written into each stub so it validates and runs as written.
    #[arg(long, value_name = "NAME", default_value = "server")]
    pub server_name: String,

    /// Env var that holds a bearer token forwarded to every request.
    /// URL targets only.
    #[arg(
        long = "bearer-token-env",
        value_name = "NAME",
        conflicts_with = "server_command"
    )]
    pub bearer_token_env: Option<String>,

    /// Directory the generated YAML files are written under.
    #[arg(long, value_name = "DIR", default_value = "tests/tools")]
    pub output: PathBuf,

    /// Replace existing stub files instead of skipping them.
    #[arg(long)]
    pub overwrite: bool,

    /// Print every stub concatenated to stdout instead of writing to disk.
    #[arg(long)]
    pub stdout: bool,

    /// Exit 6 if any generated stub differs from the checked-in file.
    /// Useful for CI drift detection.
    #[arg(long)]
    pub check: bool,

    /// Skip the output-schema conformance test emitted for every tool that
    /// declares an `outputSchema`.
    #[arg(long = "no-output-schema-tests")]
    pub no_output_schema_tests: bool,

    /// Additionally emit deterministic probe cases per tool: boundary values
    /// at declared schema bounds, just-outside violation probes, empty-value
    /// probes, and an unexpected-extra-property probe. Capped at 12 per tool.
    #[arg(long)]
    pub probe: bool,
}
