//! User Management Service
//!
//! Handles user creation, retrieval, and management operations.

use std::collections::HashMap;
use std::fmt;
use std::sync::{Arc, RwLock};

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

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

/// User role enumeration
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum UserRole {
    /// System administrator with full access
    Admin,
    /// Developer with deployment permissions
    Developer,
    /// Operator with read/write access
    Operator,
    /// Viewer with read-only access
    Viewer,
    /// Auditor with audit log access
    Auditor,
}

impl fmt::Display for UserRole {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            UserRole::Admin => write!(f, "admin"),
            UserRole::Developer => write!(f, "developer"),
            UserRole::Operator => write!(f, "operator"),
            UserRole::Viewer => write!(f, "viewer"),
            UserRole::Auditor => write!(f, "auditor"),
        }
    }
}

/// User account status
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum UserStatus {
    /// Active user account
    Active,
    /// Suspended user account
    Suspended,
    /// Locked user account (too many failed logins)
    Locked,
    /// Disabled user account
    Disabled,
}

impl fmt::Display for UserStatus {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            UserStatus::Active => write!(f, "active"),
            UserStatus::Suspended => write!(f, "suspended"),
            UserStatus::Locked => write!(f, "locked"),
            UserStatus::Disabled => write!(f, "disabled"),
        }
    }
}

/// User entity
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
    /// Unique user identifier
    pub id: String,

    /// Username (unique)
    pub username: String,

    /// Email address (unique)
    pub email: String,

    /// Full name
    pub full_name: String,

    /// User roles
    pub roles: Vec<UserRole>,

    /// Account status
    pub status: UserStatus,

    /// Password hash (Argon2)
    #[serde(skip_serializing)]
    pub password_hash: String,

    /// MFA enabled flag
    pub mfa_enabled: bool,

    /// MFA secret (if enabled)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub mfa_secret: Option<String>,

    /// Account creation timestamp
    pub created_at: DateTime<Utc>,

    /// Last login timestamp
    pub last_login: Option<DateTime<Utc>>,

    /// Last password change timestamp
    pub password_changed_at: DateTime<Utc>,

    /// Failed login attempts
    pub failed_login_attempts: u32,

    /// Last failed login timestamp
    pub last_failed_login: Option<DateTime<Utc>>,

    /// User metadata
    #[serde(skip_serializing_if = "Option::is_none")]
    pub metadata: Option<HashMap<String, String>>,
}

impl User {
    /// Create new user
    pub fn new(
        username: impl Into<String>,
        email: impl Into<String>,
        full_name: impl Into<String>,
        password_hash: String,
        roles: Vec<UserRole>,
    ) -> Self {
        let now = Utc::now();

        Self {
            id: Uuid::new_v4().to_string(),
            username: username.into(),
            email: email.into(),
            full_name: full_name.into(),
            roles,
            status: UserStatus::Active,
            password_hash,
            mfa_enabled: false,
            mfa_secret: None,
            created_at: now,
            last_login: None,
            password_changed_at: now,
            failed_login_attempts: 0,
            last_failed_login: None,
            metadata: None,
        }
    }

    /// Check if user has specific role
    pub fn has_role(&self, role: UserRole) -> bool {
        self.roles.contains(&role)
    }

    /// Check if user has admin role
    pub fn is_admin(&self) -> bool {
        self.has_role(UserRole::Admin)
    }

    /// Check if user is active
    pub fn is_active(&self) -> bool {
        self.status == UserStatus::Active
    }

    /// Increment failed login attempts
    pub fn increment_failed_login(&mut self) {
        self.failed_login_attempts += 1;
        self.last_failed_login = Some(Utc::now());

        // Lock account after 5 failed attempts
        if self.failed_login_attempts >= 5 {
            self.status = UserStatus::Locked;
        }
    }

    /// Reset failed login attempts
    pub fn reset_failed_login(&mut self) {
        self.failed_login_attempts = 0;
        self.last_failed_login = None;
    }
}

/// User management service
pub struct UserService {
    /// In-memory user store (replace with database in production)
    users: Arc<RwLock<HashMap<String, User>>>,

    /// Username to ID mapping
    username_map: Arc<RwLock<HashMap<String, String>>>,

