use crate::FileId;

use super::{ReadFileError, WriteFileError};
use chrono::prelude::*;
use serde::{Deserialize, Serialize};
use std::{
    io::{Read, Write},
    path::PathBuf,
};

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct FileInfo {
    pub id: FileId,

    // Early versions of the application didn't support a name field, so it is possible that
    // metadata won't contain the name. We can just default to an empty string when loading the
    // metadata, as all future versions will require a filename when the file gets uploaded.
    #[serde(default)]
    pub name: String,
    pub size: usize,
    pub created: DateTime<Utc>,
    pub file_type: String,
    pub hash: String,
    pub extension: String,
}

impl FileInfo {
    pub fn load(path: PathBuf) -> Result<Self, ReadFileError> {
        let mut content: Vec<u8> = Vec::new();
        let mut file =
            std::fs::File::open(path.clone()).map_err(|_| ReadFileError::FileNotFound(path))?;
        file.read_to_end(&mut content)?;
        let js = serde_json::from_slice(&content)?;

        Ok(js)
    }

    pub fn save(&self, path: PathBuf) -> Result<(), WriteFileError> {
        let ser = serde_json::to_string(self).unwrap();
        let mut file = std::fs::File::create(path)?;
        let _ = file.write(ser.as_bytes())?;
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::store::FileId;
    use tempdir::TempDir;

    #[test]
    fn it_saves_and_loads_metadata() {
        let tmp = TempDir::new("var").unwrap();
        let created = Utc::now();

        let info = FileInfo {
            id: FileId("temp-id".to_owned()),
            name: "test-image".to_owned(),
            size: 23777,
            created,
            file_type: "image/png".to_owned(),
            hash: "abcdefg".to_owned(),
            extension: "png".to_owned(),
        };
        let mut path = tmp.path().to_owned();
        path.push(&PathBuf::from(info.id.clone()));
        info.save(path.clone()).unwrap();

        let info_ = FileInfo::load(path).unwrap();
        assert_eq!(info_.size, 23777);
        assert_eq!(info_.created, info.created);
        assert_eq!(info_.file_type, "image/png");
        assert_eq!(info_.hash, info.hash);
    }
}
