//! Handler for `mcptest import`.
//!
//! Reads an external benchmark/eval task corpus, normalizes it into mcptest
//! agent tests, and writes a suite YAML with a provenance header. The mapping
//! and rendering live in `mcptest_core::benchmark_import`; this handler does the
//! IO and assembles the provenance manifest.

use std::path::Path;

use anyhow::{Context, Result};
use mcptest_core::benchmark_import::{
    import_tasks, to_suite_yaml, BenchmarkKind, ImportManifest, IMPORT_TRANSFORM_VERSION,
};
use serde_json::Value;

use crate::cli::args::{ImportArgs, ImportKind};
use crate::cli::exit_codes::{EXIT_INVALID_INVOCATION, EXIT_OK};

/// Import an external corpus into a suite. Returns 0 on success, 2 when the
/// input cannot be read or carries no importable task.
pub(crate) fn import_command(args: ImportArgs) -> Result<i32> {
    let doc = match read_json(&args.file) {
        Ok(v) => v,
        Err(code) => return Ok(code),
    };
    let kind = match args.kind {
        ImportKind::Generic => BenchmarkKind::Generic,
        ImportKind::Promptfoo => BenchmarkKind::Promptfoo,
    };
    let (tasks, skipped) = import_tasks(&doc, kind);
    if tasks.is_empty() {
        eprintln!(
            "mcptest import: no importable tasks in {:?} ({} skipped); check --kind",
            args.file,
            skipped.len()
        );
        return Ok(EXIT_INVALID_INVOCATION);
    }

    let servers = if args.servers.is_empty() {
        vec!["server".to_string()]
    } else {
        args.servers.clone()
    };
    let manifest = ImportManifest {
        source: args.source.clone().unwrap_or_else(|| kind_label(args.kind)),
        source_url: args.source_url.clone(),
        license: args.license.clone(),
        transform_version: IMPORT_TRANSFORM_VERSION.to_string(),
        imported: tasks.len(),
        skipped,
    };

    let yaml = to_suite_yaml(&tasks, &manifest, &servers, &args.model)
        .context("rendering the imported suite")?;

    match &args.output {
        Some(path) => {
            super::overwrite::ensure_overwritable(path, args.force)?;
            std::fs::write(path, &yaml).with_context(|| format!("writing suite to {path:?}"))?;
            eprintln!(
                "mcptest import: wrote {} agent test(s) to {}",
                manifest.imported,
                path.display()
            );
        }
        None => print!("{yaml}"),
    }
    Ok(EXIT_OK)
}

/// A human label for an import kind, used as the default provenance source.
fn kind_label(kind: ImportKind) -> String {
    match kind {
        ImportKind::Generic => "generic".to_string(),
        ImportKind::Promptfoo => "promptfoo".to_string(),
    }
}

/// Read and parse a JSON file, mapping read and parse failures to exit code 2.
fn read_json(path: &Path) -> std::result::Result<Value, i32> {
    let body = match std::fs::read_to_string(path) {
        Ok(b) => b,
        Err(e) => {
            eprintln!("mcptest import: cannot read {path:?}: {e}");
            return Err(EXIT_INVALID_INVOCATION);
        }
    };
    serde_json::from_str(&body).map_err(|e| {
        eprintln!("mcptest import: {path:?} is not valid JSON: {e}");
        EXIT_INVALID_INVOCATION
    })
}
