//! CLI verbs: search, category-search, list, categories, health.
//! All dispatch to the Node backend via `POST /` with `{action, ...params}`.

use crate::jlcpcb;
use crate::{ok, warn, CategoriesArgs, CategorySearchArgs, HealthArgs, ListArgs, SearchArgs};
use serde_json::{json, Value};

fn emit_json(v: &Value) {
    // Put OK: on stderr so stdout stays pure JSON for `| jq`.
    let count = v.get("components").and_then(|c| c.as_array()).map(|a| a.len()).unwrap_or(0);
    let total = v.get("count").or_else(|| v.get("total")).and_then(|n| n.as_u64());
    let query = v.get("query").or_else(|| v.get("category")).and_then(|q| q.as_str()).unwrap_or("");
    let summary = if let Some(t) = total {
        format!("{} components (showing {}, total {})", count, count, t)
    } else {
        format!("{} components", count)
    };
    if crate::stderr_is_tty() {
        eprintln!("{}{}OK:{} {} ({})", crate::GREEN, crate::BOLD, crate::RESET, summary, query);
    } else {
        eprintln!("OK: {} ({})", summary, query);
    }
    println!("{}", serde_json::to_string_pretty(v).unwrap_or_default());
}

fn resolve_or_fail(explicit: Option<String>) -> Result<String, String> {
    match jlcpcb::resolve_backend(explicit) {
        Some(api) => Ok(api),
        None => {
            warn("No backend at http://127.0.0.1:8774 and no --api / JLCPCB_API set.");
            Err(
                "JLCPCB backend unreachable. Point --api / JLCPCB_API at the service URL \
                 (e.g. https://jlcpcb-<slug>.adom.cloud), or start a local backend with \
                 `adom-jlcpcb install` + the service/backend/ Node server."
                    .into(),
            )
        }
    }
}

pub fn run_search(args: SearchArgs) -> Result<(), String> {
    let api = resolve_or_fail(args.api)?;
    let v = jlcpcb::post_action(
        &api,
        json!({
            "action": "search",
            "keyword": args.keyword,
            "limit": args.limit,
            "inStockOnly": args.in_stock_only,
        }),
    )?;
    if let Some(e) = v.get("error").and_then(|v| v.as_str()) { return Err(e.into()); }
    emit_json(&v);
    Ok(())
}

pub fn run_category_search(args: CategorySearchArgs) -> Result<(), String> {
    let api = resolve_or_fail(args.api)?;
    let v = jlcpcb::post_action(
        &api,
        json!({
            "action": "category_search",
            "keyword": args.keyword,
            "category": args.category,
            "limit": args.limit,
            "inStockOnly": args.in_stock_only,
        }),
    )?;
    if let Some(e) = v.get("error").and_then(|v| v.as_str()) { return Err(e.into()); }
    emit_json(&v);
    Ok(())
}

pub fn run_list(args: ListArgs) -> Result<(), String> {
    let api = resolve_or_fail(args.api)?;
    let mut body = json!({
        "action": "list",
        "category": args.category,
        "limit": args.limit,
        "inStockOnly": args.in_stock_only,
    });
    if let Some(sc) = args.subcategory {
        body["subcategory"] = Value::String(sc);
    }
    let v = jlcpcb::post_action(&api, body)?;
    if let Some(e) = v.get("error").and_then(|v| v.as_str()) { return Err(e.into()); }
    emit_json(&v);
    Ok(())
}

pub fn run_categories(args: CategoriesArgs) -> Result<(), String> {
    let api = resolve_or_fail(args.api)?;
    let v = jlcpcb::post_action(&api, json!({"action": "categories"}))?;
    if let Some(e) = v.get("error").and_then(|v| v.as_str()) { return Err(e.into()); }
    println!("{}", serde_json::to_string_pretty(&v).unwrap_or_default());
    Ok(())
}

pub fn run_health(args: HealthArgs) -> Result<(), String> {
    let api = resolve_or_fail(args.api)?;
    let v = jlcpcb::get_health(&api)?;
    ok(format!("backend {} reachable", api));
    println!("{}", serde_json::to_string_pretty(&v).unwrap_or_default());
    Ok(())
}