//! Authentication and Authorization Module
//!
//! Comprehensive authentication system with JWT tokens, password hashing,
//! user management, and Cedar policy integration.
//!
//! # Architecture
//!
//! ```text
//! ┌─────────────────────────────────────────┐
//! │     Authentication Module               │
//! ├─────────────────────────────────────────┤
//! │                                         │
//! │  ┌──────────────┐  ┌─────────────────┐ │
//! │  │ JWT Service  │  │ Password Service│ │
//! │  │              │  │                 │ │
//! │  │ • Generate   │  │ • Hash (Argon2) │ │
//! │  │ • Validate   │  │ • Verify        │ │
//! │  │ • Rotate     │  │ • Strength      │ │
//! │  │ • Revoke     │  └─────────────────┘ │
//! │  └──────────────┘                      │
//! │         │                              │
//! │         ▼                              │
//! │  ┌──────────────┐                     │
//! │  │ User Service │                     │
//! │  │              │                     │
//! │  │ • Create     │                     │
//! │  │ • Login      │                     │
//! │  │ • Logout     │                     │
//! │  │ • Update     │                     │
//! │  └──────────────┘                     │
//! └─────────────────────────────────────────┘
//! ```
//!
//! # Token Flow
//!
//! ```text
//! 1. Login Request
//!    ↓
//! 2. Validate Credentials (Password Service)
//!    ↓
//! 3. Generate Token Pair (JWT Service)
//!    ├─ Access Token (15 min)
//!    └─ Refresh Token (7 days)
//!    ↓
//! 4. Return Token Pair
//!    ↓
//! 5. Client Uses Access Token
//!    ↓
//! 6. Access Token Expires
//!    ↓
//! 7. Rotate Using Refresh Token
//!    ↓
//! 8. New Token Pair Generated
//! ```
//!
//! # Security Features
//!
//! - **RS256 Asymmetric Signing**: Enhanced security over symmetric HMAC
//! - **Token Rotation**: Automatic rotation before expiry
//! - **Token Revocation**: Blacklist-based revocation
//! - **Password Hashing**: Argon2id (memory-hard, side-channel resistant)
//! - **Cedar Integration**: Permissions hash for quick validation
//! - **Thread-Safe**: Arc + RwLock for concurrent access
//!
//! # Usage Example
//!
//! ```no_run
//! # use control_center::auth::{JwtService, PasswordService};
//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
//! // Initialize services
//! let private_key = std::fs::read("keys/private.pem")?;
//! let public_key = std::fs::read("keys/public.pem")?;
//!
//! let jwt_service = JwtService::new(
//!     &private_key,
//!     &public_key,
//!     "control-center",
//!     vec!["orchestrator".to_string(), "cli".to_string()],
//! )?;
//!
//! let password_service = PasswordService::new();
//!
//! // Verify password
//! let password_hash = password_service.hash_password("secure_password")?;
//! if password_service.verify_password("secure_password", &password_hash)? {
//!     // Generate token pair for authenticated user
//!     let tokens = jwt_service.generate_token_pair(
//!         "user123",
//!         "workspace1",
//!         "permissions_hash",
//!         None,
//!     )?;
//!
//!     println!("Access token: {}", tokens.access_token);
//!     println!("Expires in: {} seconds", tokens.expires_in);
//!
//!     // Validate token
//!     let claims = jwt_service.validate_token(&tokens.access_token)?;
//!     println!("User: {}, Workspace: {}", claims.sub, claims.workspace);
//!
//!     // Rotate token before expiry
//!     if claims.needs_rotation() {
//!         let new_tokens = jwt_service.rotate_token(&tokens.refresh_token)?;
//!         println!("New access token: {}", new_tokens.access_token);
//!     }
//!
//!     // Revoke token (logout)
//!     jwt_service.revoke_token(&claims.jti, claims.exp)?;
//! }
//! # Ok(())
//! # }
//! ```

pub mod jwt;
pub mod password;
pub mod user;

// Re-export commonly used types
use std::collections::HashMap;
use std::sync::Arc;

pub use jwt::{BlacklistStats, JwtService, TokenClaims, TokenPair, TokenType};
pub use password::{PasswordService, PasswordStrength};
pub use user::{User, UserRole, UserService, UserStatus};

use crate::error::{auth, http, infrastructure, ControlCenterError, Result};
#[cfg(feature = "mfa")]
use crate::mfa::MfaService;

