use actix_web::HttpResponse;
use actix_web::http::StatusCode;
use serde::ser::SerializeStruct;
use serde::{Serialize, Serializer};

#[derive(Debug)]
pub enum DatabaseError {
    DeleteError,
    InsertError(diesel::result::Error),
    InsertGetError,
    NotFound,
    Protected,
    UpdateError(diesel::result::Error),
}

impl DatabaseError {
    fn get_code(&self) -> StatusCode {
        match self {
            DatabaseError::NotFound => StatusCode::NOT_FOUND,
            DatabaseError::Protected => StatusCode::FORBIDDEN,
            _ => StatusCode::INTERNAL_SERVER_ERROR
        }
    }
}

impl Serialize for DatabaseError {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut s = serializer.serialize_struct("error", 3)?;
        s.serialize_field("type", "database-error")?;
        s.serialize_field("code", &self.get_code().as_u16())?;
        s.serialize_field("description", &String::from(self))?;
        s.end()
    }
}

impl From<&DatabaseError> for String {
    fn from(err: &DatabaseError) -> Self {
        match err {
            DatabaseError::InsertError(_) => String::from("error on inserting into database"),
            DatabaseError::InsertGetError => {
                String::from("error on retrieving new entry from database (your entry was saved)")
            }
            DatabaseError::NotFound => String::from("model was not found in database"),
            DatabaseError::DeleteError => String::from("error on deleting from database"),
            DatabaseError::Protected => String::from("model is protected"),
            DatabaseError::UpdateError(_) => String::from("error on updating the model"),
        }
    }
}

impl From<DatabaseError> for HttpResponse {
    fn from(err: DatabaseError) -> Self {
        HttpResponse::build(err.get_code()).json(err)
    }
}