use super::util::*;
use crate::schema::*;
use diesel::pg::*;
use diesel::prelude::*;
use rand::prelude::*;
use serde_json::{json, Serializer, Value};
use std::path::Path;
use std::time::*;
use uuid::Uuid;

pub fn db() -> PgConnection {
    let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
    PgConnection::establish(&database_url).expect(&format!("Error connecting to {}", database_url))
}

#[derive(Debug, Queryable, Identifiable, Insertable, serde::Serialize)]
pub struct User {
    pub id: i32,
    pub username: String,
    pub password: String,
    pub salt: String,
    pub image: String,
    pub email: String,
    pub google: String,
    pub email_valid: String,
    pub data: String
}

impl User {
    // pub fn create(key: String) -> Result<Self, String> {
    //     let uuid = Uuid::new_v4().to_string();
    //     println!("{}", uuid);
    //     let result = diesel::insert_into(users::table)
    //         .values((
    //             users::dsl::username.eq(key),
    //             users::dsl::password.eq("".to_string()),
    //             users::dsl::salt.eq("".to_string()),
    //             users::dsl::image.eq("/static/img/default_user.svg".to_string()),
    //             users::dsl::email_valid.eq(uuid),
    //             users::dsl::email.eq("".to_string()),
    //             users::dsl::google.eq("".to_string()),
    //         ))
    //         .get_result(&db());
    //     if !result.is_err() {
    //         let user_session = result.unwrap();
    //         return Ok(user_session);
    //     }
    //     Err(format!("User not created"))
    // }

    pub fn create(username: String, email: String, pass_not_hashed: String) -> Result<Self, String> {
        let mut rng = rand::thread_rng();
        let y: f64 = rng.gen();
        let salt = sha256::digest(y.to_string());

        let pass_hashed = sha256::digest(format!("{}{}", &salt, &pass_not_hashed));
        let uuid = Uuid::new_v4().to_string();
        let result = diesel::insert_into(users::table)
            .values((
                users::dsl::username.eq(username),
                users::dsl::password.eq(pass_hashed),
                users::dsl::salt.eq(salt),
                users::dsl::image.eq("/static/img/default_user.svg".to_string()),
                users::dsl::email_valid.eq(uuid),
                users::dsl::email.eq(email),
                users::dsl::google.eq("".to_string()),
                users::dsl::data.eq("{}".to_string()),
            ))
            .get_result(&db());
        if !result.is_err() {
            let user_session = result.unwrap();
            return Ok(user_session);
        }
        match result.err() {
            Some(x) => {
                if format!("{}", x).contains("email") {
                    return Err("Un compte est déjà associé à cet Email".to_string());
                } else if format!("{}", x).contains("username") {
                    return Err("Ce nom d'utilisateur est déja utilisé".to_string());
                }
                return Err("Could not create user".to_string());
            },
            None => return Err("Could not create user".to_string())
        }
        Err("Could not create user".to_string())
    }

    pub fn find_all() -> Result<Vec<Self>, String> {
        let result = users::dsl::users.get_results::<Self>(&db());
        if !result.is_err() {
            let users = result.unwrap();
            return Ok(users);
        }
        Err(format!("Users not found"))
    }

    pub fn find(id: i32) -> Result<Self, String> {
        let result = users::dsl::users
            .filter(users::dsl::id.eq(id))
            .get_result::<Self>(&db());
        if !result.is_err() {
            let user_session = result.unwrap();
            return Ok(user_session);
        }
        Err(format!("User {} not found", id))
    }

    pub fn update(self) -> Result<Self, String> {
        let filter = users::dsl::users.filter(users::dsl::id.eq(self.id));
        let result = diesel::update(filter)
            .set((
                users::dsl::username.eq(self.username),
                users::dsl::password.eq(self.password),
                users::dsl::salt.eq(self.salt),
                users::dsl::image.eq(self.image),
                users::dsl::email.eq(self.email),
                users::dsl::email_valid.eq(self.email_valid),
                users::dsl::google.eq(self.google),
            ))
            .get_result::<Self>(&db());
        if !result.is_err() {
            let user_session = result.unwrap();
            return Ok(user_session);
        }
        Err(format!("User {} not update", self.id))
    }

    pub fn find_by_username(username: &str) -> Result<Self, String> {
        let result = users::dsl::users
            .filter(users::dsl::username.eq(&username))
            .get_result::<Self>(&db());
        if !result.is_err() {
            let user = result.unwrap();
            return Ok(user);
        }
        Err(format!("User {} not find", &username))
    }

    pub fn find_by_changepass(changepass: &str) -> Result<Self, String> {
        let result = users::dsl::users
            .filter(users::dsl::password.eq(&changepass))
            .get_result::<Self>(&db());
        if !result.is_err() {
            let user = result.unwrap();
            return Ok(user);
        }
        Err(format!("User {} not find", &changepass))
    }

    pub fn find_by_email(email: &str) -> Result<Self, String> {
        let result = users::dsl::users
            .filter(users::dsl::email.eq(&email))
            .get_result::<Self>(&db());
        if !result.is_err() {
            let user = result.unwrap();
            return Ok(user);
        }
        Err(format!("User {} not find", &email))
    }

    pub fn find_by_key(key: &str) -> Result<Self, String> {
        let result = users::dsl::users
            .filter(users::dsl::username.eq(&key))
            .filter(users::dsl::password.eq(""))
            .get_result::<Self>(&db());
        if !result.is_err() {
            let user = result.unwrap();
            return Ok(user);
        }
        Err(format!("User {} not find", &key))
    }

    pub fn find_by_google(google: &str) -> Result<Self, String> {
        let result = users::dsl::users
            .filter(users::dsl::google.eq(&google))
            .get_result::<Self>(&db());
        if !result.is_err() {
            let user = result.unwrap();
            return Ok(user);
        }
        Err(format!("User by google {} not find", &google))
    }

    pub fn find_by_email_valid(email_valid: &str) -> Result<Self, String> {
        let result = users::dsl::users
            .filter(users::dsl::email_valid.eq(&email_valid))
            .get_result::<Self>(&db());
        if !result.is_err() {
            let user = result.unwrap();
            return Ok(user);
        }
        Err(format!("User by email_valid {} not find", &email_valid))
    }

    pub fn generate_salt(&mut self) {
        let mut rng = rand::thread_rng();
        let y: f64 = rng.gen(); // generates a float between 0 and 1
        self.salt = sha256::digest(y.to_string())
    }

    pub fn delete(self) -> Result<String, String> {
        let id = self.id.clone();
        let name = self.username.clone();
        let filter_user = users::dsl::users.filter(users::dsl::id.eq(&id));
        let result_del_user = diesel::delete(filter_user)
            .returning(users::dsl::username)
            .get_result::<String>(&db());
        if result_del_user.is_err() {
            return Err(format!("User cannot be deleted in db()"));
        }
        return Ok("User was deleted".to_string());
    }

    pub fn to_json(self) -> Value {
        let mut user_value = json!(self);
        user_value
    }
}
