//! Universal JSON I/O helpers for rok-cli.
#![allow(dead_code)]

use serde_json::{json, Value};

/// Print a successful result as either human text or JSON.
pub fn ok(json_mode: bool, fields: Value) {
    if json_mode {
        let mut obj = json!({"status": "ok"});
        if let (Some(o), Some(f)) = (obj.as_object_mut(), fields.as_object()) {
            for (k, v) in f {
                o.insert(k.clone(), v.clone());
            }
        }
        println!("{}", serde_json::to_string_pretty(&obj).unwrap());
    }
    // human text is emitted by the caller via println!/eprintln!
}

/// Emit a JSON error object when json_mode is true.
pub fn err(json_mode: bool, code: &str, message: &str) {
    if json_mode {
        let obj = json!({"status":"error","code":code,"message":message});
        println!("{}", serde_json::to_string_pretty(&obj).unwrap());
    } else {
        eprintln!("error [{}]: {}", code, message);
    }
}

/// Load and parse a JSON file; returns the parsed Value.
pub fn load_file(path: &str) -> anyhow::Result<Value> {
    let content =
        std::fs::read_to_string(path).map_err(|e| anyhow::anyhow!("Cannot read {path}: {e}"))?;
    serde_json::from_str(&content).map_err(|e| anyhow::anyhow!("Invalid JSON in {path}: {e}"))
}

/// Extract a string field from a JSON Value.
pub fn get_str<'a>(v: &'a Value, key: &str) -> Option<&'a str> {
    v.get(key)?.as_str()
}

/// Extract a bool field from a JSON Value (default false).
pub fn get_bool(v: &Value, key: &str) -> bool {
    v.get(key).and_then(|x| x.as_bool()).unwrap_or(false)
}

/// Extract a string array field from a JSON Value.
pub fn get_str_array(v: &Value, key: &str) -> Vec<String> {
    v.get(key)
        .and_then(|x| x.as_array())
        .map(|arr| {
            arr.iter()
                .filter_map(|s| s.as_str().map(|s| s.to_string()))
                .collect()
        })
        .unwrap_or_default()
}
