//! User management API endpoints (admin only)

use axum::{
    extract::{Path, State},
    http::StatusCode,
    Json,
};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

use crate::auth::{hash_password, AdminUser};
use crate::db;
use crate::AppState;

use super::auth::ErrorResponse;

/// User info response
#[derive(Debug, Serialize)]
pub struct UserInfo {
    pub id: String,
    pub username: String,
    pub email: Option<String>,
    pub role: String,
    pub enabled: bool,
    pub created_at: String,
    pub last_login: Option<String>,
    pub permissions: Vec<String>,
}

/// Create user request
#[derive(Debug, Deserialize)]
pub struct CreateUserRequest {
    pub username: String,
    pub password: String,
    pub email: Option<String>,
    pub role: String,
    pub permissions: Option<Vec<String>>,
}

/// Update user request
#[derive(Debug, Deserialize)]
pub struct UpdateUserRequest {
    pub email: Option<String>,
    pub role: String,
    pub enabled: bool,
    pub password: Option<String>,
}

/// Set permissions request
#[derive(Debug, Deserialize)]
pub struct SetPermissionsRequest {
    pub permissions: Vec<String>,
}

/// Set client access request
#[derive(Debug, Deserialize)]
pub struct SetClientAccessRequest {
    pub client_ids: Vec<String>,
}

/// GET /api/users - List all users
pub async fn list_users(
    State(state): State<AppState>,
    _admin: AdminUser,
) -> Result<Json<Vec<UserInfo>>, (StatusCode, Json<ErrorResponse>)> {
    let db = state.db.as_ref().ok_or_else(|| {
        (
            StatusCode::SERVICE_UNAVAILABLE,
            Json(ErrorResponse {
                error: "Database not available".to_string(),
            }),
        )
    })?;

    let users = db::get_all_users(db.pool()).await.map_err(|e| {
        tracing::error!("Database error: {}", e);
        (
            StatusCode::INTERNAL_SERVER_ERROR,
            Json(ErrorResponse {
                error: "Failed to fetch users".to_string(),
            }),
        )
    })?;

    let mut result = Vec::new();
    for user in users {
        let permissions = db::get_user_permissions(db.pool(), user.id)
            .await
            .unwrap_or_default();

        result.push(UserInfo {
            id: user.id.to_string(),
            username: user.username,
            email: user.email,
            role: user.role,
            enabled: user.enabled,
            created_at: user.created_at.to_rfc3339(),
            last_login: user.last_login.map(|t| t.to_rfc3339()),
            permissions,
        });
    }

    Ok(Json(result))
}

