//! Utility functions for Cloud Hypervisor operations.

use std::fs;
use std::path::Path;

use crate::cloudhv::error::CloudHvError;

/// Checks if cloud-hypervisor-static and ch-remote-static are available.
pub fn ensure_deps() -> Result<(), CloudHvError> {
    if crate::process::which("cloud-hypervisor-static").is_none() {
        return Err(CloudHvError::DependencyMissing(
            "cloud-hypervisor-static not found on PATH. Install Cloud Hypervisor static binary."
                .into(),
        ));
    }
    if crate::process::which("ch-remote-static").is_none() {
        return Err(CloudHvError::DependencyMissing(
            "ch-remote-static not found on PATH. Install Cloud Hypervisor tools (static).".into(),
        ));
    }
    Ok(())
}

/// Checks if a process with the given PID exists.
pub fn proc_exists(pid: i64) -> bool {
    #[cfg(target_os = "linux")]
    {
        Path::new(&format!("/proc/{}", pid)).exists()
    }
    #[cfg(not(target_os = "linux"))]
    {
        let res = crate::process::run(&format!("kill -0 {}", pid))
            .die(false)
            .silent(true)
            .execute();
        res.map(|r| r.success).unwrap_or(false)
    }
}

/// Writes a JSON value to a file, creating parent directories if needed.
pub fn write_json(path: &Path, value: &serde_json::Value) -> Result<(), CloudHvError> {
    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent).map_err(|e| CloudHvError::IoError(e.to_string()))?;
    }
    let s =
        serde_json::to_string_pretty(value).map_err(|e| CloudHvError::JsonError(e.to_string()))?;
    fs::write(path, s).map_err(|e| CloudHvError::IoError(e.to_string()))
}

/// Reads a JSON value from a file.
pub fn read_json(path: &Path) -> Result<serde_json::Value, CloudHvError> {
    let content = fs::read_to_string(path).map_err(|e| CloudHvError::IoError(e.to_string()))?;
    serde_json::from_str(&content).map_err(|e| CloudHvError::JsonError(e.to_string()))
}

/// Joins command parts into a shell-safe command string.
pub fn shell_join(parts: &[String]) -> String {
    let mut s = String::new();
    for (i, p) in parts.iter().enumerate() {
        if i > 0 {
            s.push(' ');
        }
        s.push_str(&shell_escape(p));
    }
    s
}

/// Escapes a string for safe shell usage.
pub fn shell_escape(s: &str) -> String {
    if s.is_empty() {
        return "''".into();
    }
    if s.chars()
        .all(|c| c.is_ascii_alphanumeric() || "-_./=:".contains(c))
    {
        return s.into();
    }
    // Single-quote wrap, escape existing quotes
    let mut out = String::from("'");
    for ch in s.chars() {
        if ch == '\'' {
            out.push_str("'\"'\"'");
        } else {
            out.push(ch);
        }
    }
    out.push('\'');
    out
}
