use std::path::{Path, PathBuf};

use rootcause::prelude::*;

use crate::util::storage::SideloadingStorage;

pub struct FsStorage {
    path: PathBuf,
}

impl FsStorage {
    pub fn new(path: PathBuf) -> Self {
        FsStorage { path }
    }
}

impl Default for FsStorage {
    fn default() -> Self {
        Self::new(PathBuf::from("."))
    }
}

impl SideloadingStorage for FsStorage {
    fn store_data(&self, key: &str, data: &[u8]) -> Result<(), Report> {
        let path = self.path.join(key);
        let parent = path.parent().unwrap_or(Path::new("."));
        isideload_vfs::fs::create_dir_all(parent).context("Failed to create storage directory")?;
        isideload_vfs::fs::write(&path, data).context("Failed to write data to file")?;

        Ok(())
    }

    fn retrieve_data(&self, key: &str) -> Result<Option<Vec<u8>>, Report> {
        let path = self.path.join(key);
        match isideload_vfs::fs::read(&path) {
            Ok(data) => Ok(Some(data)),
            Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),
            Err(e) => Err(report!(e).context("Failed to read data from file").into()),
        }
    }

    fn store(&self, key: &str, value: &str) -> Result<(), Report> {
        self.store_data(key, value.as_bytes())
    }

    fn retrieve(&self, key: &str) -> Result<Option<String>, Report> {
        match self.retrieve_data(key) {
            Ok(Some(data)) => Ok(Some(String::from_utf8_lossy(&data).into_owned())),
            Ok(None) => Ok(None),
            Err(e) => Err(e),
        }
    }
}