/// POST /api/users - Create new user
pub async fn create_user(
    State(state): State<AppState>,
    _admin: AdminUser,
    Json(request): Json<CreateUserRequest>,
) -> Result<Json<UserInfo>, (StatusCode, Json<ErrorResponse>)> {
    let db = state.db.as_ref().ok_or_else(|| {
        (
            StatusCode::SERVICE_UNAVAILABLE,
            Json(ErrorResponse {
                error: "Database not available".to_string(),
            }),
        )
    })?;

    // Validate role
    let valid_roles = ["admin", "operator", "viewer"];
    if !valid_roles.contains(&request.role.as_str()) {
        return Err((
            StatusCode::BAD_REQUEST,
            Json(ErrorResponse {
                error: format!("Invalid role. Must be one of: {:?}", valid_roles),
            }),
        ));
    }

    // Validate password
    if request.password.len() < 8 {
        return Err((
            StatusCode::BAD_REQUEST,
            Json(ErrorResponse {
                error: "Password must be at least 8 characters".to_string(),
            }),
        ));
    }

    // Check if username exists
    if db::get_user_by_username(db.pool(), &request.username)
        .await
        .map_err(|e| {
            tracing::error!("Database error: {}", e);
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(ErrorResponse {
                    error: "Database error".to_string(),
                }),
            )
        })?
        .is_some()
    {
        return Err((
            StatusCode::CONFLICT,
            Json(ErrorResponse {
                error: "Username already exists".to_string(),
            }),
        ));
    }

    // Hash password
    let password_hash = hash_password(&request.password).map_err(|e| {
        tracing::error!("Password hashing error: {}", e);
        (
            StatusCode::INTERNAL_SERVER_ERROR,
            Json(ErrorResponse {
                error: "Failed to hash password".to_string(),
            }),
        )
    })?;

    // Create user
    let user = db::create_user(
        db.pool(),
        &request.username,
        &password_hash,
        request.email.as_deref(),
        &request.role,
    )
    .await
    .map_err(|e| {
        tracing::error!("Failed to create user: {}", e);
        (
            StatusCode::INTERNAL_SERVER_ERROR,
            Json(ErrorResponse {
                error: "Failed to create user".to_string(),
            }),
        )
    })?;

    // Set initial permissions if provided
    let permissions = if let Some(perms) = request.permissions {
        db::set_user_permissions(db.pool(), user.id, &perms)
            .await
            .map_err(|e| {
                tracing::error!("Failed to set permissions: {}", e);
                (
                    StatusCode::INTERNAL_SERVER_ERROR,
                    Json(ErrorResponse {
                        error: "Failed to set permissions".to_string(),
                    }),
                )
            })?;
        perms
    } else {
        // Default permissions based on role
        let default_perms = match request.role.as_str() {
            "admin" => vec![
                "view",
                "control",
                "transfer",
                "manage_users",
                "manage_clients",
            ],
            "operator" => vec!["view", "control", "transfer"],
            "viewer" => vec!["view"],
            _ => vec!["view"],
        };
        let perms: Vec<String> = default_perms.into_iter().map(String::from).collect();
        db::set_user_permissions(db.pool(), user.id, &perms)
            .await
            .ok();
        perms
    };

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

    Ok(Json(UserInfo {
        id: user.id.to_string(),
        username: user.username,
        email: user.email,
        role: user.role,
        enabled: user.enabled,
        created_at: user.created_at.to_rfc3339(),
        last_login: None,
        permissions,
    }))
}

/// GET /api/users/:id - Get user details
pub async fn get_user(
    State(state): State<AppState>,
    _admin: AdminUser,
    Path(id): Path<String>,
) -> Result<Json<UserInfo>, (StatusCode, Json<ErrorResponse>)> {
    let db = state.db.as_ref().ok_or_else(|| {
        (
            StatusCode::SERVICE_UNAVAILABLE,
            Json(ErrorResponse {
                error: "Database not available".to_string(),
            }),
        )
    })?;

    let user_id = Uuid::parse_str(&id).map_err(|_| {
        (
            StatusCode::BAD_REQUEST,
            Json(ErrorResponse {
                error: "Invalid user ID".to_string(),
            }),
        )
    })?;

    let user = db::get_user_by_id(db.pool(), user_id)
        .await
        .map_err(|e| {
            tracing::error!("Database error: {}", e);
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(ErrorResponse {
                    error: "Database error".to_string(),
                }),
            )
        })?
        .ok_or_else(|| {
            (
                StatusCode::NOT_FOUND,
                Json(ErrorResponse {
                    error: "User not found".to_string(),
                }),
            )
        })?;

    let permissions = db::get_user_permissions(db.pool(), user.id)
        .await
        .unwrap_or_default();

    Ok(Json(UserInfo {
        id: user.id.to_string(),
        username: user.username,
        email: user.email,
        role: user.role,
        enabled: user.enabled,
        created_at: user.created_at.to_rfc3339(),
        last_login: user.last_login.map(|t| t.to_rfc3339()),
        permissions,
    }))
}

