//! Arguments for `mcptest evidence`.

use std::path::PathBuf;
use std::time::Duration;

use clap::{Args, Subcommand};

use crate::cli::parsers::parse_duration;

/// Aggregate a run's metadata into a portable, signable evidence artifact, or
/// verify one with `evidence verify`.
///
/// The emit form reads a `mcptest run --format json` report and writes the
/// `evidence.json` artifact (server identity, spec version, corpus hash, source
/// provenance, grades, reproducibility). `--sign` reuses the release Sigstore
/// cosign path to attach a detached signature. `subcommand_negates_reqs` lets
/// `evidence verify <file>` run without the positional report.
#[derive(Debug, Args)]
#[command(subcommand_negates_reqs = true)]
pub struct EvidenceArgs {
    /// Path to a serialized `mcptest run --format json` report to build the
    /// evidence from. Required unless a subcommand is given.
    #[arg(required = true)]
    pub report: Option<PathBuf>,

    /// Fold a `mcptest security --format json` report's severity counts into
    /// the evidence grades.
    #[arg(long, value_name = "FILE")]
    pub security: Option<PathBuf>,

    /// Mark the run byte-reproducible (the `sbom --verify` / `SOURCE_DATE_EPOCH`
    /// parity signal). Off by default.
    #[arg(long)]
    pub reproducible: bool,

    /// Write the artifact to this path instead of stdout. Required with
    /// `--sign` (cosign signs a file). `--out` is accepted as a hidden
    /// alias for the pre-1.1 spelling.
    #[arg(long, alias = "out", value_name = "PATH")]
    pub output: Option<PathBuf>,

    /// Overwrite the output if it already exists (off by default so a re-run
    /// cannot silently clobber a tracked or hand-edited file).
    #[arg(long)]
    pub force: bool,

    /// Sign the written artifact with Sigstore cosign keyless (GitHub OIDC),
    /// the same path the release workflow uses. Produces `<output>.sig` and
    /// `<output>.cert`. Requires `cosign` on PATH and `--output`.
    #[arg(long)]
    pub sign: bool,

    /// Optional subcommand. When present, runs that instead of emitting.
    #[command(subcommand)]
    pub command: Option<EvidenceCommand>,
}

/// Subcommands under `mcptest evidence`.
#[derive(Debug, Subcommand)]
pub enum EvidenceCommand {
    /// Verify an evidence artifact, rejecting stale, forked, or unsigned
    /// evidence.
    Verify(EvidenceVerifyArgs),
    /// Assemble a governance pack: the evidence artifact plus the available
    /// coverage, fault-coverage, and cost sections, in one signable envelope.
    Pack(EvidencePackArgs),
    /// Diff two governance packs and report what regressed since the last one.
    Diff(EvidenceDiffArgs),
}

/// Arguments for `mcptest evidence pack`.
#[derive(Debug, Args)]
pub struct EvidencePackArgs {
    /// Path to a serialized `mcptest run --format json` report to build the
    /// pack from.
    pub report: PathBuf,

    /// Fold a `mcptest security --format json` report's severity counts into
    /// the grades.
    #[arg(long, value_name = "FILE")]
    pub security: Option<PathBuf>,

    /// Fold a `mcptest coverage --format json` report in as the coverage
    /// section.
    #[arg(long, value_name = "FILE")]
    pub coverage: Option<PathBuf>,

    /// Fold a `mcptest fault-coverage --format json` report in as the runtime
    /// fault-coverage section.
    #[arg(long, value_name = "FILE")]
    pub fault_coverage: Option<PathBuf>,

    /// Fold a `mcptest posture --format json` report in as the runtime
    /// posture section.
    #[arg(long, value_name = "FILE")]
    pub posture: Option<PathBuf>,

    /// Attach an arbitrary JSON sidecar under a named section, as
    /// `--section name=path` (for example `--section model_compat=drift.json`).
    /// Repeatable.
    #[arg(long = "section", value_name = "NAME=PATH")]
    pub sections: Vec<String>,

