use actix_web::{error::ErrorInternalServerError, web, Responder};
use argon2::{
    password_hash::{rand_core::OsRng, PasswordHasher, SaltString},
    Argon2,
};
use sea_orm::{ActiveModelTrait, ActiveValue, EntityTrait};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

use crate::{auth::AuthedUser, AppState};

pub struct UserController;

#[derive(Serialize)]
pub struct UserWithoutPassword {
    id: Uuid,
    name: String,
    email: String,
}

#[derive(Deserialize)]
pub struct CreateUserDto {
    name: String,
    email: String,
    password: String,
}

impl From<entity::user::Model> for UserWithoutPassword {
    fn from(value: entity::user::Model) -> Self {
        Self {
            id: value.id,
            name: value.name,
            email: value.email,
        }
    }
}

impl UserController {
    pub async fn list_users(
        state: web::Data<AppState>,
        _executor: AuthedUser,
    ) -> actix_web::Result<impl Responder> {
        let db = &state.db;
        let users = entity::prelude::User::find()
            .all(db)
            .await
            .map_err(ErrorInternalServerError)?;
        Ok(web::Json(
            users
                .into_iter()
                .map(UserWithoutPassword::from)
                .collect::<Vec<_>>(),
        ))
    }

    pub async fn create_user(
        state: web::Data<AppState>,
        user: web::Json<CreateUserDto>,
    ) -> actix_web::Result<impl Responder> {
        let db = &state.db;
        let user = user.into_inner();

        let salt = SaltString::generate(&mut OsRng);
        let argon2 = Argon2::default();

        let password_hash = argon2
            .hash_password(user.password.as_bytes(), &salt)
            .map_err(ErrorInternalServerError)?;

        let user = entity::user::ActiveModel {
            id: ActiveValue::NotSet,
            name: ActiveValue::Set(user.name),
            email: ActiveValue::Set(user.email),
            hash: ActiveValue::Set(password_hash.to_string()),
        };

        let result = user.insert(db).await.map_err(ErrorInternalServerError)?;

        Ok(web::Json(UserWithoutPassword::from(result)))
    }
}