/// PUT /api/users/:id - Update user
pub async fn update_user(
    State(state): State<AppState>,
    admin: AdminUser,
    Path(id): Path<String>,
    Json(request): Json<UpdateUserRequest>,
) -> Result<Json<UserInfo>, (StatusCode, Json<ErrorResponse>)> {
    let db = state.db.as_ref().ok_or_else(|| {
        (
            StatusCode::SERVICE_UNAVAILABLE,
            Json(ErrorResponse {
                error: "Database not available".to_string(),
            }),
        )
    })?;

    let user_id = Uuid::parse_str(&id).map_err(|_| {
        (
            StatusCode::BAD_REQUEST,
            Json(ErrorResponse {
                error: "Invalid user ID".to_string(),
            }),
        )
    })?;

    // Prevent admin from disabling themselves
    if user_id.to_string() == admin.0.user_id && !request.enabled {
        return Err((
            StatusCode::BAD_REQUEST,
            Json(ErrorResponse {
                error: "Cannot disable your own account".to_string(),
            }),
        ));
    }

    // Validate role
    let valid_roles = ["admin", "operator", "viewer"];
    if !valid_roles.contains(&request.role.as_str()) {
        return Err((
            StatusCode::BAD_REQUEST,
            Json(ErrorResponse {
                error: format!("Invalid role. Must be one of: {:?}", valid_roles),
            }),
        ));
    }

    // Update user
    let user = db::update_user(
        db.pool(),
        user_id,
        request.email.as_deref(),
        &request.role,
        request.enabled,
    )
    .await
    .map_err(|e| {
        tracing::error!("Database error: {}", e);
        (
            StatusCode::INTERNAL_SERVER_ERROR,
            Json(ErrorResponse {
                error: "Failed to update user".to_string(),
            }),
        )
    })?
    .ok_or_else(|| {
        (
            StatusCode::NOT_FOUND,
            Json(ErrorResponse {
                error: "User not found".to_string(),
            }),
        )
    })?;

    // Update password if provided
    if let Some(password) = request.password {
        if password.len() < 8 {
            return Err((
                StatusCode::BAD_REQUEST,
                Json(ErrorResponse {
                    error: "Password must be at least 8 characters".to_string(),
                }),
            ));
        }

        let password_hash = hash_password(&password).map_err(|e| {
            tracing::error!("Password hashing error: {}", e);
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(ErrorResponse {
                    error: "Failed to hash password".to_string(),
                }),
            )
        })?;

        db::update_user_password(db.pool(), user_id, &password_hash)
            .await
            .map_err(|e| {
                tracing::error!("Database error: {}", e);
                (
                    StatusCode::INTERNAL_SERVER_ERROR,
                    Json(ErrorResponse {
                        error: "Failed to update password".to_string(),
                    }),
                )
            })?;
    }

    let permissions = db::get_user_permissions(db.pool(), user.id)
        .await
        .unwrap_or_default();

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

    Ok(Json(UserInfo {
        id: user.id.to_string(),
        username: user.username,
        email: user.email,
        role: user.role,
        enabled: user.enabled,
        created_at: user.created_at.to_rfc3339(),
        last_login: user.last_login.map(|t| t.to_rfc3339()),
        permissions,
    }))
}

/// DELETE /api/users/:id - Delete user
pub async fn delete_user(
    State(state): State<AppState>,
    admin: AdminUser,
    Path(id): Path<String>,
) -> Result<StatusCode, (StatusCode, Json<ErrorResponse>)> {
    let db = state.db.as_ref().ok_or_else(|| {
        (
            StatusCode::SERVICE_UNAVAILABLE,
            Json(ErrorResponse {
                error: "Database not available".to_string(),
            }),
        )
    })?;

    let user_id = Uuid::parse_str(&id).map_err(|_| {
        (
            StatusCode::BAD_REQUEST,
            Json(ErrorResponse {
                error: "Invalid user ID".to_string(),
            }),
        )
    })?;

    // Prevent admin from deleting themselves
    if user_id.to_string() == admin.0.user_id {
        return Err((
            StatusCode::BAD_REQUEST,
            Json(ErrorResponse {
                error: "Cannot delete your own account".to_string(),
            }),
        ));
    }

    let deleted = db::delete_user(db.pool(), user_id).await.map_err(|e| {
        tracing::error!("Database error: {}", e);
        (
            StatusCode::INTERNAL_SERVER_ERROR,
            Json(ErrorResponse {
                error: "Failed to delete user".to_string(),
            }),
        )
    })?;

    if deleted {
        tracing::info!("Deleted user: {}", id);
        Ok(StatusCode::NO_CONTENT)
    } else {
        Err((
            StatusCode::NOT_FOUND,
            Json(ErrorResponse {
                error: "User not found".to_string(),
            }),
        ))
    }
}

