//! `provisioning-tool` — thin CLI over the provisioning-core tool registry.
//!
//! Subcommands:
//!   list    — print all registered tools (name, category, description)
//!   schema  — print the JSON Schema for a named tool
//!   invoke  — invoke a tool with JSON params, print result

use anyhow::{Context as _, Result};
use clap::{Parser, Subcommand};
use provisioning_core::{
    tool::Context,
    sources::{NclCache, OrchestratorClient},
    providers::hcloud::HcloudClient,
    Registry,
};
use provisioning_tool::{build_env, print_json};
use serde_json::{Value, json};
use std::sync::Arc;

// ── CLI ───────────────────────────────────────────────────────────────────────

#[derive(Parser)]
#[command(
    name = "provisioning-tool",
    about = "Invoke provisioning-core tools from the command line",
    version
)]
struct Cli {
    /// Orchestrator base URL
    #[arg(long, env = "ORCHESTRATOR_URL", default_value = "http://localhost:9011")]
    orchestrator_url: String,

    /// Provisioning workspaces root
    #[arg(long, env = "PROVISIONING_WORKSPACES_ROOT", default_value = "workspaces")]
    workspaces_root: String,

    /// Compact JSON output (default: pretty-printed)
    #[arg(long, short = 'c')]
    compact: bool,

    #[command(subcommand)]
    command: Cmd,
}

#[derive(Subcommand)]
enum Cmd {
    /// List all registered tools
    List {
        /// Filter by category (read, mutation, destructive, admin)
        #[arg(long)]
        category: Option<String>,
    },
    /// Show JSON Schema for a tool
    Schema {
        /// Tool name (e.g. workspace_list)
        name: String,
    },
    /// Invoke a tool with JSON params
    Invoke {
        /// Tool name (e.g. installer_settings_defaults)
        name: String,
        /// JSON params object (default: {})
        #[arg(long, default_value = "{}")]
        params: String,
        /// Additional key=value params merged into --params
        #[arg(long = "param", value_name = "KEY=VALUE")]
        extra: Vec<String>,
    },
}

// ── Entry point ───────────────────────────────────────────────────────────────

#[tokio::main]
async fn main() -> Result<()> {
    tracing_subscriber::fmt()
        .with_env_filter(
            tracing_subscriber::EnvFilter::from_default_env()
                .add_directive(tracing::Level::WARN.into()),
        )
        .with_writer(std::io::stderr)
        .init();

    let cli = Cli::parse();

    // Build registry
    let cache  = Arc::new(NclCache::new());
    let orch   = Arc::new(OrchestratorClient::new(&cli.orchestrator_url));
    let hcloud = Arc::new(HcloudClient::new());
    let reg = Registry::with_all_tools(cache, orch, hcloud, None)
        .context("failed to initialize tool registry")?;

    // Build environment + context
    let mut env = build_env();
    env.orchestrator_url = cli.orchestrator_url.clone();
    env.workspaces_root  = cli.workspaces_root.clone().into();
    let ctx = Context::new(Arc::new(env));

    match &cli.command {
        Cmd::List { category } => {
            let tools: Vec<Value> = reg
                .list()
                .into_iter()
                .filter(|m| {
                    category
                        .as_deref()
                        .map(|f| format!("{:?}", m.category).to_lowercase() == f)
                        .unwrap_or(true)
                })
                .map(|m| json!({
                    "name":        m.name,
                    "category":    format!("{:?}", m.category),
                    "description": m.description,
                }))
                .collect();
            print_json(&json!({ "tools": tools, "total": tools.len() }), cli.compact);
        }

        Cmd::Schema { name } => {
            let schema = reg.schema_of(name).ok_or_else(|| {
                anyhow::anyhow!("tool '{}' not found", name)
            })?;
            print_json(&schema, cli.compact);
        }

        Cmd::Invoke { name, params, extra } => {
            let mut p: Value = serde_json::from_str(params)
                .with_context(|| format!("invalid JSON for --params: {params}"))?;

            for kv in extra {
                let (k, v) = kv.split_once('=').ok_or_else(|| {
                    anyhow::anyhow!("--param must be KEY=VALUE, got: {kv}")
                })?;
                p[k] = json!(v);
            }

            let result = reg.invoke(name, p, &ctx).await.map_err(|e| {
                anyhow::anyhow!("{}", e)
            })?;
            print_json(&result, cli.compact);
        }
    }

    Ok(())
}