    /// Email to ID mapping
    email_map: Arc<RwLock<HashMap<String, String>>>,
}

impl UserService {
    /// Create new user service
    pub fn new() -> Self {
        Self {
            users: Arc::new(RwLock::new(HashMap::new())),
            username_map: Arc::new(RwLock::new(HashMap::new())),
            email_map: Arc::new(RwLock::new(HashMap::new())),
        }
    }

    /// Create new user
    pub async fn create_user(&self, user: User) -> Result<User> {
        let mut users = self.users.write().map_err(|e| {
            ControlCenterError::from(infrastructure::internal_error(&format!(
                "Failed to acquire write lock: {}",
                e
            )))
        })?;

        let mut username_map = self.username_map.write().map_err(|e| {
            ControlCenterError::from(infrastructure::internal_error(&format!(
                "Failed to acquire write lock: {}",
                e
            )))
        })?;

        let mut email_map = self.email_map.write().map_err(|e| {
            ControlCenterError::from(infrastructure::internal_error(&format!(
                "Failed to acquire write lock: {}",
                e
            )))
        })?;

        // Check for duplicate username
        if username_map.contains_key(&user.username) {
            return Err(http::HttpError::Validation(format!(
                "Username '{}' already exists",
                user.username
            ))
            .into());
        }

        // Check for duplicate email
        if email_map.contains_key(&user.email) {
            return Err(http::HttpError::Validation(format!(
                "Email '{}' already exists",
                user.email
            ))
            .into());
        }

        let user_id = user.id.clone();
        let username = user.username.clone();
        let email = user.email.clone();

        users.insert(user_id.clone(), user.clone());
        username_map.insert(username, user_id.clone());
        email_map.insert(email, user_id);

        Ok(user)
    }

    /// Find user by ID
    pub async fn find_by_id(&self, id: &str) -> Result<Option<User>> {
        let users = self.users.read().map_err(|e| {
            ControlCenterError::from(infrastructure::internal_error(&format!(
                "Failed to acquire read lock: {}",
                e
            )))
        })?;

        Ok(users.get(id).cloned())
    }

    /// Find user by username
    pub async fn find_by_username(&self, username: &str) -> Result<Option<User>> {
        let user_id_opt = {
            let username_map = self.username_map.read().map_err(|e| {
                ControlCenterError::from(infrastructure::internal_error(&format!(
                    "Failed to acquire read lock: {}",
                    e
                )))
            })?;

            username_map.get(username).cloned()
        };

        match user_id_opt {
            Some(user_id) => self.find_by_id(&user_id).await,
            None => Ok(None),
        }
    }

    /// Find user by email
    pub async fn find_by_email(&self, email: &str) -> Result<Option<User>> {
        let user_id_opt = {
            let email_map = self.email_map.read().map_err(|e| {
                ControlCenterError::from(infrastructure::internal_error(&format!(
                    "Failed to acquire read lock: {}",
                    e
                )))
            })?;

            email_map.get(email).cloned()
        };

        match user_id_opt {
            Some(user_id) => self.find_by_id(&user_id).await,
            None => Ok(None),
        }
    }

    /// Update user
    pub async fn update_user(&self, user: User) -> Result<()> {
        let mut users = self.users.write().map_err(|e| {
            ControlCenterError::from(infrastructure::internal_error(&format!(
                "Failed to acquire write lock: {}",
                e
            )))
        })?;

        if !users.contains_key(&user.id) {
            return Err(auth::user_not_found(&format!("User with ID '{}'", user.id)).into());
        }

        users.insert(user.id.clone(), user);
        Ok(())
    }

    /// Update last login timestamp
    pub async fn update_last_login(&self, user_id: &str) -> Result<()> {
        let mut users = self.users.write().map_err(|e| {
            ControlCenterError::from(infrastructure::internal_error(&format!(
                "Failed to acquire write lock: {}",
                e
            )))
        })?;

        let user = users.get_mut(user_id).ok_or_else(|| {
            ControlCenterError::from(auth::user_not_found(&format!("User with ID '{}'", user_id)))
        })?;

        user.last_login = Some(Utc::now());
        user.reset_failed_login();

        Ok(())
    }