/// PUT /api/users/:id/permissions - Set user permissions
pub async fn set_permissions(
    State(state): State<AppState>,
    _admin: AdminUser,
    Path(id): Path<String>,
    Json(request): Json<SetPermissionsRequest>,
) -> Result<StatusCode, (StatusCode, Json<ErrorResponse>)> {
    let db = state.db.as_ref().ok_or_else(|| {
        (
            StatusCode::SERVICE_UNAVAILABLE,
            Json(ErrorResponse {
                error: "Database not available".to_string(),
            }),
        )
    })?;

    let user_id = Uuid::parse_str(&id).map_err(|_| {
        (
            StatusCode::BAD_REQUEST,
            Json(ErrorResponse {
                error: "Invalid user ID".to_string(),
            }),
        )
    })?;

    // Validate permissions
    let valid_permissions = [
        "view",
        "control",
        "transfer",
        "manage_users",
        "manage_clients",
    ];
    for perm in &request.permissions {
        if !valid_permissions.contains(&perm.as_str()) {
            return Err((
                StatusCode::BAD_REQUEST,
                Json(ErrorResponse {
                    error: format!(
                        "Invalid permission: {}. Valid: {:?}",
                        perm, valid_permissions
                    ),
                }),
            ));
        }
    }

    db::set_user_permissions(db.pool(), user_id, &request.permissions)
        .await
        .map_err(|e| {
            tracing::error!("Database error: {}", e);
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(ErrorResponse {
                    error: "Failed to set permissions".to_string(),
                }),
            )
        })?;

    tracing::info!("Updated permissions for user: {}", id);
    Ok(StatusCode::OK)
}

/// PUT /api/users/:id/clients - Set user client access
pub async fn set_client_access(
    State(state): State<AppState>,
    _admin: AdminUser,
    Path(id): Path<String>,
    Json(request): Json<SetClientAccessRequest>,
) -> Result<StatusCode, (StatusCode, Json<ErrorResponse>)> {
    let db = state.db.as_ref().ok_or_else(|| {
        (
            StatusCode::SERVICE_UNAVAILABLE,
            Json(ErrorResponse {
                error: "Database not available".to_string(),
            }),
        )
    })?;

    let user_id = Uuid::parse_str(&id).map_err(|_| {
        (
            StatusCode::BAD_REQUEST,
            Json(ErrorResponse {
                error: "Invalid user ID".to_string(),
            }),
        )
    })?;

    // Parse client IDs
    let client_ids: Result<Vec<Uuid>, _> = request
        .client_ids
        .iter()
        .map(|s| Uuid::parse_str(s))
        .collect();

    let client_ids = client_ids.map_err(|_| {
        (
            StatusCode::BAD_REQUEST,
            Json(ErrorResponse {
                error: "Invalid client ID format".to_string(),
            }),
        )
    })?;

    db::set_user_client_access(db.pool(), user_id, &client_ids)
        .await
        .map_err(|e| {
            tracing::error!("Database error: {}", e);
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(ErrorResponse {
                    error: "Failed to set client access".to_string(),
                }),
            )
        })?;

    tracing::info!("Updated client access for user: {}", id);
    Ok(StatusCode::OK)
}