    /// Mark the run byte-reproducible (the `sbom --verify` parity signal).
    #[arg(long)]
    pub reproducible: bool,

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

    /// Overwrite the output if it already exists (off by default so a re-run
    /// cannot silently clobber a tracked or hand-edited file).
    #[arg(long)]
    pub force: bool,

    /// Sign the written pack with Sigstore cosign keyless, the same path the
    /// release workflow uses. Requires `cosign` on PATH and `--output`.
    #[arg(long)]
    pub sign: bool,

    /// Output format when writing to stdout.
    #[arg(long, value_enum, value_name = "FORMAT", default_value_t = PackFormat::Json)]
    pub format: PackFormat,
}

/// Output formats for `mcptest evidence pack`.
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, clap::ValueEnum)]
#[value(rename_all = "kebab-case")]
pub enum PackFormat {
    /// The machine-readable pack envelope (default).
    #[default]
    Json,
    /// A human-readable Markdown summary.
    Markdown,
}

/// Arguments for `mcptest evidence diff`.
#[derive(Debug, Args)]
pub struct EvidenceDiffArgs {
    /// The earlier (approved) pack.
    pub old: PathBuf,
    /// The newer pack to compare against it.
    pub new: PathBuf,

    /// Exit non-zero when any tracked signal regressed. On by default; pass
    /// `--fail-on-regression false` to report without gating.
    #[arg(
        long,
        value_name = "BOOL",
        default_value_t = true,
        action = clap::ArgAction::Set
    )]
    pub fail_on_regression: bool,

    /// Output format.
    #[arg(long, value_enum, value_name = "FORMAT", default_value_t = DiffOutFormat::Pretty)]
    pub format: DiffOutFormat,
}

/// Output formats for `mcptest evidence diff`.
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, clap::ValueEnum)]
#[value(rename_all = "kebab-case")]
pub enum DiffOutFormat {
    /// Plain-text table (default).
    #[default]
    Pretty,
    /// The structured diff as JSON.
    Json,
    /// GitHub-flavored Markdown.
    Markdown,
}

/// Arguments for `mcptest evidence verify`.
#[derive(Debug, Args)]
pub struct EvidenceVerifyArgs {
    /// Path to the `evidence.json` artifact to verify.
    pub evidence: PathBuf,

    /// Reject evidence whose `generated_at` is older than this (for example
    /// `720h` for 30 days). Omit to skip the staleness check.
    #[arg(long, value_name = "DURATION", value_parser = parse_duration)]
    pub max_age: Option<Duration>,

    /// Path to a detached signature for the artifact. Defaults to `<evidence>.sig`
    /// when that file exists.
    #[arg(long, value_name = "FILE")]
    pub signature: Option<PathBuf>,

    /// Reject the artifact when it is unsigned.
    #[arg(long)]
    pub require_signed: bool,

    /// Path to the signing certificate. Defaults to `<evidence>.cert` when that
    /// file exists. Needed (with the identity flags) to cryptographically verify
    /// the signature via cosign rather than only checking it is present.
    #[arg(long, value_name = "FILE")]
    pub certificate: Option<PathBuf>,

    /// Expected signer identity (a cosign keyless `--certificate-identity`, for
    /// example the GitHub Actions workflow URL). With `--certificate-oidc-issuer`
    /// this triggers a real `cosign verify-blob`; a tampered signature is then
    /// rejected instead of passing on presence alone.
    #[arg(long = "certificate-identity", value_name = "IDENTITY")]
    pub certificate_identity: Option<String>,

    /// Expected OIDC issuer (cosign keyless `--certificate-oidc-issuer`, for
    /// example `https://token.actions.githubusercontent.com`).
    #[arg(long = "certificate-oidc-issuer", value_name = "URL")]
    pub certificate_oidc_issuer: Option<String>,
}
