//! Password Management Service
//!
//! Secure password hashing and verification using Argon2id algorithm.

use argon2::{
    password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
    Argon2,
};

use crate::error::{auth, ControlCenterError, Result};

/// Password strength levels
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum PasswordStrength {
    /// Weak password (< 8 chars, no complexity)
    Weak,
    /// Fair password (8+ chars, limited complexity)
    Fair,
    /// Good password (10+ chars, good complexity)
    Good,
    /// Strong password (12+ chars, high complexity)
    Strong,
    /// Very strong password (16+ chars, full complexity)
    VeryStrong,
}

/// Password management service
pub struct PasswordService {
    argon2: Argon2<'static>,
}

impl PasswordService {
    /// Create new password service with default Argon2 configuration
    pub fn new() -> Self {
        Self {
            argon2: Argon2::default(),
        }
    }

    /// Hash password using Argon2id
    ///
    /// Uses cryptographically secure random salt
    pub fn hash_password(&self, password: &str) -> Result<String> {
        let salt = SaltString::generate(&mut OsRng);

        self.argon2
            .hash_password(password.as_bytes(), &salt)
            .map(|hash| hash.to_string())
            .map_err(|e| {
                ControlCenterError::Auth(auth::AuthError::Authentication(format!(
                    "Password hashing failed: {}",
                    e
                )))
            })
    }

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

        Ok(self
            .argon2
            .verify_password(password.as_bytes(), &parsed_hash)
            .is_ok())
    }

    /// Evaluate password strength
    pub fn evaluate_strength(&self, password: &str) -> PasswordStrength {
        let len = password.len();
        let has_lowercase = password.chars().any(|c| c.is_lowercase());
        let has_uppercase = password.chars().any(|c| c.is_uppercase());
        let has_digit = password.chars().any(|c| c.is_ascii_digit());
        let has_special = password.chars().any(|c| !c.is_alphanumeric());

        let complexity_score = [has_lowercase, has_uppercase, has_digit, has_special]
            .iter()
            .filter(|&&x| x)
            .count();

        match (len, complexity_score) {
            (0..=7, _) => PasswordStrength::Weak,
            (8..=9, 0..=2) => PasswordStrength::Fair,
            (8..=9, 3..=4) => PasswordStrength::Good,
            (10..=11, 0..=2) => PasswordStrength::Fair,
            (10..=11, 3..=4) => PasswordStrength::Good,
            (12..=15, 0..=2) => PasswordStrength::Good,
            (12..=15, 3..=4) => PasswordStrength::Strong,
            (16.., 0..=2) => PasswordStrength::Good,
            (16.., 3) => PasswordStrength::Strong,
            (16.., 4..) => PasswordStrength::VeryStrong,
            _ => PasswordStrength::Weak,
        }
    }

    /// Check if password meets minimum requirements
    ///
    /// Requirements:
    /// - At least 8 characters
    /// - At least 2 character types (lowercase, uppercase, digit, special)
    pub fn meets_requirements(&self, password: &str) -> bool {
        matches!(
            self.evaluate_strength(password),
            PasswordStrength::Fair
                | PasswordStrength::Good
                | PasswordStrength::Strong
                | PasswordStrength::VeryStrong
        )
    }
}

impl Default for PasswordService {
    fn default() -> Self {
        Self::new()
    }
}

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

    #[test]
    fn test_password_hashing() {
        let service = PasswordService::new();
        let password = "test_password_123";

        let hash = service.hash_password(password).unwrap();
        assert!(!hash.is_empty());
        assert!(hash.starts_with("$argon2"));
    }

    #[test]
    fn test_password_verification() {
        let service = PasswordService::new();
        let password = "test_password_123";

        let hash = service.hash_password(password).unwrap();

        // Correct password should verify
        assert!(service.verify_password(password, &hash).unwrap());

        // Wrong password should fail
        assert!(!service.verify_password("wrong_password", &hash).unwrap());
    }

    #[test]
    fn test_password_strength_weak() {
        let service = PasswordService::new();
        assert_eq!(service.evaluate_strength("abc"), PasswordStrength::Weak);
        assert_eq!(service.evaluate_strength("1234567"), PasswordStrength::Weak);
    }

    #[test]
    fn test_password_strength_fair() {
        let service = PasswordService::new();
        // Fair: 8-9 chars with 0-2 complexity types (lowercase, uppercase, digit,
        // special)
        assert_eq!(
            service.evaluate_strength("password1"), // 9 chars, 2 types: lowercase + digit
            PasswordStrength::Fair
        );
    }

    #[test]
    fn test_password_strength_good() {
        let service = PasswordService::new();
        assert_eq!(
            service.evaluate_strength("Password123"),
            PasswordStrength::Good
        );
    }

    #[test]
    fn test_password_strength_strong() {
        let service = PasswordService::new();
        assert_eq!(
            service.evaluate_strength("Password123!"),
            PasswordStrength::Strong
        );
    }

    #[test]
    fn test_password_strength_very_strong() {
        let service = PasswordService::new();
        assert_eq!(
            service.evaluate_strength("MyP@ssw0rd!2024Secure"),
            PasswordStrength::VeryStrong
        );
    }

    #[test]
    fn test_password_requirements() {
        let service = PasswordService::new();

        // Too weak
        assert!(!service.meets_requirements("abc"));
        assert!(!service.meets_requirements("1234567"));

        // Meets requirements
        assert!(service.meets_requirements("Password1"));
        assert!(service.meets_requirements("MyPassword123"));
        assert!(service.meets_requirements("Secure!Pass2024"));
    }

    #[test]
    fn test_different_salts() {
        let service = PasswordService::new();
        let password = "test_password";

        let hash1 = service.hash_password(password).unwrap();
        let hash2 = service.hash_password(password).unwrap();

        // Different salts should produce different hashes
        assert_ne!(hash1, hash2);

        // Both should verify correctly
        assert!(service.verify_password(password, &hash1).unwrap());
        assert!(service.verify_password(password, &hash2).unwrap());
    }
}