/// Unified authentication service combining JWT, password, and MFA management
#[allow(clippy::manual_non_exhaustive)]
pub struct AuthService {
    /// JWT token service
    pub jwt: Arc<JwtService>,

    /// Password management service
    pub password: Arc<PasswordService>,

    /// User management service
    pub user: Arc<UserService>,

    /// MFA service (optional)
    #[cfg(feature = "mfa")]
    pub mfa: Option<Arc<MfaService>>,

    /// MFA service placeholder when feature is disabled
    #[cfg(not(feature = "mfa"))]
    _mfa_placeholder: (),
}

impl AuthService {
    /// Create new authentication service
    ///
    /// # Arguments
    /// * `jwt_service` - JWT token service
    /// * `password_service` - Password management service
    /// * `user_service` - User management service
    pub fn new(
        jwt_service: JwtService,
        password_service: PasswordService,
        user_service: UserService,
    ) -> Self {
        #[cfg(feature = "mfa")]
        {
            Self {
                jwt: Arc::new(jwt_service),
                password: Arc::new(password_service),
                user: Arc::new(user_service),
                mfa: None,
            }
        }
        #[cfg(not(feature = "mfa"))]
        {
            Self {
                jwt: Arc::new(jwt_service),
                password: Arc::new(password_service),
                user: Arc::new(user_service),
                _mfa_placeholder: (),
            }
        }
    }

    /// Create new authentication service with MFA support
    #[cfg(feature = "mfa")]
    pub fn with_mfa(
        jwt_service: JwtService,
        password_service: PasswordService,
        user_service: UserService,
        mfa_service: MfaService,
    ) -> Self {
        Self {
            jwt: Arc::new(jwt_service),
            password: Arc::new(password_service),
            user: Arc::new(user_service),
            mfa: Some(Arc::new(mfa_service)),
        }
    }

    /// Authenticate user and generate token pair
    ///
    /// # Arguments
    /// * `username` - Username or email
    /// * `password` - Plain text password
    /// * `workspace` - Workspace identifier
    ///
    /// # Returns
    /// Token pair on successful authentication (or partial token if MFA
    /// required)
    pub async fn login(
        &self,
        username: &str,
        password: &str,
        workspace: &str,
    ) -> Result<TokenPair> {
        // Find user
        let user = self.user.find_by_username(username).await?.ok_or_else(|| {
            ControlCenterError::Auth(auth::AuthError::Authentication(
                "Invalid credentials".to_string(),
            ))
        })?;

        // Check user status
        if user.status != UserStatus::Active {
            return Err(ControlCenterError::Auth(auth::AuthError::Authentication(
                format!("User account is {}", user.status),
            )));
        }

        // Verify password
        if !self
            .password
            .verify_password(password, &user.password_hash)?
        {
            return Err(ControlCenterError::Auth(auth::AuthError::Authentication(
                "Invalid credentials".to_string(),
            )));
        }

        // Check if MFA is required
        #[cfg(feature = "mfa")]
        {
            if let Some(ref mfa_service) = self.mfa {
                if mfa_service.user_has_mfa(&user.id).await? {
                    // Return partial token - user must complete MFA
                    // The token has limited permissions and expires in 5 minutes
                    let mut metadata = HashMap::new();
                    metadata.insert("mfa_required".to_string(), "true".to_string());
                    metadata.insert("ttl".to_string(), "300".to_string());
                    return self.jwt.generate_token_pair(
                        &user.id,
                        workspace,
                        "mfa_pending",
                        Some(metadata),
                    );
                }
            }
        }

        // Update last login
        self.user.update_last_login(&user.id).await?;

        // Generate permissions hash from user roles
        let permissions_hash = self.generate_permissions_hash(&user.roles);

        // Generate token pair
        self.jwt
            .generate_token_pair(&user.id, workspace, &permissions_hash, None)
    }

