use crate::utils;
use std::{
    borrow::Cow,
    fs, io,
    path::{Path, PathBuf},
};

pub(crate) fn temp_dir_or_else<'p>(default: impl Fn() -> &'p Path) -> Cow<'p, Path> {
    if cfg!(target_os = "wasi") {
        default().into()
    } else {
        std::env::temp_dir().into()
    }
}

pub(crate) struct NamedTempFile {
    file_path: PathBuf,
    file: fs::File,
}

impl NamedTempFile {
    #[inline]
    pub(crate) fn new<'p>(fallback_dir: impl Fn() -> &'p Path) -> io::Result<Self> {
        let temp_dir = temp_dir_or_else(fallback_dir);
        fs::create_dir_all(&temp_dir)?;
        let random = rand::random::<usize>();
        let file_path = temp_dir.join(format!("{random}.tmp"));
        let file = fs::File::create(&file_path)?;
        Ok(Self { file, file_path })
    }

    #[inline]
    pub(crate) fn as_file_mut(&mut self) -> &mut fs::File {
        &mut self.file
    }

    #[inline]
    pub(crate) fn persist(self, new_path: impl AsRef<Path>) -> io::Result<()> {
        let Self { file, file_path } = self;
        file.sync_all()?;
        drop(file);

        let new_path_ref = new_path.as_ref();
        if let Some(parent) = new_path_ref.parent() {
            fs::create_dir_all(parent)?;
        }
        utils::fs::mv(file_path, new_path_ref)
    }
}
