use anyhow::{Context, Result};
use serde_json::Value;
use std::path::PathBuf;

pub(in crate::install) fn read_json_file(path: &PathBuf) -> Result<Value> {
    if path.exists() {
        let content = std::fs::read_to_string(path)
            .with_context(|| format!("读取 {} 失败", path.display()))?;
        serde_json::from_str(&content).with_context(|| format!("解析 {} 失败", path.display()))
    } else {
        Ok(serde_json::json!({}))
    }
}

pub(in crate::install) fn write_json_file(path: &PathBuf, value: &Value) -> Result<()> {
    let content = serde_json::to_string_pretty(value)?;
    crate::atomic_file::write_atomic(path, content)
        .with_context(|| format!("写入 {} 失败", path.display()))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn write_json_failure_preserves_existing_file() -> Result<()> {
        let _guard = crate::atomic_file::failpoint_test_lock();
        let path = std::env::temp_dir().join(format!(
            "remem-json-atomic-{}-{}.json",
            std::process::id(),
            chrono::Utc::now().timestamp_nanos_opt().unwrap_or_default()
        ));
        std::fs::write(&path, r#"{"existing":true}"#)?;
        crate::atomic_file::fail_next_rename_for_path_for_test(&path);

        let err = write_json_file(&path, &serde_json::json!({"existing": false}))
            .expect_err("injected failure must abort JSON write");
        assert!(format!("{err:?}").contains("injected atomic write failure"));
        assert_eq!(std::fs::read_to_string(&path)?, r#"{"existing":true}"#);
        crate::atomic_file::clear_failpoints_for_test();
        let _ = std::fs::remove_file(path);
        Ok(())
    }
}