    /// Complete MFA verification and upgrade partial token to full access
    ///
    /// # Arguments
    /// * `partial_token` - Partial access token from initial login
    /// * `mfa_code` - MFA verification code (TOTP or backup code)
    ///
    /// # Returns
    /// Full access token pair on successful MFA verification
    #[cfg(feature = "mfa")]
    pub async fn complete_mfa_login(
        &self,
        partial_token: &str,
        mfa_code: &str,
    ) -> Result<TokenPair> {
        // Validate partial token
        let claims = self.jwt.validate_token(partial_token)?;

        // Verify it's a pending MFA token
        if claims.permissions_hash != "mfa_pending" {
            return Err(ControlCenterError::Auth(auth::AuthError::Authentication(
                "Invalid MFA token".to_string(),
            )));
        }

        // Get MFA service
        let mfa_service = self.mfa.as_ref().ok_or_else(|| {
            ControlCenterError::Infrastructure(infrastructure::InfrastructureError::Internal(
                "MFA not configured".to_string(),
            ))
        })?;

        // Get user for email
        let user = self.user.find_by_id(&claims.sub).await?.ok_or_else(|| {
            ControlCenterError::Http(http::HttpError::NotFound("User not found".to_string()))
        })?;

        // Verify MFA code
        let verification = mfa_service
            .verify_any_mfa(&user.id, &user.email, mfa_code)
            .await?;

        if !verification.verified {
            return Err(ControlCenterError::Auth(auth::AuthError::Authentication(
                "Invalid MFA code".to_string(),
            )));
        }

        // Revoke partial token
        self.jwt.revoke_token(&claims.jti, claims.exp)?;

        // Update last login
        self.user.update_last_login(&user.id).await?;

        // Generate full permissions hash
        let permissions_hash = self.generate_permissions_hash(&user.roles);

        // Generate full access token pair
        self.jwt
            .generate_token_pair(&user.id, &claims.workspace, &permissions_hash, None)
    }

    /// Logout user by revoking tokens
    pub async fn logout(&self, access_token: &str) -> Result<()> {
        let claims = self.jwt.validate_token(access_token)?;
        self.jwt.revoke_token(&claims.jti, claims.exp)?;
        Ok(())
    }

    /// Validate access token and return claims
    pub fn validate(&self, token: &str) -> Result<TokenClaims> {
        self.jwt.validate_token(token)
    }

    /// Rotate tokens using refresh token
    pub fn refresh(&self, refresh_token: &str) -> Result<TokenPair> {
        self.jwt.rotate_token(refresh_token)
    }

