use crate::api::error::ApiError;
use crate::api::middleware::AuthedUser;
use crate::api::pagination::{Page, PageQuery};
use crate::api::state::AppState;
use crate::database::UserRow;
use axum::extract::{Extension, Query};
use axum::Json;
use hbb_common::ResultType;
use serde::Serialize;
use serde_json::{json, Value};
use std::sync::Arc;

#[derive(Debug, Serialize)]
pub struct UserPayload {
    pub name: String,
    pub display_name: String,
    pub avatar: String,
    pub email: String,
    pub note: String,
    pub status: i64,
    pub is_admin: bool,
    /// The desktop client's OIDC poll loop deserializes the AuthBody using
    /// the Rust struct in src/hbbs_http/account.rs, where `info` is a
    /// REQUIRED field (no #[serde(default)]). Missing it makes serde fail,
    /// the poll loop's `Ok(_)` arm fires, and the client polls forever
    /// even though the OIDC session was successful. Emit an empty object
    /// — the client's own UserInfo defaults handle the rest.
    pub info: Value,
}

impl From<&UserRow> for UserPayload {
    fn from(u: &UserRow) -> Self {
        Self {
            name: u.username.clone(),
            display_name: u.display_name.clone(),
            avatar: u.avatar.clone(),
            email: u.email.clone(),
            note: u.note.clone(),
            status: u.status,
            is_admin: u.is_admin,
            info: json!({}),
        }
    }
}

pub async fn hash_password(plain: String) -> ResultType<String> {
    Ok(
        hbb_common::tokio::task::spawn_blocking(move || bcrypt::hash(plain, 10))
            .await??,
    )
}

pub async fn verify_password(hash: String, plain: String) -> ResultType<bool> {
    Ok(
        hbb_common::tokio::task::spawn_blocking(move || bcrypt::verify(plain, &hash))
            .await??,
    )
}

/// `GET /api/users` — paginated list of users visible to the caller. Admin
/// sees all enabled users; non-admin sees themselves plus members of any
/// device-group they share. Flutter decoder at common/hbbs/hbbs.dart:26.
pub async fn list(
    Extension(state): Extension<Arc<AppState>>,
    user: AuthedUser,
    Query(q): Query<PageQuery>,
) -> Result<Json<Page<UserPayload>>, ApiError> {
    let (total, rows) = state
        .db
        .users_list_accessible(user.user_id, user.is_admin, q.offset(), q.limit())
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?;
    Ok(Json(Page {
        total,
        data: rows.iter().map(UserPayload::from).collect(),
    }))
}
