//! `mcptest conformance check-ids` handler (WOR-945).
//!
//! Lists every `check:` id from the resolved corpus's
//! `traceability.json`, with an implementation marker. The current
//! implementation registry is intentionally empty in v1 (we ship the
//! library + CLI surface today; the registry of which checks have a
//! Rust implementation lands as a follow-up). Every id renders as
//! "not implemented yet" so the worklist is honest.

use anyhow::{Context, Result};
use serde::Serialize;

use crate::cli::args::{ConformanceCheckIdsArgs, ConformanceCheckIdsFormat};
use mcptest_core::conformance::corpus;

/// JSON wire shape for `check-ids --reporter json`.
#[derive(Debug, Serialize)]
struct CheckIdsReport {
    target_version: String,
    corpus_source: corpus::CorpusSource,
    total: usize,
    implemented: usize,
    missing: usize,
    checks: Vec<CheckEntry>,
}

#[derive(Debug, Serialize)]
struct CheckEntry {
    id: String,
    implemented: bool,
}

pub fn check_ids_command(args: ConformanceCheckIdsArgs) -> Result<i32> {
    let cache_dir = super::conformance_paths::user_cache_dir();
    let target_version = match args.target_version {
        Some(v) => v,
        None => corpus::latest_available(args.corpus_dir.as_deref(), cache_dir.as_deref())
            .context("could not resolve a target version")?,
    };
    let loaded = corpus::load(
        &target_version,
        args.corpus_dir.as_deref(),
        cache_dir.as_deref(),
    )
    .with_context(|| format!("loading corpus for {target_version}"))?;

    let ids = corpus::check_ids(&loaded);
    let registry = implemented_check_ids();
    let entries: Vec<CheckEntry> = ids
        .iter()
        .filter_map(|id| {
            let implemented = registry.contains(&id.as_str());
            if args.missing_only && implemented {
                return None;
            }
            Some(CheckEntry {
                id: id.clone(),
                implemented,
            })
        })
        .collect();

    let implemented_count = entries.iter().filter(|e| e.implemented).count();
    let report = CheckIdsReport {
        target_version: loaded.spec_version.clone(),
        corpus_source: loaded.source,
        total: ids.len(),
        implemented: implemented_count,
        missing: ids.len().saturating_sub(implemented_count),
        checks: entries,
    };

    match args.format {
        ConformanceCheckIdsFormat::Json => {
            println!(
                "{}",
                serde_json::to_string_pretty(&report).context("serializing JSON")?
            );
        }
        ConformanceCheckIdsFormat::Pretty => render_pretty(&report),
    }
    Ok(0)
}

fn render_pretty(report: &CheckIdsReport) {
    println!("conformance corpus: {}", report.target_version);
    println!(
        "  source     : {:?}",
        serde_json::to_value(report.corpus_source).unwrap_or_default()
    );
    println!("  total      : {}", report.total);
    println!("  implemented: {}", report.implemented);
    println!("  missing    : {}", report.missing);
    println!();
    for entry in &report.checks {
        let marker = if entry.implemented { "[x]" } else { "[ ]" };
        println!("  {marker} {}", entry.id);
    }
}

/// Set of check ids the runner has a wire-observable implementation for.
///
/// Sourced from the per-SEP probe registry
/// ([`mcptest_core::conformance::PER_SEP_PROBE_CHECK_IDS`]) so the listing and
/// the live probe never drift: a check shows `[x]` here exactly when
/// `conformance run --server` actually verifies it. The set grows as more
/// probe families land (WOR-1404).
fn implemented_check_ids() -> std::collections::BTreeSet<&'static str> {
    mcptest_core::conformance::PER_SEP_PROBE_CHECK_IDS
        .iter()
        .copied()
        .collect()
}
