use super::*;
use derive_getters::Getters;
use domain;
use serde::{Deserialize, Serialize};
use serde_json;
use std::collections::HashMap;
use std::fs;
use std::string::String;

#[derive(Getters)]
pub struct JsonRepository {
    pub directory: String,
    users: HashMap<String, domain::User>,
}

impl Repository for JsonRepository {
    fn save(&mut self, user: &domain::User) -> Result<(), Error> {
        match self.users.get(&(user.name().clone())) {
            Some(_) => Err(Error::UserAlreadyExist),
            None => {
                self.users.insert(user.name().clone(), user.clone());
                let data = Data {
                    users: self.users.clone(),
                };
                match serde_json::to_string(&data) {
                    Ok(s) => match fs::write(self.directory.clone(), s) {
                        Ok(_) => Ok(()),
                        Err(e) => Err(Error::NotAbleToSave(format!("{}", e))),
                    },
                    Err(e) => Err(Error::NotAbleToSave(format!("{}", e))),
                }
            }
        }
    }

    fn load(&self, user: &String) -> Result<domain::User, Error> {
        match self.users.get(user) {
            Some(u) => Ok(u.clone()),
            None => Err(Error::UserDontExist),
        }
    }

    fn update(&mut self, original_user: &String, user: &domain::User) -> Result<(), Error> {
        match self.users.get(original_user) {
            Some(_) => {
                self.users.insert(user.name().clone(), user.clone());
                let data = Data {
                    users: self.users.clone(),
                };
                match serde_json::to_string(&data) {
                    Ok(s) => match fs::write(self.directory.clone(), s) {
                        Ok(_) => Ok(()),
                        Err(e) => Err(Error::NotAbleToSave(format!("{}", e))),
                    },
                    Err(e) => Err(Error::NotAbleToSave(format!("{}", e))),
                }
            }

            None => Err(Error::UserDontExist),
        }
    }

    fn delete(&mut self, user: &String) -> Result<(), Error> {
        match self.users.remove(user) {
            Some(_) => {
                let data = Data {
                    users: self.users.clone(),
                };
                match serde_json::to_string(&data) {
                    Ok(s) => match fs::write(self.directory.clone(), s) {
                        Ok(_) => Ok(()),
                        Err(e) => Err(Error::NotAbleToSave(format!("{}", e))),
                    },
                    Err(e) => Err(Error::NotAbleToSave(format!("{}", e))),
                }
            }
            None => Err(Error::UserDontExist),
        }
    }

    fn size(&self) -> Result<usize, Error> {
        Ok(self.users.len())
    }

    fn clear(&mut self) -> Result<(), Error> {
        self.users = HashMap::new();

        let data = Data {
            users: self.users.clone(),
        };
        match serde_json::to_string(&data) {
            Ok(s) => match fs::write(self.directory.clone(), s) {
                Ok(_) => Ok(()),
                Err(e) => Err(Error::NotAbleToSave(format!("{}", e))),
            },
            Err(e) => Err(Error::NotAbleToSave(format!("{}", e))),
        }
    }
}

impl JsonRepository {
    pub fn new(directory: &String) -> Result<Self, Error> {
        let mut repo = JsonRepository {
            directory: directory.clone(),
            users: HashMap::new(),
        };
        match repo.get_all_from_file() {
            Ok(d) => {
                repo.users = d.users;
                Ok(repo)
            }
            Err(e) => match e {
                Error::NotAbleToAccesRepository(_) => match fs::File::create(directory) {
                    Ok(_) => Ok(repo),
                    Err(e) => Err(Error::NotAbleToInstantiateRepository(format!("{}", e))),
                },
                _ => Err(e),
            },
        }
    }
    fn get_all_from_file(&self) -> Result<Data, Error> {
        match fs::read_to_string(self.directory.clone()) {
            Ok(s) => match Self::convert_json_to_object(&s) {
                Ok(d) => Ok(d),
                Err(e) => Err(Error::NotAbleToLoadData(format!("{}", e))),
            },
            Err(e) => Err(Error::NotAbleToAccesRepository(format!("{}", e))),
        }
    }

    fn convert_json_to_object(s: &String) -> Result<Data, serde_json::Error> {
        let data: Data = serde_json::from_str(s)?;
        Ok(data)
    }
}

#[derive(Deserialize, Serialize, Debug)]
struct Data {
    pub users: HashMap<String, domain::User>,
}
