use std::ffi::OsString;
use std::fs;
use std::path::{Path, PathBuf};

use anyhow::{Context, Result};

pub fn atomic_write(dest: &Path, content: &str) -> Result<()> {
    if let Some(parent) = dest.parent() {
        if !parent.as_os_str().is_empty() {
            fs::create_dir_all(parent).with_context(|| format!("creating {}", parent.display()))?;
        }
    }
    let mut tmp_name = OsString::from(dest.as_os_str());
    tmp_name.push(".aimgr-tmp");
    let tmp = PathBuf::from(tmp_name);
    fs::write(&tmp, content).with_context(|| format!("writing {}", tmp.display()))?;
    fs::rename(&tmp, dest)
        .with_context(|| format!("renaming {} to {}", tmp.display(), dest.display()))?;
    Ok(())
}

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

    #[test]
    fn atomic_write_creates_parents() {
        let dir = tempdir().unwrap();
        let dest = dir.path().join("a/b/c.txt");
        atomic_write(&dest, "hello").unwrap();
        assert_eq!(fs::read_to_string(&dest).unwrap(), "hello");
    }

    #[test]
    fn atomic_write_overwrites() {
        let dir = tempdir().unwrap();
        let dest = dir.path().join("x.txt");
        atomic_write(&dest, "one").unwrap();
        atomic_write(&dest, "two").unwrap();
        assert_eq!(fs::read_to_string(&dest).unwrap(), "two");
    }
}
