//! Password hashing using Argon2id
//!
//! SEC-9: Explicitly uses Argon2id (hybrid variant) for password hashing
//! Argon2id provides resistance against both side-channel and GPU attacks

use anyhow::{anyhow, Result};
use argon2::{
    password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
    Algorithm, Argon2, Params, Version,
};

/// Hash a password using Argon2id
///
/// SEC-9: Explicitly configured to use Argon2id variant
/// - Algorithm: Argon2id (hybrid of Argon2i and Argon2d)
/// - Version: 0x13 (latest version)
/// - Memory: 19456 KiB (default)
/// - Iterations: 2 (default)
/// - Parallelism: 1 (default)
pub fn hash_password(password: &str) -> Result<String> {
    let salt = SaltString::generate(&mut OsRng);

    // Explicitly use Argon2id (Algorithm::Argon2id)
    let argon2 = Argon2::new(
        Algorithm::Argon2id, // SEC-9: Explicit Argon2id variant
        Version::V0x13,      // Latest version
        Params::default(),   // Default params (19456 KiB, 2 iterations, 1 parallelism)
    );

    let hash = argon2
        .hash_password(password.as_bytes(), &salt)
        .map_err(|e| anyhow!("Failed to hash password: {}", e))?;
    Ok(hash.to_string())
}

/// Verify a password against a stored hash
pub fn verify_password(password: &str, hash: &str) -> Result<bool> {
    let parsed_hash =
        PasswordHash::new(hash).map_err(|e| anyhow!("Invalid password hash format: {}", e))?;

    // Argon2::default() uses Argon2id, but we verify against the hash's embedded algorithm
    let argon2 = Argon2::default();
    Ok(argon2
        .verify_password(password.as_bytes(), &parsed_hash)
        .is_ok())
}

/// Generate a random password (for initial admin)
pub fn generate_random_password(length: usize) -> String {
    use rand::Rng;
    const CHARSET: &[u8] = b"ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789!@#$%";
    let mut rng = rand::thread_rng();
    (0..length)
        .map(|_| {
            let idx = rng.gen_range(0..CHARSET.len());
            CHARSET[idx] as char
        })
        .collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_hash_and_verify() {
        let password = "test_password_123";
        let hash = hash_password(password).unwrap();
        assert!(verify_password(password, &hash).unwrap());
        assert!(!verify_password("wrong_password", &hash).unwrap());
    }

    #[test]
    fn test_random_password() {
        let password = generate_random_password(16);
        assert_eq!(password.len(), 16);
    }
}
