#![allow(clippy::unwrap_used)]
use std::ops::Deref;
use std::{fs::File, os::unix::fs::FileExt, path::Path, sync::Arc};

use parking_lot::Mutex;
use serde::{Serialize, de::DeserializeOwned};
use serde_json::{from_reader, to_vec_pretty, to_writer_pretty};

#[derive(Debug, Clone)]
pub struct Persist<T: DeserializeOwned + Serialize + Default> {
    pub data: Arc<Mutex<T>>,
    file: Arc<File>,
    path: String,
}

impl<T> Persist<T>
where
    T: DeserializeOwned + Serialize + Default,
{
    pub fn open(path: impl AsRef<Path>) -> Self {
        let str_path = path.as_ref().to_str().unwrap_or_default().to_string();
        let mut file = File::options()
            .write(true)
            .read(true)
            .create(true)
            .truncate(false)
            .open(path.as_ref())
            .expect(&str_path);

        if file.metadata().expect(&str_path).len() == 0 {
            let data = T::default();
            to_writer_pretty(&mut file, &data).expect(&str_path);
            let data = Arc::new(Mutex::new(data));
            Self {
                data,
                file: Arc::new(file),
                path: str_path,
            }
        } else {
            let data = from_reader(&mut file).expect(&str_path);
            let data = Arc::new(Mutex::new(data));
            Self {
                data,
                file: Arc::new(file),
                path: str_path,
            }
        }
    }

    #[instrument(skip(self))]
    pub fn save(&self) {
        let data = self.data.lock();
        let bytes = to_vec_pretty(data.deref()).expect(&self.path);
        self.file.set_len(0).expect(&self.path);
        self.file.write_all_at(&bytes, 0).expect(&self.path);
        self.file.sync_all().expect(&self.path);
    }

    pub fn cloned(&self) -> T
    where
        T: Clone,
    {
        self.data.lock().clone()
    }
}

impl<T> Drop for Persist<T>
where
    T: DeserializeOwned + Serialize + Default,
{
    fn drop(&mut self) {
        self.save();
    }
}
