use std::{
    fs::File,
    time::{SystemTime, UNIX_EPOCH},
};

use password_hash::{PasswordHash, PasswordHasher, SaltString};
use pbkdf2::{Params, Pbkdf2};
use serde::{de::DeserializeOwned, Serialize};

#[must_use]
pub fn load_or_create_config<T>(path: &str) -> T
where
    T: Default + Serialize + DeserializeOwned,
{
    std::fs::read_to_string(path).map_or_else(
        |_| {
            let defaults = T::default();
            std::fs::write(path, toml::to_string(&defaults).unwrap()).unwrap();
            defaults
        },
        |data| toml::from_str(&data).unwrap(),
    )
}

#[must_use]
pub fn open_secret_key() -> Result<File, std::io::Error> {
    File::open("assets/security/client_secret_key.ec2b")
}

#[must_use]
pub fn cur_timestamp_ms() -> u64 {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_millis() as u64
}

#[must_use]
pub fn cur_timestamp() -> u64 {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_secs() as u64
}

#[must_use]
pub fn hash_string(content: &str) -> Result<String, pbkdf2::password_hash::Error> {
    let salt = SaltString::generate(rand::thread_rng());
    let hash = Pbkdf2.hash_password_customized(
        content.as_bytes(),
        None,
        None,
        Params {
            rounds: 10000,
            output_length: 32,
        },
        &salt,
    )?;

    Ok(hash.serialize().to_string())
}

#[must_use]
pub fn verify_hash(content: &str, hash_str: &str) -> Option<()> {
    let hash = PasswordHash::new(hash_str).ok()?;
    hash.verify_password(&[&Pbkdf2], content).ok()
}
