use std::sync::Arc;

use tracing::info;
use uuid::Uuid;
use validator::Validate;

use crate::error::{http, ControlCenterError, Result};
use crate::models::{CreateUserRequest, UpdateUserRequest, User, UserResponse};
use crate::services::{AuthService, DatabaseService};

/// User service for managing user operations
#[derive(Clone)]
pub struct UserService {
    db: Arc<DatabaseService>,
}

impl UserService {
    /// Create a new user service
    pub fn new(db: Arc<DatabaseService>) -> Self {
        Self { db }
    }

    /// Create a new user
    pub async fn create_user(&self, request: CreateUserRequest) -> Result<UserResponse> {
        // Validate request
        request
            .validate()
            .map_err(|e| ControlCenterError::Http(http::HttpError::Validation(e.to_string())))?;

        // Check if user already exists
        if self.find_by_email(&request.email).await?.is_some() {
            let msg = format!("User with email {} already exists", request.email);
            return Err(ControlCenterError::Http(http::conflict(&msg)));
        }

        if self.find_by_username(&request.username).await?.is_some() {
            let msg = format!("User with username {} already exists", request.username);
            return Err(ControlCenterError::Http(http::conflict(&msg)));
        }

        // Hash password
        let password_hash = AuthService::hash_password(&request.password)?;

        // Create user
        let user = User::new(
            request.email,
            request.username,
            password_hash,
            request.first_name,
            request.last_name,
            request.roles,
        );

        // Save to database
        let created_user: User =
            self.db
                .db
                .create("users")
                .content(user)
                .await?
                .ok_or_else(|| {
                    ControlCenterError::from(crate::error::database::DatabaseError::Database(
                        "Failed to create user".to_string(),
                    ))
                })?;

        info!("Created user: {}", created_user.username);

        Ok(UserResponse::from(created_user))
    }

    /// Get user by ID
    pub async fn get_by_id(&self, user_id: Uuid) -> Result<User> {
        let query = "SELECT * FROM users WHERE user_id = $user_id";
        let mut result = self.db.db.query(query).bind(("user_id", user_id)).await?;

        let users: Vec<User> = result.take(0)?;
        users
            .into_iter()
            .next()
            .ok_or_else(|| ControlCenterError::UserNotFound(user_id.to_string()))
    }

    /// Get user response by ID (without sensitive data)
    pub async fn get_user_response_by_id(&self, user_id: Uuid) -> Result<UserResponse> {
        let user = self.get_by_id(user_id).await?;
        Ok(UserResponse::from(user))
    }

    /// Find user by email
    pub async fn find_by_email(&self, email: &str) -> Result<Option<User>> {
        let query = "SELECT * FROM users WHERE email = $email";
        let mut result = self
            .db
            .db
            .query(query)
            .bind(("email", email.to_string()))
            .await?;

        let users: Vec<User> = result.take(0)?;
        Ok(users.into_iter().next())
    }

    /// Find user by username
    pub async fn find_by_username(&self, username: &str) -> Result<Option<User>> {
        let query = "SELECT * FROM users WHERE username = $username";
        let mut result = self
            .db
            .db
            .query(query)
            .bind(("username", username.to_string()))
            .await?;

        let users: Vec<User> = result.take(0)?;
        Ok(users.into_iter().next())
    }

    /// Find user by email or username
    pub async fn find_by_email_or_username(&self, identifier: &str) -> Result<Option<User>> {
        let query = "SELECT * FROM users WHERE email = $identifier OR username = $identifier";
        let mut result = self
            .db
            .db
            .query(query)
            .bind(("identifier", identifier.to_string()))
            .await?;

        let users: Vec<User> = result.take(0)?;
        Ok(users.into_iter().next())
    }