    /// Generate permissions hash from user roles
    ///
    /// This creates a stable hash of the user's Cedar permissions
    /// for quick validation without policy evaluation
    fn generate_permissions_hash(&self, roles: &[UserRole]) -> String {
        use sha2::{Digest, Sha256};

        let mut hasher = Sha256::new();
        for role in roles {
            hasher.update(role.to_string().as_bytes());
        }

        format!("{:x}", hasher.finalize())
    }
}

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

    fn create_test_jwt_service() -> JwtService {
        // Pre-generated RSA keys to avoid runtime key generation (avoids rand_core
        // conflict)
        let private_pem = b"-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC7F43HxrVfJJ+k
DQMEjENGqJLnBn6MvJnCu93A4ZNKEEpPGX1Y6V+qiqLH5B7wNMIJ2QVnLjYjKZu5
K5LkVCH8N7N1s9QXiLI7TzQVEH2TQx3MzQFRmVzGlyVFxF0qMHf/mNlZvE0Hb9oI
YdYnBW5k4TRmzrN4THEbWqLcGVVfMVFQqG1j/V3G5dTpV1sN8a9xqJG0dKNmVP2M
H5JG3kQ8CxFqp7pRsGvqH9IVjB6KXj8L3VPQYJxIBKHGZMlsP9SVqH3vwqLLKcBj
G2UvhkVYJ6ZLvYIj7LvZKrCBrP3h5Q7hYpXZqDGYd5dDwWFJ3xkVMkJMGR3D3VrK
R1jvJTEhAgMBAAECggEAXV2TZjfRbhR0IaqU3bpSZvb+6uL+5OgWRO5VbG0XGFmv
1v+2F5hQEZYKKxZvTlF4IxN1J0YQRKt4BG6PZqN9+Uj8z7DVEDqiWLIZSEhUPzVu
E3IKZaEqvGKvEwBglmfqnE6cEZ2Kb9aOx1a5N8UH5q7xQ2d4YxKfYwRBkDYEZmZp
R4tEb0qfKFgIGmCfK5fNvJqXvH/f1xq9QW1qrGYqGAqh7bkxvXGfkRBXh7Q8sAXW
B2XQ8L9CZV5lsP0F7n8nKvDVKfqZfGXlZJZQvJPF5L8p6g7V7uE+6bZlF4YZLRvW
2aq2xZR6bGCWxBpLb2lG1QU0aJLqVmVjAQYDM9PB0QKBgQDfhKn8F5aITVFJhVVu
BN8F2hLRtFDfSPTN0JM1WZq/EqYdLjnlAY8L7kBBzNaKgOGBRqfO1lVv7DG4vPtX
nrLaM3SU3F0lC8tVwqJTUvZS5/Pl3wqAZRlWbWm4Yp6gWXlGPTaWRpGRBOqB+rWP
b8rX2SnZjvFlQZlZPfvHGWpkFQKBgQDYtvXAGNdj8hMZ6mMUKYv3YbF7F2xUlf1x
H0f8I5QGPiZoqzNGJXCEGvNVNEUgR7LY6AeqWtLqRKJsQ5NYb2XzLhA2BVOV1BV2
Z1Y3s0V5XjFE4q7gWmEcLlWyEiKiRCRQC4T7qB+gUQ8lFBrGqQ4vZqE+7pQyYfCt
1yFQnvqrQQKBgQCcvCvE4wMhF2lYFxM4v8qCDXJ9nLMKjYPFn3C0YrP6Zn2Pm7r3
fJGDVBmN5wVLDXqZnSrmh0iBs7PdBqeHALF7jTj9X8tNe+hpXwQ/GWSF8b3q1b6k
wZLhKpJQGpYH0M3l7B6rrM3Vr1E/DQjXvXdqXrL3vKfHQrEk3c9zVCkfTQKBgGVl
qdvBYvdgWHvPzL3+vqvLPVKDnLjN6hXA2cJGaBNpOlm+bI5cJLLnWhJrjCkK+POo
0oPKvnO5qKQjRDWLVCJH3z8MxPjqV6Yk1hAg/4vqV3oQsJDfU3qiDPAHrBbKBlRm
4KYmEqIhqNYJqXN7nnQFOLYFxOaXKqe6HBh1g4QBAoGBAIRXPwKJhwIUfLJ8DQAO
9z9YH3eIiGkLxF8f7WZvVnNwMEpXl4IqN8N3qGXKJfXJI1YfBKXN7LOqCOVFGkVq
JkVN8qPTwAKU8E2U3dGzQVPP2JmGPPNyZJxQJ3sYtKnLkz2YdEbKDhkG5wPDHKKu
LfEK1YMqL/VvAJdIXxqZqDXf
-----END PRIVATE KEY-----";

        let public_pem = b"-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuxeNx8a1XySfpA0DBIxD
RqiS5wZ+jLyZwrvdwOGTShBKTxl9WOlfqoqix+Qe8DTCCdkFZy42IymbhSuS5FQh
/DezdLPUF4iyO080FRB9k0MdzM0BUZlcxpclRcRdKjB3/5jZWbxNB2/aCGHWJwVu
ZOE0Zs6zeExxG1qi3BlVXzFRUKhtY/1dxuXU6Vdbl/GvcaiRtHSjZlT9jB+SRt5E
PAsTaqe6UbBr6h/SFYweil4/C91T0GCcSAShxmTJbD/Ulah978KizynAYxtlL4ZF
WCemS72CI+y72SqwgaZ94eUO4WKV2agxmHeXQ8FhSd8ZFTJCTBKDW91Kykdo7yUx
IQIDAQAB
-----END PUBLIC KEY-----";

        JwtService::new(
            private_pem,
            public_pem,
            "test-issuer",
            vec!["test-audience".to_string()],
        )
        .unwrap()
    }

    #[test]
    fn test_auth_service_creation() {
        let jwt_service = create_test_jwt_service();
        let password_service = PasswordService::new();
        let user_service = UserService::new();

        let auth_service = AuthService::new(jwt_service, password_service, user_service);

        assert!(Arc::strong_count(&auth_service.jwt) >= 1);
        assert!(Arc::strong_count(&auth_service.password) >= 1);
        assert!(Arc::strong_count(&auth_service.user) >= 1);
    }

    #[test]
    fn test_permissions_hash_generation() {
        let jwt_service = create_test_jwt_service();
        let password_service = PasswordService::new();
        let user_service = UserService::new();

        let auth_service = AuthService::new(jwt_service, password_service, user_service);

        let roles = vec![UserRole::Admin, UserRole::Developer];
        let hash1 = auth_service.generate_permissions_hash(&roles);
        let hash2 = auth_service.generate_permissions_hash(&roles);

        // Hash should be deterministic
        assert_eq!(hash1, hash2);
        assert_eq!(hash1.len(), 64); // SHA256 hex = 64 chars
    }
}
