//! Small per-key JSON persistence for renderer state that outgrows
//! localStorage (the synced-hash manifest scales with workspace file count;
//! localStorage's ~5 MB quota fails silently around 40k files).

use sha2::{Digest, Sha256};
use std::path::{Path, PathBuf};

/// Keys are arbitrary renderer strings (project::user::workspace-path) —
/// hash them into filenames so path separators/lengths can't break the store.
pub fn state_file_path(base_dir: &Path, key: &str) -> PathBuf {
    let name = hex::encode(Sha256::digest(key.as_bytes()));
    base_dir.join("client-state").join(format!("{name}.json"))
}

pub fn write_atomic(path: &Path, value: &str) -> std::io::Result<()> {
    if let Some(parent) = path.parent() {
        std::fs::create_dir_all(parent)?;
    }
    let tmp = path.with_extension("json.tmp");
    std::fs::write(&tmp, value.as_bytes())?;
    std::fs::rename(&tmp, path)?;
    Ok(())
}

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

    #[test]
    fn key_hashing_is_stable_and_filesystem_safe() {
        let base = Path::new("/data");
        let a = state_file_path(base, "proj::@user::/some/deep/path");
        let b = state_file_path(base, "proj::@user::/some/deep/path");
        assert_eq!(a, b);
        let name = a.file_name().unwrap().to_string_lossy().to_string();
        assert!(name.ends_with(".json"));
        assert_eq!(name.len(), 64 + ".json".len());
    }

    #[test]
    fn write_atomic_roundtrip() {
        let dir = tempfile::tempdir().unwrap();
        let path = state_file_path(dir.path(), "k1");
        write_atomic(&path, "{\"a\":1}").unwrap();
        assert_eq!(std::fs::read_to_string(&path).unwrap(), "{\"a\":1}");
        assert!(!path.with_extension("json.tmp").exists());
        // Overwrite works.
        write_atomic(&path, "{}").unwrap();
        assert_eq!(std::fs::read_to_string(&path).unwrap(), "{}");
    }
}
