// License: see LICENSE file at root directory of `master` branch

//! # Users

use {
    std::io::{self, Error, ErrorKind},

    bailamos::db::Id,
    rusqlite::Connection,
    zeros::Hash,

    crate::db::ID,
};

/// # Passphrase
pub type Passphrase = Vec<u8>;

/// # Table
pub const TABLE: &str = "users";

/// # Date created
pub const DATE_CREATED: &str = "date_created";

/// # Date modified
pub const DATE_MODIFIED: &str = "date_modified";

/// # Name
pub const NAME: &str = "name";

/// # Passphrase
pub const PASSPHRASE: &str = "passphrase";

/// # E-mail
pub const EMAIL: &str = "email";

/// # User
#[derive(Debug)]
pub struct User {
    id: Option<Id>,
    name: String,
    passphrase: Passphrase,
    email: Option<String>,
}

impl User {

    /// # Makes new instance
    pub const fn new(id: Option<Id>, name: String, passphrase: Passphrase, email: Option<String>) -> Self {
        Self {
            id,
            name,
            passphrase,
            email,
        }
    }

    /// # ID
    pub fn id(&self) -> Option<Id> {
        self.id
    }

    /// # Name
    pub fn name(&self) -> &str {
        &self.name
    }

    /// # Passphrase
    pub fn passphrase(&self) -> &Passphrase {
        &self.passphrase
    }

    /// # E-mail
    pub fn email(&self) -> Option<&str> {
        self.email.as_ref().map(|e| e.as_str())
    }

}

/// # Hashes passphrase
pub fn hash_passphrase<B>(passphrase: B) -> Passphrase where B: AsRef<[u8]> {
    Hash::Sha3_512.hash(Hash::Sha3_512.hash(passphrase))
}

/// # Loads users, sorted by name, in ascending order
pub fn load_users(conn: &Connection) -> io::Result<Vec<User>> {
    let result = move || -> rusqlite::Result<_> {
        let mut statement = conn.prepare_cached(&format!(
            "select {id},{name},{passphrase},{email} from {table} order by {name};",
            id=ID, name=NAME, passphrase=PASSPHRASE, email=EMAIL, table=TABLE,
        ))?;
        let mut result = vec![];
        for r in statement.query_map(rusqlite::NO_PARAMS, |r| Ok((r.get(0)?, r.get(1)?, r.get::<_, Vec<u8>>(2)?, r.get(3)?)))? {
            let (id, name, passphrase, email) = r?;
            result.push(User { id, name, passphrase, email});
        }
        Ok(result)
    };
    result().map_err(|e| Error::new(ErrorKind::Other, e))
}
