//! One-shot CLI verbs: `adom-mouser search|part|health`.
//!
//! Resolution order for the data source:
//!   1. If `--api` / `MOUSER_API` is set → talk to that backend via POST /
//!   2. If http://127.0.0.1:8775 is reachable → use it as the backend
//!   3. Else fall back to a direct Mouser API call (requires MOUSER_API_KEY)

use crate::mouser::{self, SearchResult};
use crate::{ok, warn, HealthArgs, PartArgs, SearchArgs};
use serde_json::{json, Value};
use std::time::Duration;

fn resolve_backend(explicit: Option<String>) -> Option<String> {
    if let Some(api) = explicit.filter(|s| !s.is_empty()) {
        return Some(api);
    }
    let local = "http://127.0.0.1:8775".to_string();
    let agent = ureq::AgentBuilder::new()
        .timeout(Duration::from_secs(2))
        .build();
    match agent.get(&format!("{}/health", local)).call() {
        Ok(_) => Some(local),
        Err(_) => None,
    }
}

fn backend_post(api: &str, body: Value) -> Result<Value, String> {
    let url = if api.ends_with('/') { api.to_string() } else { format!("{}/", api) };
    let agent = ureq::AgentBuilder::new()
        .timeout(Duration::from_secs(30))
        .build();
    let res = match agent.post(&url).set("Content-Type", "application/json").send_json(body) {
        Ok(r) => r,
        Err(ureq::Error::Status(_code, r)) => r,
        Err(e) => return Err(format!("backend POST failed: {}", e)),
    };
    res.into_json().map_err(|e| format!("backend JSON parse failed: {}", e))
}

fn emit(result: SearchResult) {
    // Always print clean JSON on stdout (so the AI can pipe `| jq`), and
    // emit an OK: line on a separate line so the AI knows success. The
    // OK: line goes to stdout before the JSON so it doesn't corrupt the
    // JSON if somebody does `| jq`. Actually — emit OK on stderr so the
    // stdout stream is pure JSON.
    if crate::stderr_is_tty() {
        eprintln!(
            "{}{}OK:{} {} components (query: {})",
            crate::GREEN,
            crate::BOLD,
            crate::RESET,
            result.components.len(),
            result.query
        );
    } else {
        eprintln!("OK: {} components (query: {})", result.components.len(), result.query);
    }
    let v = serde_json::to_value(&result).unwrap_or_else(|_| json!({}));
    println!("{}", serde_json::to_string_pretty(&v).unwrap_or_default());
}

pub fn run_search(args: SearchArgs) -> Result<(), String> {
    let backend = resolve_backend(args.api);
    let result = if let Some(api) = backend.as_ref() {
        let v = backend_post(
            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());
        }
        serde_json::from_value(v).map_err(|e| format!("response parse: {}", e))?
    } else {
        warn("No backend at http://127.0.0.1:8775 and no --api / MOUSER_API set; calling Mouser API directly.");
        let key = std::env::var("MOUSER_API_KEY").unwrap_or_default();
        mouser::search(&key, &args.keyword, args.limit, args.in_stock_only)?
    };
    emit(result);
    Ok(())
}

pub fn run_part(args: PartArgs) -> Result<(), String> {
    let backend = resolve_backend(args.api);
    let result = if let Some(api) = backend.as_ref() {
        let v = backend_post(api, json!({"action": "part", "partNumber": args.part_number}))?;
        if let Some(e) = v.get("error").and_then(|v| v.as_str()) {
            return Err(e.into());
        }
        serde_json::from_value(v).map_err(|e| format!("response parse: {}", e))?
    } else {
        warn("No backend at http://127.0.0.1:8775 and no --api / MOUSER_API set; calling Mouser API directly.");
        let key = std::env::var("MOUSER_API_KEY").unwrap_or_default();
        mouser::part(&key, &args.part_number)?
    };
    emit(result);
    Ok(())
}

pub fn run_health(args: HealthArgs) -> Result<(), String> {
    let backend = resolve_backend(args.api);
    if let Some(api) = backend.as_ref() {
        let url = if api.ends_with('/') {
            format!("{}health", api)
        } else {
            format!("{}/health", api)
        };
        let agent = ureq::AgentBuilder::new()
            .timeout(Duration::from_secs(10))
            .build();
        match agent.get(&url).call() {
            Ok(res) => {
                let v: Value = res.into_json().map_err(|e| format!("JSON: {}", e))?;
                ok(format!("backend {} reachable", api));
                println!("{}", serde_json::to_string_pretty(&v).unwrap_or_default());
                Ok(())
            }
            Err(e) => Err(format!("backend at {} unreachable: {}", api, e)),
        }
    } else {
        warn("No backend at http://127.0.0.1:8775 and no --api / MOUSER_API set; pinging Mouser directly.");
        let key = std::env::var("MOUSER_API_KEY").unwrap_or_default();
        let n = mouser::ping(&key)?;
        ok(format!("Mouser API reachable (test query returned {} results)", n));
        Ok(())
    }
}