    /// Update user
    pub async fn update_user(
        &self,
        user_id: Uuid,
        request: UpdateUserRequest,
    ) -> Result<UserResponse> {
        // Validate request
        request
            .validate()
            .map_err(|e| ControlCenterError::Http(http::HttpError::Validation(e.to_string())))?;

        // Get existing user
        let mut user = self.get_by_id(user_id).await?;

        // Check for conflicts if email or username is being changed
        if let Some(ref new_email) = request.email {
            if new_email != &user.email && self.find_by_email(new_email).await?.is_some() {
                let msg = format!("User with email {} already exists", new_email);
                return Err(ControlCenterError::Http(http::conflict(&msg)));
            }
        }

        if let Some(ref new_username) = request.username {
            if new_username != &user.username
                && self.find_by_username(new_username).await?.is_some()
            {
                let msg = format!("User with username {} already exists", new_username);
                return Err(ControlCenterError::Http(http::conflict(&msg)));
            }
        }

        // Hash new password if provided
        let mut update_request = request;
        if let Some(ref password) = update_request.password {
            let password_hash = AuthService::hash_password(password)?;
            update_request.password = Some(password_hash);
        }

        // Update user
        user.update(update_request);

        // Save to database
        let query = "UPDATE users SET
            email = $email,
            username = $username,
            password_hash = $password_hash,
            first_name = $first_name,
            last_name = $last_name,
            is_active = $is_active,
            is_verified = $is_verified,
            roles = $roles,
            updated_at = $updated_at
            WHERE user_id = $user_id";

        // Clone data before using in database operations to avoid borrow issues
        let email = user.email.clone();
        let username = user.username.clone();
        let password_hash = user.password_hash.clone();
        let first_name = user.first_name.clone();
        let last_name = user.last_name.clone();
        let is_active = user.is_active;
        let is_verified = user.is_verified;
        let roles = user.roles.clone();
        let updated_at = user.updated_at;

        self.db
            .db
            .query(query)
            .bind(("email", email))
            .bind(("username", username))
            .bind(("password_hash", password_hash))
            .bind(("first_name", first_name))
            .bind(("last_name", last_name))
            .bind(("is_active", is_active))
            .bind(("is_verified", is_verified))
            .bind(("roles", roles))
            .bind(("updated_at", updated_at))
            .bind(("user_id", user_id))
            .await?;

        info!("Updated user: {}", user.username);

        Ok(UserResponse::from(user))
    }

    /// Delete user
    pub async fn delete_user(&self, user_id: Uuid) -> Result<()> {
        // Check if user exists
        let user = self.get_by_id(user_id).await?;

        // Delete user
        let query = "DELETE users WHERE user_id = $user_id";
        self.db.db.query(query).bind(("user_id", user_id)).await?;

        // Also invalidate all sessions for this user
        let session_query = "UPDATE sessions SET is_active = false WHERE user_id = $user_id";
        self.db
            .db
            .query(session_query)
            .bind(("user_id", user_id))
            .await?;

        info!("Deleted user: {}", user.username);

        Ok(())
    }

    /// List users with pagination
    pub async fn list_users(
        &self,
        limit: Option<usize>,
        offset: Option<usize>,
        active_only: Option<bool>,
    ) -> Result<Vec<UserResponse>> {
        let mut query = "SELECT * FROM users".to_string();
        let mut conditions = Vec::new();

        if let Some(active) = active_only {
            conditions.push(format!("is_active = {}", active));
        }

        if !conditions.is_empty() {
            query.push_str(&format!(" WHERE {}", conditions.join(" AND ")));
        }

        query.push_str(" ORDER BY created_at DESC");

        if let Some(limit) = limit {
            query.push_str(&format!(" LIMIT {}", limit));
        }

        if let Some(offset) = offset {
            query.push_str(&format!(" START {}", offset));
        }

        let mut result = self.db.db.query(&query).await?;
        let users: Vec<User> = result.take(0)?;

        Ok(users.into_iter().map(UserResponse::from).collect())
    }

    /// Update user's last login timestamp
    pub async fn update_last_login(&self, user_id: Uuid) -> Result<()> {
        let query = "UPDATE users SET last_login = time::now(), updated_at = time::now() WHERE \
                     user_id = $user_id";
        self.db.db.query(query).bind(("user_id", user_id)).await?;

        Ok(())
    }