    /// Delete user
    pub async fn delete_user(&self, user_id: &str) -> Result<()> {
        let mut users = self.users.write().map_err(|e| {
            ControlCenterError::from(infrastructure::internal_error(&format!(
                "Failed to acquire write lock: {}",
                e
            )))
        })?;

        let user = users.remove(user_id).ok_or_else(|| {
            ControlCenterError::from(auth::user_not_found(&format!("User with ID '{}'", user_id)))
        })?;

        let mut username_map = self.username_map.write().map_err(|e| {
            ControlCenterError::from(infrastructure::internal_error(&format!(
                "Failed to acquire write lock: {}",
                e
            )))
        })?;

        let mut email_map = self.email_map.write().map_err(|e| {
            ControlCenterError::from(infrastructure::internal_error(&format!(
                "Failed to acquire write lock: {}",
                e
            )))
        })?;

        username_map.remove(&user.username);
        email_map.remove(&user.email);

        Ok(())
    }

    /// List all users
    pub async fn list_users(&self) -> Result<Vec<User>> {
        let users = self.users.read().map_err(|e| {
            ControlCenterError::from(infrastructure::internal_error(&format!(
                "Failed to acquire read lock: {}",
                e
            )))
        })?;

        Ok(users.values().cloned().collect())
    }

    /// Get user count
    pub async fn count(&self) -> Result<usize> {
        let users = self.users.read().map_err(|e| {
            ControlCenterError::from(infrastructure::internal_error(&format!(
                "Failed to acquire read lock: {}",
                e
            )))
        })?;

        Ok(users.len())
    }
}

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

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

    fn create_test_user() -> User {
        User::new(
            "testuser",
            "test@example.com",
            "Test User",
            "$argon2id$v=19$m=19456,t=2,p=1$...".to_string(),
            vec![UserRole::Developer],
        )
    }

    #[tokio::test]
    async fn test_create_user() {
        let service = UserService::new();
        let user = create_test_user();

        let result = service.create_user(user.clone()).await;
        assert!(result.is_ok());

        let created = result.unwrap();
        assert_eq!(created.username, user.username);
        assert_eq!(created.email, user.email);
    }

    #[tokio::test]
    async fn test_find_user_by_username() {
        let service = UserService::new();
        let user = create_test_user();
        service.create_user(user.clone()).await.unwrap();

        let found = service.find_by_username("testuser").await.unwrap();
        assert!(found.is_some());
        assert_eq!(found.unwrap().username, "testuser");
    }

    #[tokio::test]
    async fn test_duplicate_username() {
        let service = UserService::new();
        let user1 = create_test_user();
        let mut user2 = create_test_user();
        user2.email = "different@example.com".to_string();

        service.create_user(user1).await.unwrap();
        let result = service.create_user(user2).await;

        assert!(result.is_err());
    }

    #[tokio::test]
    async fn test_user_roles() {
        let user = create_test_user();
        assert!(user.has_role(UserRole::Developer));
        assert!(!user.has_role(UserRole::Admin));
        assert!(!user.is_admin());
    }

    #[tokio::test]
    async fn test_failed_login_lockout() {
        let mut user = create_test_user();
        assert_eq!(user.status, UserStatus::Active);

        for _ in 0..5 {
            user.increment_failed_login();
        }

        assert_eq!(user.status, UserStatus::Locked);
        assert_eq!(user.failed_login_attempts, 5);
    }

    #[tokio::test]
    async fn test_update_last_login() {
        let service = UserService::new();
        let user = create_test_user();
        let user_id = user.id.clone();

        service.create_user(user).await.unwrap();
        service.update_last_login(&user_id).await.unwrap();

        let updated = service.find_by_id(&user_id).await.unwrap().unwrap();
        assert!(updated.last_login.is_some());
    }

    #[tokio::test]
    async fn test_list_users() {
        let service = UserService::new();

        let user1 = create_test_user();
        let mut user2 = create_test_user();
        user2.username = "testuser2".to_string();
        user2.email = "test2@example.com".to_string();

        service.create_user(user1).await.unwrap();
        service.create_user(user2).await.unwrap();

        let users = service.list_users().await.unwrap();
        assert_eq!(users.len(), 2);
    }
}