    /// Activate user
    pub async fn activate_user(&self, user_id: Uuid) -> Result<UserResponse> {
        let query =
            "UPDATE users SET is_active = true, updated_at = time::now() WHERE user_id = $user_id";
        self.db.db.query(query).bind(("user_id", user_id)).await?;

        self.get_user_response_by_id(user_id).await
    }

    /// Deactivate user
    pub async fn deactivate_user(&self, user_id: Uuid) -> Result<UserResponse> {
        let query =
            "UPDATE users SET is_active = false, updated_at = time::now() WHERE user_id = $user_id";
        self.db.db.query(query).bind(("user_id", user_id)).await?;

        // Also invalidate all sessions for this user
        let session_query = "UPDATE sessions SET is_active = false WHERE user_id = $user_id";
        self.db
            .db
            .query(session_query)
            .bind(("user_id", user_id))
            .await?;

        self.get_user_response_by_id(user_id).await
    }

    /// Verify user email
    pub async fn verify_user(&self, user_id: Uuid) -> Result<UserResponse> {
        let query = "UPDATE users SET is_verified = true, updated_at = time::now() WHERE user_id \
                     = $user_id";
        self.db.db.query(query).bind(("user_id", user_id)).await?;

        self.get_user_response_by_id(user_id).await
    }

    /// Add role to user
    pub async fn add_role(&self, user_id: Uuid, role: String) -> Result<UserResponse> {
        let mut user = self.get_by_id(user_id).await?;

        if !user.has_role(&role) {
            user.roles.push(role.clone());

            let query = "UPDATE users SET roles = $roles, updated_at = time::now() WHERE user_id \
                         = $user_id";
            let roles = user.roles.clone();
            self.db
                .db
                .query(query)
                .bind(("roles", roles))
                .bind(("user_id", user_id))
                .await?;

            info!("Added role '{}' to user: {}", role, user.username);
        }

        Ok(UserResponse::from(user))
    }

    /// Remove role from user
    pub async fn remove_role(&self, user_id: Uuid, role: String) -> Result<UserResponse> {
        let mut user = self.get_by_id(user_id).await?;

        user.roles.retain(|r| r != &role);

        let query =
            "UPDATE users SET roles = $roles, updated_at = time::now() WHERE user_id = $user_id";
        let roles = user.roles.clone();
        self.db
            .db
            .query(query)
            .bind(("roles", roles))
            .bind(("user_id", user_id))
            .await?;

        info!("Removed role '{}' from user: {}", role, user.username);

        Ok(UserResponse::from(user))
    }

    /// Get user count
    pub async fn get_user_count(&self, active_only: Option<bool>) -> Result<i64> {
        let mut query = "SELECT count() FROM users".to_string();

        if let Some(active) = active_only {
            query.push_str(&format!(" WHERE is_active = {}", active));
        }

        let mut result = self.db.db.query(&query).await?;
        let counts: Vec<i64> = result.take(0)?;

        Ok(counts.into_iter().next().unwrap_or(0))
    }
}

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

    #[test]
    fn test_create_user_request_validation() {
        // Valid request
        let valid_request = CreateUserRequest {
            email: "test@example.com".to_string(),
            username: "testuser".to_string(),
            password: "password123".to_string(),
            first_name: Some("Test".to_string()),
            last_name: Some("User".to_string()),
            roles: None,
        };
        assert!(valid_request.validate().is_ok());

        // Invalid email
        let invalid_email = CreateUserRequest {
            email: "invalid-email".to_string(),
            username: "testuser".to_string(),
            password: "password123".to_string(),
            first_name: None,
            last_name: None,
            roles: None,
        };
        assert!(invalid_email.validate().is_err());

        // Short password
        let short_password = CreateUserRequest {
            email: "test@example.com".to_string(),
            username: "testuser".to_string(),
            password: "short".to_string(),
            first_name: None,
            last_name: None,
            roles: None,
        };
        assert!(short_password.validate().is_err());

        // Short username
        let short_username = CreateUserRequest {
            email: "test@example.com".to_string(),
            username: "ab".to_string(),
            password: "password123".to_string(),
            first_name: None,
            last_name: None,
            roles: None,
        };
        assert!(short_username.validate().is_err());
    }
}
